vue2源码分析
vue 中的潜规则
- 以
\_
开头的数据表示私有属性,不能被访问 - 以 $ 开头的数据表示只读数据
proxy 数据代理
相当于用 this.name
访问 this.\_data.name
例如:
1 | var o1 = { name: "张三" }; |
发布订阅模式
目的:解耦,让各个模块之间没有紧密的联系。
在 vue 中,整个更新是按照组件的单位进行判断,以节点为单位进行更新。
- 如果代码中没有自定义组件,则在比较算法的时候,会将全部的模板对应的虚拟 dom 进行比较
- 如果代码中含有自定义组件,则在比较算法的时候,会判断是那一个组件进行了更新,只会判断更新数据的组件,其他组件不会更新。
特征:
- 有一个中间的全局容器,用来存储可以被触发的的东西(函数,对象)
- 有一个方法可以往容器中传入东西,(函数,对象)
- 有一个方法可以将容器中的东西取出来并且使用,如果是函数则是函数调用,如果是对象,则是对象的方法调用
js 中的类型比较
- 基本类型比较值
- 引用类型比较引用地址
- 引用类型和基本类型比较时转化为基本类型行进行比较,如果是 === 严格比较,不会转换
1 | [] == [] // false |
JavaScript 是解释型语言,从左到右解析时碰到第一个 [] 会在内存中声明一个引用地址来存放一个空数组,接着往右继续解析时会在内存中继续声明一个引用地址来比较,由于两个空数组不是同一个引用地址,所以判断结果为 false
1 | let a = []; |
上面代码首先声明了 a 为一个空数组,下面判断时相当于都指向了同一个引用地址,所以结果为 true
事件移除分析
1 | // 获取按钮实例 |
上面代码并不会移除按钮的点击事件,因为两个方法不是同一个引用地址,所以不会被移除,点击按钮仍然可以出发打印
改进:
1 | let f = () => { |
改进后让添加的方法和移除的方法都执向同一个引用地址,这样可以移除按钮绑定的事件
引入 Watcher
- 实现(分成两部)
- 修改后刷新(响应式)
- 依赖收集
在 Vue 中提供有构造函数 Watcher ,有以下几个方法
get()
用来计算或执行处理函数update()
公共的外部方法,该方法会触发内部的run方法run()
运行,用来判断内部运行是异步运行还是同步运行,这个方法最终会调用内部的get方法cleanupDep()
清除队列
其中 get()
方法来完成页面渲染操作
引入 Dep 对象
该对象提供 依赖收集(depend),和派发更新(notify)的功能
在notify中调用 Watcher 的 update 方法
Watcher 和 Dep
之前将渲染 watcher 放在全局作用域中,这样的处理是有问题的。
- vue项目中包含很多组件,每个组件都是自治的
- 那么 watcher 可能会有多个
- 每一个 watcher 描述一个渲染行为或计算行为
- 子组件发生数据更新,页面就要进行渲染,(vue中是局部更新)
- 例如:vue中推荐使用 计算属性 代替复杂的 插值表达式
- 计算属性会伴随其使用的属性变化而变化
name() => this.firstName + this.lastName
- 上述表达式中计算属性 name 依赖于 firstName 和 lastName
- 只要被依赖的属性发生变化,那么就会促使计算属性重新计算,这种重新计算行为就是
watcher
来完成的。
- 依赖收集 和 派发更新 是怎么运行起来的???
- 在 vue 访问页面的时候就收集到了用到的属性,修改的时候进行更新,收集到什么,就更新什么
所谓的依赖收集实际上就是告诉当前的 watcher 什么属性被访问到了,那么这个 watcher 在 计算 或者 渲染页面 的时候就会将这些收集的属性进行更新,没有收集到的属性不会被更新,这样就起到的局部更新的作用。
如何将 属性 与 当前 watcher 关联起来???
- 在全局准备一个 targetStack(target 栈,简单理解为一个 watcher “数组”,吧一个操作中使用到的watcher存储起来)
- 在watcher调用get的时候,将当前watcher放到全局,在get调用结束之前,将这个全局的 watcher 移除,方便后面的watcher进行更新,由此提供两个方法:pushTarget,popTarget
- 在每一个属性中都有一个Dep对象
- 我们在访问对象属性的时候( get方法 ),我们的渲染 watcher 就在全局中,将属性与 watcher 关联,就是将当前的 watcher 存储到 Dep 中,同时在watcher中也存储当前的Dep对象,是互相引用的关系
- 属性引用的当前的渲染 watcher ,就知道谁渲染它
- 当前渲染 watcher 引用当前访问的属性(Dep对象),知道要去渲染什么属性
在Dep中有一个方法为notify(),内部就是将subs里面的东西取出来,依次调用其 uodate 方法。
- subs里面存储的就是知道要渲染什么属性的 watcher
梳理 watcher 和 dep 之间的关系
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 SZX的开发笔记!
评论