Skip to content

一、vue-单向数据流

1. vue 官网给出的答案 链接->

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。 这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。 这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

2. 代码演示

  • 具体代码

    vue
        <template>
            <div>
                <ChildData :userInfo="userInfo" :msg="msg"/>
            </div>
        </template>
        
        <script>
          import ChildData from '@/components/ChildData.vue';
        
          export default {
            name: 'ParentData',
            components: {
              ChildData,
            },
            data() {
              return {
                userInfo: {
                  name: '张三',
                },
                msg: 'vue',
              };
            },
          };
        </script>
    vue
    <template>
        <div>
            <h1>{{ userInfo.name }}</h1>
            <h1>{{ msg }}</h1>
        </div>
    </template>
    
    <script>
      export default {
        name: 'ChildData',
        props: {
          userInfo: Object,
          msg: String,
        },
        mounted() {
          this.userInfo.name = '李四';
          this.msg = 'hello';
        },
      };
    </script>
  • 不关闭 eslint,控制台编译报错

    text
       16:5  error  Unexpected mutation of "userInfo" prop  vue/no-mutating-props
       17:5  error  Unexpected mutation of "msg" prop       vue/no-mutating-props
  • 关闭 eslint,在 .eslintrc.js 文件中添加如下配置,重启服务

    javascript
    module.exports = {
        rules: {
          'vue/no-mutating-props': 0,
        },
    };
  • 编译不报错,但是浏览器控制台,出现了警告
    vue-data 从这我们能明白两点:

    • 值确实能够被改变
    • 针对基本类型的值,有警告提示
    • 针对引用类型的值,没有提示
  • 同时我们查看 vue 的源码,确实有这段校验警告 vue-props

3. 官网推荐写法

这里有两种常见的试图变更一个 prop 的情形:

  1. 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 作为其初始值:
vue
    props: {
      userInfo: Object,
      msg: String,
    },
    data() {
      return {
        currentUserInfo: this.userInfo,
      }
    },
  1. 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
vue
    computed: {
      currentMsg() {
        return `消息提示:${this.msg.trim()}`
      }
    }
  • 页面获取
    vue
    <template>
        <div>
            <h1>{{ currentUserInfo.name }}</h1>
            <h1>{{ currentMsg }}</h1>
        </div>
    </template>
    
    <script>
      export default {
        name: 'ChildData',
        props: {
          userInfo: Object,
          msg: String,
        },
        data() {
          return {
            currentUserInfo: this.userInfo,
          }
        },
        computed: {
          currentMsg() {
            return `消息提示:${this.msg.trim()}`
          }
        }
      };
    </script>

4. 产生的疑问

为什么这边 data 函数里面访问了 this 中的变量,只是这个变量是 props 属性, 但是有时候访问 data 中定义的 变量却是 undefined

  • 如下这段示例:

    vue
    <template>
        <div>
            <h1>{{ currentMsg }}</h1>
            <h1>{{ newMsg }}</h1>
        </div>
    </template>
    
    <script>
      export default {
        name: 'ChildData',
        props: {
          userInfo: Object,
          msg: String,
        },
        data() {
          return {
            currentMsg: this.msg,
            newMsg: this.currentMsg,
          };
        },
        mounted() {
          this.currentMsg = 'hello';
          console.log('newMsg--->', this.newMsg);
        },
      };
    </script>

    vue-props-2.png

  • 查看源码才发现

    js
        export function initState (vm: Component) {
        vm._watchers = []
        const opts = vm.$options
        if (opts.props) initProps(vm, opts.props) // 先初始化 props
        if (opts.methods) initMethods(vm, opts.methods)
        if (opts.data) {  // 再初始化 data
          initData(vm)
        } else {
          observe(vm._data = {}, true /* asRootData */)
        }
        if (opts.computed) initComputed(vm, opts.computed)
        if (opts.watch && opts.watch !== nativeWatch) {
          initWatch(vm, opts.watch)
        }
      }

    通过这段源码,我们可以清晰的看到,vue 在初始化 data 前已经初始化完 props。

5. 如果就想改父组件传过来的属性,怎么办?

  • 在父组件中定义更改改属性的方法
  • 子组件通过 $emit 触发父组件传入的方法回调,进行数值的变更。

Released under the MIT License.