其中整个过程大致可以分成三个部分:
父组件通过绑定属性传值给子组件的props
知识点:with()的使用
子组件读取props
知识点:vue的双向绑定
父组件 data 更新时,子组件的props也进行更新
知识点:watcher原理示例代码:
<div class="a" >
<testb :child-name="parentName" ></testb>
父组件传人的值: {{parentName}}
</div>
new Vue({
el:".a",
name:"A",
components:{
testb:{
props:{
childName:""
},
template: '子组件接收的值: {{childName}}
',
}
},
data(){
return {
parentName:"我是父组件"
}
},
})
根据上面的场景设置,testb 是一个子组件,接收一个 props(child-name),然后 根组件 A 把 自身的 parentName 绑定到 子组件的属性 child-name 上
tip:关于大小写
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名 ,否则会报错:
(如果不使用驼峰法命名就无所谓了)
父组件在传值前会产生这样页面渲染函数:
(function() {
with(this){
return _c('div',{staticClass:"a"},[
_c('testb',{attrs:{"child-name":parentName}})
],1)
}
})
特点1:它是个立即执行函数
平时不建议使用,因为会导致局部数据泄露全局!
特点3:_c 是渲染组件的函数,_c(‘testb’) 表示渲染 testb 这个子组件
之后这个函数会被执行,执行时会绑定父组件为作用域 ,渲染函数内部所有的变量,都会从父组件对象 上去获取,绑定了父作用域之后, parentName 自然会从父组件获取,类似这样
{ attrs: { child-name: parentVm.parentName } }
函数执行了,内部的 _c(‘testb’) 第一个执行,然后传入了 赋值后的 attrs
父组件赋值之后的 attrs 就是下面这样:
{ attrs: { child-name: "我是父组件" } }
此时,父组件就正式利用 props 把 parentName 传给了 子组件的props 的child-name
而子组件拿到父组件赋值过后的 attr 而 attrs 包含普通属性和 props,所以需要 筛选出 props,然后保存起来
如果在这里打印一下,我们可以发现:
props 会被保存到实例的_props 中,并且 会逐一复制到实例上,并且每一个属性会被设置为响应式的
其中props的访问和更改源码大概是这个样子:
Object.defineProperty(childVm, childName, {
get() {
return this._props.childName
},
set(val) {
this._props.childName = val
}
});
Set 时和Get时访问的this.childName,会默认找到this._props.childName
watcher就是vue的专用监听器
watcher的作用:
1.用于实例自己更新视图
2.用于给 依赖的属性保存,然后属性变化的时候,通知实例更新
props更新流程:
1.parentName 会收集 父组件的 watcher
2.父组件重新渲染,重新赋值 props
关于在子组件里修改props时:
父组件传递的参数 :数组和对象,子组件接受之后可以直接进行修改,并且会传递给父组件相应的值也会修改(不报错)
如果传递的值是字符串和数字(非引用类型),直接修改会报错
不推荐子组件直接修改父组件中的参数,不是因为会影响父组件(毕竟是单向数据流),而是避免这个参数被多个子组件引用,无法找到造成数据不正常的原因
当参数为引用类型时:
<div class="a" >
<testb :child-name="parentName" ></testb>
父组件:<input type="text" v-model='parentName.name'>
</div>
此时修改子组件会影响父组件!最好方法就是在data里再建一个值拷贝一下再处理
js数据储存方式:
堆空间 | 栈空间 |
---|---|
Object | Number/String/Object在堆中的地址… |
父组件会给子组件传递对象在栈空间的地址数据,子组件如果试图修改props里的对象,修改的就是这个地址上在堆空间的实际数据,并且即使修改了地址也不会发生变化,所以vue检测不到props发生了修改,也就不会报错了