学习Vue---5.Vue源码分析

分析 vue 作为一个MVVM 框架的基本实现原理

一、预备知识

1. [].slice.call(lis): 将伪数组转换为真数组

2. node.nodeType: 得到节点类型

节点:document(html文件节点)、Element(元素节点)、Attribute(属性节点)、Text(文本节点)

3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)

4. Object.keys(obj): 得到对象自身可枚举属性组成的数组

5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性

6. DocumentFragment: 文档碎片(高效批量更新多个节点)

二、数据代理

1.数据代理

通过一个对象代理对另一个对象(在前一个对象内部)中属性的操作(读/写)

2.vue 数据代理

data 对象的所有属性的操作(读/写)由 vm 对象来代理操作

3.好处

通过 vm 对象就可以方便的操作 data 中的数据

4.基本实现流程

  1. 通过 Object.defineProperty() 给 vm 添加与 data 对象的属性对应的属性描述符
  2. 所有添加的属性都包含 getter/setter
  3. getter/setter 内部去操作 data 中对应的属性数据

三、模板解析

1.模板解析的基本流程

1) 将 el 的所有子节点取出,添加到一个新建的文档 fragment 对象中

2) 对 fragment 中的所有层次子节点递归进行编译解析处理

  1. 对大括号表达式文本节点进行解析

  2. 对元素节点的指令属性进行解析

    1. 事件指令解析

    2. 一般指令解析

3) 将解析后的 fragment 添加到 el 中显示

2.大括号表达式解析

  1. 根据正则对象得到匹配出的表达式字符串:子匹配/RegExp.$1 name
  2. 从 data 中取出表达式对应的属性值
  3. 将属性值设置为文本节点的 textContent

3.事件指令解析

  1. 从指令名中取出事件名
  2. 根据指令的值(表达式)从 methods 中得到对应的事件处理函数对象
  3. 给当前元素节点绑定指定事件名和回调函数的 dom 事件监听
  4. 指令解析完后,移除此指令属性

4.一般指令解析

1) 得到指令名和指令值(表达式) text/html/class msg/myClass

2) 从 data 中根据表达式得到对应的值

3) 根据指令名确定需要操作元素节点的什么属性

  1. v-text---textContent 属性
  2. v-html---innerHTML 属性
  3. v-class--className 属性

4) 将得到的表达式的值设置到对应的属性上

5) 移除元素的指令属性

四、数据绑定

1.数据绑定

一旦更新了 data 中的某个属性数据,所有界面上直接使用或间接使用了此属性的节点都会更新。

2.数据劫持

  1. 数据劫持是 vue 中用来实现数据绑定的一种技术
  2. 基本思想:通过 defineProperty() 来监视 data 中所有属性(任意层次)数据的变化, 一旦变化就去更新界面

3.四个重要对象

实现数据的绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器 Observer,用来监听所有属性。

如果属性发生变化了,就需要告诉订阅者 Watcher 看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器 Dep 来专门收集这些订阅者,然后在监听器 Observer 和订阅 Watcher 之间进行统一管理。

接着,我们还需要有一个指令解析器 Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者 Watcher,并替换模板数据或者绑定相应的函数。此时当订阅者 Watcher 接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。

3.1 Observer(监听器)

  1. 用来对 data 所有属性数据进行劫持的构造函数
  2. 给 data 中所有属性重新定义属性描述(get/set)
  3. 为 data 中的每个属性创建对应的 dep 对象

3.2 Dep(Depend)

  1. data 中的每个属性(所有层次)都对应一个 dep 对象
  2. 创建的时机:
    1. 在初始化 define data 中各个属性时创建对应的 dep 对象
    2. 在 data 中的某个属性值被设置为新的对象时
  3. 对象的结构:
  4. subs 属性说明
    1. 当 watcher 被创建时,内部将当前 watcher 对象添加到对应的 dep 对象的subs 中
    2. 当此 data 属性的值发生改变时,subs 中所有的 watcher 都会收到更新的通知,从而最终更新对应的界面

