一、准备知识
[].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
:vm
exp
:对应的表达式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) 每个模板表达式建立与表达式对应的
watcher
watcher
建立时会根据表达式值去取属性值value
将
Dep
的静态属性target
暂时置为本watcher
触发一次
value
的get
操作value
的get
操作执行dep
的depend
方法,进而触发watcher
和dep
互相绑定