一文应对 vue2 原理
1.源码详解
1.精简版的 vue 源码

2.初始化的流程
1.注册全局指令
2.注册 mixin
3.初始化插件
4.初始化 vue(init 方法)
在初始化之前,前三项就已经被加到 options 对象里面了!

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

3.模版编译(解析 DOM)
_compile 方法是入口
里面的 compile 方法才是核心
下面这个图只是 compile 方法里面的 compileNode 的主要过程:
非文本结点:自定义组件 和 浏览器元素 dom 标签
文本节点:普通文本 和 模板语法{
走完上面这些流程之后,des 构建完毕:主要是指令的容器(指令集 directives)

开始处理 des 数组(此时还在 compile 函数的流程里面),进行模版渲染
4.模版渲染:指令的处理
注意:此时还是在 compile 方法里面,并且在 compileNode(对于 text 类型是使用 Promise 异步将 dom 插入到页面中的)之后也就是收集完 des 之后!
// compileTextNode 函数里面的内容:
// 异步替换节点是为了防止在compileNodeList中循环处理节点时,突然删掉其中一个节点而造成处理错误
Promise.resolve().then(() => {
//原理:这里是微任务,所以会在js事件循环的末尾进行!
replace(node, frag); //把frag文本插入到页面中
});
首先进行排序(根据指令的权重)
遍历 des 指令集,处理每一个指令,优先处理 for 循环
把 des 数组中的指令对象传入 Directive 构造函数里面,进行实例化,然后执行_bind 方法:调用指令上面的 bind 和 update 函数,最终达到渲染数据到页面上的效果!
watcher是什么?涉及数据劫持和依赖收集
第一个 update 其实就是初始化渲染的过程
文本节点的渲染不会立刻生效,因为它是在 Promise 微任务里面!
5.defineReactive 数据劫持 + get 依赖收集
依赖收集有两个入口:一个是数据劫持,一个是依赖收集
1.初始化过程中的_initData 函数 ——> 但是这里实际上走不到 depend 方法,没有进行依赖收集,只是通过创建 observer,从而进行了数据劫持

核心是创建了观察者对象 observe,观察者里面又为每一个属性创建 dep 发布者!并且使用 Object.defineProperty 定义的 get 和 set 方法!
2.模版渲染过程中 Directive 类的_bind 函数(在调用完指令的 bind 和 update 之后),遍历 des 数组实例化 Directive 对象,并绑定指令的 update 到 watcher 对象的时候(其实这里就是为了渲染数据,watcher 里面有数据,update 是渲染的方法)——> 这里走了数据劫持的 get 方法里面依赖收集的核心方法:depend,进行了依赖收集
在模版渲染时,进行真实数据获取的时候(为了传递给指令的 update 函数),即 new Watcher 的时候,会触发 Object.defineProperty 定义的 get 方法!
并且在 Watcher 构造函数里面会把 Dep.target 会指向 Watcher 对象,只有在 watcher 中进行 get 获取值,才会触发依赖收集


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.可描述更加详细的过程

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

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