3.3 Compiler(指令解析器)

  1. 用来解析模板页面的对象的构造函数(一个实例)

  2. 利用 compile 对象解析模板页面

  3. 每解析一个表达式(非事件指令,如{{}}或v-text,v-html)都会创建一个对应的 watcher 对象,并建立 watcher 与 dep 的关系

  4. complie 与 watcher 关系:一对多的关系

3.4 Watcher(订阅者)

  1. 模板中每个非事件指令或表达式都对应一个 watcher 对象(与模板中表达式[不包括事件指令]一一对应)
  2. 监视当前表达式数据的变化
  3. 创建的时机:初始化的解析大括号表达式/一般指令时创建
  4. 对象的组成

 

总结:dep 与 watcher 的关系 --> 多对多

vm.name = 'abc'-->data中的name属性值变化-->name的set()调用-->dep-->相关的所有watcher-->cb()-->updater

  1. data 中的一个属性对应一个 dep,一个 dep 中可能包含多个 watcher(模板中有几个表达式使用到了同一个属性)【{{name}}/v-text="name"】

  2. 模板中一个非事件表达式对应一个 watcher,一个 watcher 中可能包含多个dep【多层表达式:a.b.c】

  3. 数据绑定使用到2个核心技术

    1. defineProperty()
    2. 消息订阅与发布

4.MVVM原理图分析

 

4.1 初始化阶段

MVVM 中会创建 Observer(用来劫持/监听所有属性)和 Compile(解析指令/大括号表达式),

Observer要劫持就需要对应的set()方法,所以在observer中为每一个属性创建了一个 dep 对象(与 data 中的属性一一对应)

Compile(做了两件事)

  1. 目的是初始化视图(显示界面),调用 updater(有很多更新节点的方法)
  2. 为表达式创建对应的 Watcher ,同时指定了更新节点的函数

Watcher 和 Dep 建立关系:

  1. watcher 放到 dep 中(添加订阅者),dep 中有一个 subs,是用来保存 n 个 watcher 的数组容器
  2. dep 放到 watcher 中,watcher 中的 depIds 是用来保存 n 个 dep 的对象容器。为了判断 dep 与 watcher 的关系是否已经建立(防止重复的建立关系)

以上都是初始化阶段会经历的过程

4.2 更新阶段

vm.name = 'Tom' 导致 data 中的数据变化,会触发监视 data 属性的 observer 中的 set() 方法,然会它又会通知 dep,dep 会去通知它保存的所有相关的 watcher,watcher 收到信息后,其回调函数会去调用 updater 更新界面

如下图所示:(黑线是初始化阶段,红线是更新阶段)

五、双向数据绑定

  1. 双向数据绑定是建立在单向数据绑定(model==>View)的基础之上的
  2. 双向数据绑定的实现流程:
    1. 在解析 v-model 指令时,给当前元素添加 input 监听
    2. 当 input 的 value 发生改变时,将最新的值赋值给当前表达式所对应的 data 属性

 

 

参考链接:

【1】Node - Web API 接口参考 | MDN

posted @   nxf_rabbit75  阅读(151)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
一、预备知识1. [].slice.call(lis): 将伪数组转换为真数组2. node.nodeType: 得到节点类型3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)4. Object.keys(obj): 得到对象自身可枚举属性组成的数组5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性6. DocumentFragment: 文档碎片(高效批量更新多个节点)二、数据代理1.数据代理2.vue 数据代理3.好处4.基本实现流程三、模板解析1.模板解析的基本流程2.大括号表达式解析3.事件指令解析4.一般指令解析四、数据绑定1.数据绑定2.数据劫持3.四个重要对象3.1 Observer(监听器)3.2 Dep(Depend)3.3 Compiler(指令解析器)3.4 Watcher(订阅者)总结:dep 与 watcher 的关系 --> 多对多4.MVVM原理图分析4.1 初始化阶段4.2 更新阶段五、双向数据绑定
点击右上角即可分享
微信分享提示