vue源码解析
目标
- 深入理解vue底层原理
- 手写vue核心部分
知识要点
vue工作机制
- 第一步,将data添加到响应式系统中;
new Observer( this.$data )
:内部将Vue实例对象的属性遍历执行Object.defineProperty(this,key,{set(newVal){},get(){}});
set(newVal){ val = newVal;dep.notify() }
:用于监听属性值的改变,并通知该属性的所有订阅者;get(){ dep.addSub(Dep.target) }
:用于监听属性值被获取,并将订阅者(Watcher实例)push
到订阅者数组中,由订阅者实例化对象时引发;
- 第二步,代理
this.$data
数据
Object.keys(this.$data).forEach(key=>{
this._proxy(key)
})
-
第三步,处理el;
new Compiler(this.$el,this)
// this:vue实例- 编译模板,当正则找到
{{msg}}
中的属性名时,new Watcher(node,name,this.vm)
//当前文本节点,对应的属性名msg,vue实例; - 在节点中找到属性名–>触发创建Watcher实例–>触发属性get() -->添加订阅者到该属性的订阅者数组;
-
总结:
Observer:
用于能监听data属性被get或被set,让每个属性成为发布者,并提供一个subs数组
存放订阅者;Compiler:
用于编译模板,找到模板中data属性的使用节点,将节点变成订阅者,并将属性值渲染到视图中;
-
template->ast->render->vdom
template()编译分为三个阶段:parse(作语法分析):使用正则解析template中vue指令、变量,形成ast抽象语法树;
optimize(使最优化):标记静态节点,在diff算法时略过,优化性能
generate(产生):把ast抽象语法树转化为渲染函数render function
vue响应式原理
Object.keys(对象):遍历对象所有可枚举的属性名,以数组形式返回;
Constructor(构造函数):是在对象创建或者实例化时候被调用的方法,通常使用该方法来初始化数据成员和所需资源。
- class Vue{}:实现遍历给所有data属性设置get、set方法,无论属性是否在组件中被用到。
- class Dep{}:为所有属性分别创建一个空数组deps,用于(push)存放组件中的所有使用到该属性的元素即(所有Watcher实例);一个属性对应一个Dep实例对应一个或多个Watcher实例(一个组件元素)。
- 与get(通过this.$data.test触发)关联:get执行时,接收并存放外部发来的该属性所对应的组件元素订阅信息。(一般没在页面中使用就不调用get方法,同时也不必为其产生Watcher实例);
- 与set方法(通过this.$data.test=’'触发)关联:set方法被执行时,接收该属性改变,遍历deps发布给该属性所有的watcher;
- claa Watcher{}:在编译时,将组件中使用到某个属性的某个元素传递给Dep做订阅,接受Dep发布的信息,通知视图更新自身这个元素所使用的这个属性,所以内部定义了updated方法。
依赖收集与追踪
先给每个data属性创建一个Dep实例并设置get、set方法,当方法被执行时,Dep、Watcher就起作用了。get的执行与watcher实例的创建都要在初始编译过程触发,set在更改数据后再编译时触发。
执行get方法时:‘外界’会产生一个Watcher实例,将Watcher实例传到了Dep实例中存放起来,所以get方法必须执行,要不然无法记录订阅者。
执行set方法:便会执行Dep实例中的通知方法notify(),这个方法会遍历deps中所有Watcher实例并让他们都执行更新操作update(),最后每个Watcher实例再各自去通知自己的视图做改变。
编译compile
把特殊的符号编译,收集起来创造Watcher。
vue编译过程是怎样的?
首先,什么是编译,为什么编译,怎么实现
vue模板语句html不识别,通过编译的过程,可以进行依赖收集,将data数据模型与视图之间绑定关系,模型变化可以通知依赖进行更新,做到模型驱动视图的变化。
双向绑定
v-model,在编译时解析,给model所属的节点加input事件监听,通过input事件将输入的最新的值设置到vue实例上,因为vue实例响应化,响应化的setter函数会通知依赖进行更新操作。