Vue源码分析
数据代理
1. 数据代理
数据代理是通过一个对象代理对另一个对象中属性的操作(读/写)。
Vue中的数据代理是通过vm
对象来代理data对象中所有属性的操作。优点是能够更方便的操作data
中的数据
2. 基本实现流程
- 通过
Object.defineProperty()
给vm
添加与data
对象的属性对应的属性描述符。 - 所有添加的属性都包含
getter
/setter
。 getter
/setter
内部去操作data中对应的属性数据。
模板解析
1. 模板解析的基本流程
-
将el的所有子节点取出, 添加到一个新建的文档fragment对象中
-
对fragment中的所有层次子节点递归进行编译解析处理
-
对大括号表达式文本节点进行解析
-
对元素节点的指令属性进行解析
-
事件指令解析
-
一般指令解析
-
-
-
将解析后的fragment 添加到el 中显示
2. 大括号表达式解析
- 根据正则对象得到匹配出的表达式字符串: 子匹配/RegExp.$1
- 从data 中取出表达式对应的属性值
- 将属性值设置为文本节点的textContent
3. 事件指令解析
- 从指令名中取出事件名
- 根据指令的值(表达式)从
methods
中得到对应的事件处理函数对象 - 给当前元素节点绑定指定事件名和回调函数的dom事件监听
- 指令解析完后,移除此指令属性
4. 一般指令解析
- 得到指令名和指令值(表达式)
- 从
data
中根据表达式得到对应的值 - 根据指令名确定需要操作元素节点的什么属性
v-text
—textContent 属性v-html
—innerHTML 属性v-class
–className 属性
- 将得到的表达式的值设置到对应的属性上
- 移除元素的指令属性
数据绑定
1. 数据绑定
一旦更新了data
中的某个属性数据,所有界面上直接使用或间接使用了此属性的节点都会更新。
2. 数据劫持
- 数据劫持是Vue中用来实现数据绑定的一种技术
- 基本思想:通过
Object.defineProperty()
来监视data
中所有属性(任意层次)数据的变化,一旦变化就去更新页面。
3. 四个重要对象
1. Observer
-
用来对data中所有属性数据进行劫持的构造函数
-
给data中所有属性重新定义属性描述(主要是get/set方法)
-
为data中的每个属性创建对应的Dep对象
2. Dep (Depend)
-
data中的每个属性(所有层次)都对应一个Dep对象
-
它的实例在什么时候创建?
初始化中的给
data
的属性进行数据劫持时创建的。- 在初始化在初始化 define data 中各个属性时创建对应的 dep 对象
- 在 data 中的某个属性值被设置为新的对象时
-
它的数量是多少?
与
data
中的属性是一一对应的。 -
Dep对象的结构
{
id: 0, // 每个dep都有一个唯一标识id,
subs: [] // n个相关的Watcher对象的容器(数组)
}
- subs属性说明
- 当Watcher被创建时,内部将当前watcher 对象添加到对应的 dep 对象的subs 中
- 当此 data 属性的值发生改变时, subs 中所有的 watcher 都会收到更新的通知,从而最终更新对应的界面
3. Compiler
- 用来解析模板页面的对象的构造函数(一个实例)
- 利用 compile 对象解析模板页面
- 每解析一个表达式(非事件指令)都会创建一个对应的 watcher 对象, 并建立 watcher 与 dep 的关系
- complier 与 watcher 关系: 一对多的关系
4. Watcher
-
模板中每个非事件指令或表达式都对应一个 watcher 对象
-
监视当前表达式数据的变化
-
它的实例在什么时候创建?
初始化中的解析大括号表达式/一般指令时创建。
-
它的数量是多少?
与模板中表达式(不包含事件指令)的个数一一对应。
-
Watcher对象的数据结构
this.cb = cb // 当表达式所对应的数据发生改变时用于更新界面的回调函数
this.vm = vm // vm对象
this.exp = exp // 对应指令的表达式
this.depIds = {"depid": "dep"} // 相关的n个Dep对象的容器(对象)
this.value = this.get() // 当前表达式对应的value值
5. Dep与Watcher之间的关系
-
什么关系?
多对多的关系。
- data属性 -> Dep -> n个Watcher(n>1:当模板中有多个表达式使用了此属性时:
{{ a }}
v-text="a"
) - 表达式 -> Watcher -> n个Dep(n>1:当使用多层表达式时:
a.name.last
)
- data属性 -> Dep -> n个Watcher(n>1:当模板中有多个表达式使用了此属性时:
-
如何建立关系?
使用data中属性的get方法中建立。
-
什么时候建立的?
初始化的解析模块中的表达式创建Watcher对象时建立。
-
数据绑定使用到 2 个核心技术
Object.defineProperty()
- 消息订阅与发布
MVVM原理图分析
初始化
Compile -> Watcher: 为表达式创建对应的watcher,指定更新的函数
Dep: 与data中的属性一一对应
- subs: 保存n个Watcher的数组容器
更新
Dep -> Watcher: 通知所有相关的Watcher
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QhbxZkDF-1626253008691)(F:\技术资料\前端面试知识点\MVVM.png)]
双向数据绑定
- 双向数据绑定是建立在单向数据绑定(model==>View)的基础之上的
- 双向数据绑定的实现流程:
- 在解析 v-model 指令时, 给当前元素添加事件监听(输入框触发方式为input)
- 当 input 的 value 发生改变时, 将最新的值赋值给当前表达式所对应的 data 属性
MVVM原理图分析
[外链图片转存中…(img-EkVatygv-1626253008689)]
初始化
Compile -> Watcher: 为表达式创建对应的watcher,指定更新的函数
Dep: 与data中的属性一一对应
- subs: 保存n个Watcher的数组容器
更新
Dep -> Watcher: 通知所有相关的Watcher
双向数据绑定
- 双向数据绑定是建立在单向数据绑定(model==>View)的基础之上的
- 双向数据绑定的实现流程:
- 在解析 v-model 指令时, 给当前元素添加事件监听(输入框触发方式为input)
- 当 input 的 value 发生改变时, 将最新的值赋值给当前表达式所对应的 data 属性