一、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,控制台编译报错
text16: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 文件中添加如下配置,重启服务
javascriptmodule.exports = { rules: { 'vue/no-mutating-props': 0, }, };
编译不报错,但是浏览器控制台,出现了警告
从这我们能明白两点:- 值确实能够被改变
- 针对基本类型的值,有警告提示
- 针对引用类型的值,没有提示
同时我们查看 vue 的源码,确实有这段校验警告
3. 官网推荐写法
这里有两种常见的试图变更一个 prop 的情形:
- 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 作为其初始值:
vueprops: { userInfo: Object, msg: String, }, data() { return { currentUserInfo: this.userInfo, } },
- 这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
vuecomputed: { 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>
查看源码才发现
jsexport 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 触发父组件传入的方法回调,进行数值的变更。