写文章不容易,点个赞呗兄弟 专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧 研究基于 Vue版本 【2.5.17】javascript
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧html
今天咱们用白话文解读props 的工做原理缓存
props 真的挺有用的,做为父传子的载体,你们确定都用过,可是你有没有想过,Props 究竟是怎么工做的?函数
但愿先看下 响应式原理,对 props 理解颇有帮助学习
响应式原理测试
<br>this
开篇以前,我提出三个问题3d
一、父组件 怎么传值给 子组件的 propscode
二、子组件如何读取props
三、父组件 data 更新,子组件的props 如何更新
今天咱们带着三个问题去开始咱们的讲解
明白这三个问题,那么相信你也就理解了 props 的工做原理
<br> <br>
如今咱们有一个这样的 根组件 A 和 他的 子组件 testb
根组件A 会 把 parentName 传给 子组件 testb 的 props
根组件A 也是 组件testb 的 父组件
<div class="a" > <testb :child-name="parentName" ></testb> </div>
new Vue({ el:".a", name:"A", components:{ testb:{ props:{ childName:"" }, template: '<p>父组件传入的 props 的值 {{childName}}</p>', } }, data(){ return { parentName:"我是父组件" } }, })
按照上面的例子,开始咱们的问题解析
父组件怎么传值给子组件的 props
这部份内容会比较多,可是这部份内容是 props 的重中之重,必须理解好吧
根据上面的场景设置,testb 是一个子组件,接收一个 props(child-name)
而后 根组件 A 把 自身的 parentName 绑定到 子组件的属性 child-name 上
父组件的模板 会被解析成一个 模板渲染函数
(function() { with(this){ return _c('div',{staticClass:"a"},[ _c('testb',{attrs:{"child-name":parentName}}) ],1) } })
这段代码须要解释下
虽然想不涉及源码,可是这段代码对咱们理解颇有帮助,并且不难,因此决定放上来
一、_c 是渲染组件的函数,_c('testb') 表示渲染 testb 这个子组件
二、由于 with 的做用是,绑定大括号内代码的 变量访问做用域
三、这是一个匿名自执行函数,会在后面执行
简化上面的函数,作个例子测试一下
function test(){ with(this){ console.log(parentName) } } test.call({parentName:"测试名字"})
你能看到,我给 test 绑定了一个对象做用域,加上 with 绑定 this,而后读取 parentName 就会从 传入的对象上获取
了解了这个,咱们来看下一步
以后,模板函数会被执行,执行时会绑定 父组件为做用域
因此渲染函数内部全部的变量,都会从父组件对象 上去获取
绑定了父做用域以后, parentName 天然会从父组件获取,相似这样
{ attrs: { child-name: parentVm.parentName } }
函数执行了,内部的 _c('testb') 第一个执行,而后传入了 赋值后的 attrs
父组件赋值以后的 attrs 就是下面这样
{ attrs: { child-name: "我是父组件" } }
此时,父组件就正式 利用 props 把 parentName 传给了 子组件的props child-name
_c('testb',{attrs:{"child-name":parentName}})
而 attrs 包含 普通属性 和 props,因此须要 筛选出 props,而后保存起来
props 会被 保存到 实例的_props 中,而且 会逐一复制到 实例上,而且每个属性会被设置为响应式的
你看到的,每个 实例都会有 一个 _props 的同时,也会把属性直接放在 实例上。
我是不会骗你的好吧
<br> <br>
经过上面的问题,咱们知道了 子组件保存了 父组件 传入 的数据
prop 的数据会被 逐一复制到 vm对象上(子组件的实例 this) 上
可是复制的时候,会对每一个属性,同时设置 get 和 set 函数,进行 访问转接 和 赋值转接
下面是我简化了源码的一段代码,了解一下
Object.defineProperty(vm, key, { get() { return this._props[key] }, set(val) { this._props[key] = val } });
我以 props 其中一个 属性 childName 为例好吧
Object.defineProperty(childVm, childName, { get() { return this._props.childName }, set(val) { this._props.childName = val } });
你访问 props 其中一个值 vm.childName,其实访问的是 vm._props.childName
你赋值 vm.childName= 5 ,实际上是赋值 vm._props.childName= 5
可是你直接在这里给 props 赋值,你是不会影响到 父组件的data 的好吧,两个东西彻底没有关系
就像你老爸给钱你用,你怎么用,对老爸都没有影响
总结
移花接木,狸猫换太子,就是 props 本人了,挂羊头卖狗肉,很明显违法行为,欺骗消费者
<br> <br>
看过我上一篇文章的都知道
每个实例都会存在 一个 专属watcher
一、用于实例本身更新视图
二、用于给 依赖的属性保存,而后属性变化的时候,通知实例更新
我已经在 上一篇讲解过 响应式原理,若是这里你不明白,能够查看一下
【Vue原理】响应式原理 - 白话版
以 parentName 为例,讲解更新,parentName 是 父组件的 data,而后传给子组件的props
在 父组件渲染函数中
(function() { with(this){ return _c('div',{staticClass:"a"},[ _c('testb',{attrs:{"child-name":parentName}}) ],1) } })
由于 Vue 会对组件的渲染函数进行缓存,因此更新的时候,不须要从新解析,直接读取缓存,会加快渲染速度
而后渲染函数执行,开启新一轮的 props 赋值操做,回到了第一个问题,若是不记得,能够回去看下
<br> <br>
一、父组件 data 的值 和 子组件的 props 通常是没有任何联系的,更改 props 不影响父组件 data,可是若是传入的是 对象,那么修改对象,是会影响父组件的,由于数据是原样传入的,因此修改对象,两个地方都会影响
二、props 也是响应式的,跟 data 本质 差很少
三、props 会访问转接,赋值转接 ,其实操做的是 vm._props 的属性
<br> <br>