Skip to content

一文应对 vue2 原理

1.源码详解

1.精简版的 vue 源码

image-20230827013840943

2.初始化的流程

image-20230827060436762

1.注册全局指令

2.注册 mixin

3.初始化插件

4.初始化 vue(init 方法)

在初始化之前,前三项就已经被加到 options 对象里面了!

image-20230827045556943

init 方法里面的 mergeOptions 是关键,mergeOptions 里面 guardComponents 是关键,主要是实现了自定义组件对象对于 vue 构造函数的继承,经过这个方法之后 options.compontents(也就是组件里面配置的那个 compontents 选项)里面的 my-component 组件就会变成一个构造函数!但是此时还没有进行实例化!

image-20230827060501711

_init 函数的总结口诀:H(init) Mix C P M D W Com E H(created)

CPMDWCE

image-20230827095802841

3.模版编译(解析 DOM)

_compile 方法是入口

里面的 compile 方法才是核心

下面这个图只是 compile 方法里面的 compileNode 的主要过程:

image-20230827100423804

非文本结点:自定义组件 和 浏览器元素 dom 标签

文本节点:普通文本 和 模板语法{

走完上面这些流程之后,des 构建完毕:主要是指令的容器(指令集 directives)

image-20230827121054527

开始处理 des 数组(此时还在 compile 函数的流程里面),进行模版渲染

4.模版渲染:指令的处理

注意:此时还是在 compile 方法里面,并且在 compileNode(对于 text 类型是使用 Promise 异步将 dom 插入到页面中的)之后也就是收集完 des 之后!

js
// compileTextNode 函数里面的内容:
// 异步替换节点是为了防止在compileNodeList中循环处理节点时,突然删掉其中一个节点而造成处理错误
Promise.resolve().then(() => {
  //原理:这里是微任务,所以会在js事件循环的末尾进行!
  replace(node, frag); //把frag文本插入到页面中
});

首先进行排序(根据指令的权重)

遍历 des 指令集,处理每一个指令,优先处理 for 循环

把 des 数组中的指令对象传入 Directive 构造函数里面,进行实例化,然后执行_bind 方法:调用指令上面的 bind 和 update 函数,最终达到渲染数据到页面上的效果!

watcher是什么?涉及数据劫持和依赖收集

image-20230827230348274

第一个 update 其实就是初始化渲染的过程

文本节点的渲染不会立刻生效,因为它是在 Promise 微任务里面!

5.defineReactive 数据劫持 + get 依赖收集

依赖收集有两个入口:一个是数据劫持,一个是依赖收集

1.初始化过程中的_initData 函数 ——> 但是这里实际上走不到 depend 方法,没有进行依赖收集,只是通过创建 observer,从而进行了数据劫持

image-20230828020010326

核心是创建了观察者对象 observe,观察者里面又为每一个属性创建 dep 发布者!并且使用 Object.defineProperty 定义的 get 和 set 方法!

2.模版渲染过程中 Directive 类的_bind 函数(在调用完指令的 bind 和 update 之后),遍历 des 数组实例化 Directive 对象,并绑定指令的 update 到 watcher 对象的时候(其实这里就是为了渲染数据,watcher 里面有数据,update 是渲染的方法)——> 这里走了数据劫持的 get 方法里面依赖收集的核心方法:depend,进行了依赖收集

image-20230828172455016

在模版渲染时,进行真实数据获取的时候(为了传递给指令的 update 函数),即 new Watcher 的时候,会触发 Object.defineProperty 定义的 get 方法!

并且在 Watcher 构造函数里面会把 Dep.target 会指向 Watcher 对象,只有在 watcher 中进行 get 获取值,才会触发依赖收集

image-20230828024959660image-20230828024909912

6.个人理解

依赖收集的两个入口,都是建立在循环之上的!

1.初始化 data 阶段的_initData方法的 new Observer 里面有 walk 方法对 data 选项进行了循环,使用 defineReactive 方法,给 data 选项的每一个属性添加 dep 对象,并进行数据劫持!

2.模版编译阶段的_compile方法里面的 compile 方法在获取所有 des 对象之后(进入模版解析阶段,渲染数据),对于 des 数组进行了循环,分别对每一个 des 对象创建 new Directive,而里面的_bind 方法又 new Watcher,调用 get 进行了依赖收集!

get 的依赖收集

在 watcher 中进行 get 获取值时,会在 get 方法里面触发 depend 方法:

watcher 的 deps[]和 dep 的 subs[]分别互相存放对方的实例,watcher 和 dep 它俩是相互依赖的

注意:而依赖收集指的就是每一个属性的get和set方法中对于watcher和dep它俩是相互的这种收集!

set 的消息发布

set 方法:本质上没有依赖收集,但是里面用了 get 方法,所以也会依赖收集

subs[]就是是用来告诉发布者 dep,有哪些 watcher 订阅了它的数据!以实现发布订阅式地对于所有使用到这个数据的地方进行更新!

个人对于指令的 update 方法对于数据渲染的贡献的理解:

注意:不管是页面数据的初始化还是数据更新,都是用的相关指令的 update 方法!都是和 des 数组有关系。

1.初始化的时候指令的 update 方法是在_compile 方法里面遍历 des 数组并 new Directive->new Watcher 的时候触发的!

2.更新数据的时候指令的 update 方法是在 set 方法里面的 dep.notify 方法里面通过 watcher 的 update 方法进行触发的!

注意:巧妇难为无米之炊,update 方法没有数据也不行,获取具体的数据值是通过(observer 里面对于属性值的 Object.defineProperty 数据劫持中的)get 方法,依赖了 Dep.target 的指向(在 watcher 里面才能获取数据)

get、set 方法

1.get 的时候会进行依赖收集,构建 watcher 的 deps[]和 dep 的 subs[]

get 是在 new Watcher 和 set 的时候触发

2.set 的时候属性对应的 dep 实例会根据 subs[]依赖数据进行消息的发布,从而让 watcher 重新使用 get 获取数据,然后调用指令的 update 方法进行数据更新

set 是在调用 set 修改 data 的值的时候触发

obersve、dep、watcher 对象

obersve 的创建是在 initeData 阶段,observe 方法,主要目的是给每一个属性都配备一个 dep 对象!并进行数据劫持,创建 get 和 set 方法!——> 为数据的获取和更新服务

dep 的创建就是在创建 obersve 的时候,会在 walk 方法的 defineReactive 方法中给每一个属性都配备一个 dep 对象!——> 为依赖收集做准备,本质上是为更新的时候服务(发布者)

watcher 的创建是在模版渲染阶段 compile 方法,new Directive 的_bind 方法里面,主要目的是进行数据的渲染和依赖收集,调用劫持属性的 get 方法(得到数据的同时进行依赖收集),让数据得以显示或者更新到页面上!——>为数据的渲染和更新服务(订阅者)

注意:watcher 会在首次渲染时直接进行数据的返回,而在更新数据的时候会调用指令的 update 方法,并传入数据

依赖收集的主要目的:为了数据以发布订阅的模式进行响应式更新!

数据劫持与依赖收集的关系:数据劫持的 get 就是为了依赖收集!set 就是为了响应式更新(订阅发布的方式)!

2.面试题

1.请描述一下依赖收集的过程?(vue 的核心)

1.注册:initData,new Observe,设置 get set

2.触发:new Watcher 的时候 get

上面两个答案已经 80 分了

3.可描述更加详细的过程

image-20230828020010326

2.请描述 get 和 set 分别做了什么?

image-20230828194536298

注意:上面这个图里一开始的 directive 对象,指的是模版编译过程中生成的 des 对象,而不是模版渲染过程中的 new Directive 对象!

image-20230828194553928