写文章不容易,点个赞呗兄弟
专一 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工做原理,源码版助于了解内部详情,让咱们一块儿学习吧
研究基于 Vue版本 【2.5.17】
若是你以为排版难看,请点击 下面连接 或者 拉到 下面关注公众号也能够吧函数
今天记录 Props 源码流程,哎,这东西,就算是研究过了,也真是会随着时间慢慢忘记的。this
幸亏我作了详细的文章,忘记了什么的,回忆起来必然是很快的。spa
好的,回到正题,Props代理
请你在读这篇以前,先去看看个人白话版code
在上面这篇文章中,也已经清楚地解决了一个问题递归
父组件 如何 把数据 当作 props 传给子组件token
因此今天,咱们只需记录 Props 的处理流程源码便可开发
在建立Vue实例的过程当中,会调用 initState 处理options,好比 props,computed,watch 等
只要你 new Vue 建立实例以后,很快就会处理options
function Vue(){ ... 其余处理 initState(this) ...解析模板,生成DOM 插入页面 } function initState(vm) { var opts = vm.$options; if (opts.props) { initProps(vm, opts.props); } ... 处理 computed,watch,methods 等其余options }
你看处处理 Props ,主要用到了一个方法 initProps,他就是本场的焦点了,让咱们来采访下源码本码
function initProps(vm, propsOpt) { // 这是父组件给子组件传入的 props 的具体值 var propsData = vm.$options.propsData || {}; var props = vm._props = {}; for (var key in propsOpt){ // 给 props 的 key 设置 响应式 defineReactive(props, key, propsData[key]); if (! (key in vm)) { // 转接访问,访问 vm 属性,转到访问 vm._props 属性 proxy(vm, "_props", key); } } }
上面的代码主要作了三件事
一、遍历 props
二、给 props 设置响应式
三、给 props 设置代理
咱们主要讲两件事
defineReactive(props, key, propsData[key])
defineReactive 在这里就不给太多源码了,你只须要记住他就是给 props 设置响应式的
function defineReactive(obj, key) { Object.defineProperty(obj, key, { get() { ...依赖收集 }, set(newVal) { ....依赖更新 } }); }
若是你想了解响应式,就能够看我这篇文章
Props 设置响应式,也是旨在数据改变时动态更新。
怎么设置响应式吗?看这里
数据是直接从 父组件上传过来的,没有进行拷贝等处理,原样传过来
怎么传的?也能够看
若是props 是基本类型
在 子组件实例上设置这个 props 属性为响应式,跟 data 本质同样,做用是监听 props 修改
若是 props 是对象
也会在 子组件实例上 设置这个 props 属性为响应式,做用也是监听 props 修改
可是!
【不会递归对象】给对象内全部属性设置响应式,由于该对象【已经在父组件中】完成响应式设置了
也就是说
若是你在 子组件中直接修改 props 对象内的数据,父组件也会跟着修改
在记录的途中,我发现了一个问题,发现没有想象中的那么简单,因此如今郑重记录
当 父组件数据 改变,子组件怎么更新?
分类型的,说得比较详细,可能有点绕?
一、 若是是基本类型,是这个流程
父组件数据改变,只会把新的数据传给子组件
子组件拿到新数据,就会直接替换到原来的 props
替换就是直接等哈,看下源码,重要语句标红
updateChildComponent 是子组件内部更新时会调用到的一个函数,这是其中更新 props 的一个片断
function updateChildComponent( vm, propsData ) { if (propsData && vm.$options.props) { // 保存 props 的地方,用于访问转接,具体看文章下面 var props = vm._props; // 全部子组件上设置的 props 的 key var propKeys = vm.$options._propKeys || []; for (var i = 0; i < propKeys.length; i++) { var key = propKeys[i]; props[key] = propsData[key] } vm.$options.propsData = propsData; } }
而 props 在子组件中也是响应式的,【直接 等号 替换】致使触发 set,set 再通知 子组件完成更新
数据是 基本类型,而后设置定时器修改数据
watcher1 是父组件,watcher2 是子组件
父组件内的 data num 通知 watcher1 更新
子组件内的 props child_num 通知 watcher2 更新
二、若是是对象,是这个流程
条件
父组件传 对象 给 子组件,而且父子组件 页面都使用到了这个数据
结果
那么这个对象,会收集到 父子组件的 watcher
因此
当 对象内部被修改的时候,会通知到 父和子 更新。
例子
父组件设置 obj 对象,并传给子组件
定时修改父组件数据 obj.name ,能够看到是 obj.name 通知 父子更新
固然,若是对象被整个替换了,而不是修改内部,那么跟 基本类型同样
区别是什么?
一、基本类型是,子组件内部 props 通知 子组件更新的
二、引用类型是,父组件的数据 data 通知 子组件更新的
在白话版中,我已经说得很清楚了, Props 有个移花接木的暗箱操做,就是访问转移
Data 也是这么作的
[
【Vue原理】代理 Data - 源码版 ]( https://mp.weixin.qq.com/s?__... )
你在项目中,会使用 http://this.xxx去访问 props,props 已经当成了 实例的属性,因此能够直接访问
可是其实你访问的是 【this._props.xxx】
为何 Vue 要这么弄,目的就是为了方便开发啊,让咱们直接简短了相关代码
而 React,访问 props,还要 this.props.xxxx,写这么长,不嫌麻烦吗?
那么,是怎么设置代理的呢,就是下面这行
proxy(vm, "_props", key);
proxy 是什么也不要急,瓜就在下面,拿凳坐好
function proxy( target, sourceKey, key ) { Object.defineProperty(target, key, { get() { return this[sourceKey][key] }, set(val) { this[sourceKey][key] = val; } }); }
这段代码作了2 个事
一、使用 props 在 vm 上占位,使得能够经过 http://vm.xxx 的形式访问到 props
二、设置 [Object.defineProperty] 的 get 和 set ,间接获取和赋值 vm._props
全部访问赋值 props,转接到 vm._props 上,直观以下图
上个实例,方便你们看
说完收工