一、准备知识
[].slice.call(list):将伪数组转换为数组node.nodeType:得到节点类型Node接口是所有节点类型的父类,节点从这个接口继承了属性和方法。Object.defineProperty(obj, propertyName, descriptor):给对象添加属性(指定描述符)- 属性描述符
- 数据描述符(4个)
- 访问描述符(2个)
- 属性描述符
Object.keys():得到对象自身可枚举属性组成的数组obj.hasOwnProperty(prop):判断props是否是obj的自身属性DocumentFragment:文档碎片(内存中保存n个element的容器对象,可用于高效批量更新多个节点)
二、数据代理
Object.defineProperty
举例
已知
a.b.xxx,实现a.xxx,即a直接访问xxx// 举例
const vm = new Vue({
el: '#app',
data: {
name: 'zjf'
}
})
console.log(vm.name) // zjf
三、模板解析
去除
el元素中所有子节点保存到一个fragment对象中// node2fragment(el)
// 1. 创建空的fragment
var fragment = document.createDocumentFragment(),
child
// 2. 将el中所有子节点转移到fragment
while (child = el.firstChild) {
fragment.appendChild(child)
}编译
fragment中所有层次子节点(递归)- 对
{ { } }表达式文本节点进行解析:使用正则解析,替换文本 - 对元素节点的指令属性进行解析
- 事件指令:拆解指令类型和指令值,调用
node.addEventListener - 一般指令:拆解指令类型和指令值,根据指令类型确定要操作的属性
- 事件指令:拆解指令类型和指令值,调用
// compileElement(fragment)
// 1. 去除最外层所有子节点
var childNodes = el.childNodes,
// compile对象
me = this
// 2. 遍历所有子节点
[].slice.call(childNodes).forEach(function(node) => {
var text = node.textContent
var reg = /\{\{(.*)\}\}/
if (me.isElementNode(node)) {
// element节点
me.compile(node)
} else if (me.isTextNode(node) && reg.test(text)) {
// text节点
me.compileText(node, RegExp.$1)
}
// 递归遍历子节点
if (node.childNodes && node.childNodes.length) {
me.compileElement(node)
}
})- 对
将编译好的
fragment添加到页面的el元素中
四、数据绑定
解释
一旦更新了
data中的某个属性数据,所有界面上直接使用或间接使用了此属性的节点都会更新。数据劫持
用来实现数据绑定的一种技术(
Object.defineProperty)过程
vm.name = 'a'–>data中的name属性值变化 –>name的set()调用 –>dep–> 相关的所有watcher–>cb()–>updater四个重要对象
Dep:
(1) 实例创建的时机:初始化时给
data的属性进行数据劫持时创建的;(2) 实例个数:与
data中的属性一一对应;(3) 实例结构:
id:标识subs:n个相关的watcher的容器Watcher:
(1) 实例创建的时机:初始化时解析模板语法时创建的;
(2) 实例个数:与模板中表达式(不包含事件指令)一一对应;
(3) 实例结构:
cb:用于界面更新的回调vm:vmexp:对应的表达式depIds:相关的n个dep的容器对象value:当前表达式对应的value
Dep与Watcher之间的关系(n:n的关系)
data属性 –>Dep–> n个watcher(场景:模板中有多个表达式使用了此属性({ { a } } v-text="a"))表达式 –>
Watcher–> n个dep(场景:多层表达式(a.b.c))
Dep与Watcher如何建立关系
(1)
Observer遍历data属性value每个
value里创建一个与value对应的dep在
value的get操作中调用dep的depend方法去绑定依赖
(2) 每个模板表达式建立与表达式对应的
watcherwatcher建立时会根据表达式值去取属性值value将
Dep的静态属性target暂时置为本watcher触发一次
value的get操作value的get操作执行dep的depend方法,进而触发watcher和dep互相绑定