文档学习
参考:
写在最前面 | Vue源码系列-Vue中文社区 (vue-js.com)
源码目录:
├─dist # 项目构建后的文件
├─scripts # 与项目构建相关的脚本和配置文件
├─flow # flow的类型声明文件
├─src # 项目源代码
│ ├─complier # 与模板编译相关的代码
│ ├─core # 通用的、与运行平台无关的运行时代码
│ │ ├─observe # 实现变化侦测的代码
│ │ ├─vdom # 实现virtual dom的代码
│ │ ├─instance # Vue.js实例的构造函数和原型方法
│ │ ├─global-api # 全局api的代码
│ │ └─components # 内置组件的代码
│ ├─server # 与服务端渲染相关的代码
│ ├─platforms # 特定运行平台的代码,如weex
│ ├─sfc # 单文件组件的解析代码
│ └─shared # 项目公用的工具代码
└─test # 项目测试代码
变化侦测篇
学习Vue
中如何实现数据的响应式系统,从而达到数据驱动视图。
我个人认为前端的核心其实是怎么展现一个数据,以及对于这个数据的一些相关操作的界面展示,无论是一些基本的标签还是css属性,更还有网络协议和js语言这种经常要操作数据的存在,可见数据的重要性,所以本篇关注的是vue2内如何实现的数据监测和视图响应
实现的基础
在ES6,vue3中存在着一种新的类型叫做proxy这种数据类型的数据访问需要调用其中的。value属性,也自带了一些数据劫持的方法,而在vue2中,实现数据劫持取出和赋值的是Object.defineProperty
方法
通过Object.defineProperty
方法可以定义 对象的get和set方法 在其中再进行其他活动
Object.defineProperty(object1, 'property1', {
get() set()});
深度遍历响应式
对于一个对象或者结构体(多种数据叠加在一个对象中)我们需要深度遍历递归且给每一个数据定义它的getset实现它的响应式
export class Observer{
constructor(){
//定义属性,解析条件判断处理不同类型的值
//官方采用this.walk() 实现步骤性拆分
//一个深度响应算法--》判断对象属性然后递归响应
def(value,'__ob__',this)//给响应式数据打上自己的标签
}
walk(){}
defineReactive(){}
}
现在我们初步实现了每一个对象内属性的响应式(稍后实现数组内的响应式)
依赖收集(官方采用的方式)
视图中对应的数据改变了,但是依赖该数据的几个视图模型该怎样接受这种变化呢?
提要:
1.js是单线程的任务处理机制 ,在任务队列的调度之中,始终都只有一个唯一运行函数(window.target也是全局唯一值)
2.谁读取了数据,谁就依赖了该数据,该数据更改时应该通知所有依赖该数据的组件
通过第二点,官方给每个数据都建立了一个依赖结构
class Dep {
constructor () {
this.subs = []//存储读取者队列
}
}
通过自身定义的一些方法,实现对读取者的存取
既然依赖已经有了自己存储的数据结构,那么该怎么去读取或者通知呢?
在数据实例中定义自己的依赖结构—>通过getter方法读取当前正在运行的全局函数(也就是正在读取该值的函数)存入读取者队列,然后通过setter方法通知所有的dep数组内对象
通过第一点:我们需要知道一个具体明确的运行对象 也就是官方定义的Watcher
谁用到了数据,谁就是依赖,我们就为谁创建一个Watcher
实例。在之后数据变化时,我们不直接去通知依赖更新,而是通知依赖对应的Watch
实例,由Watcher
实例去通知真正的视图。
export default class Watcher {
constructor (vm,expOrFn,cb) {
//获取视图 和数据对象 还有回调
}
get(){//通过上面定义的视图 以及格式化后的属性调用 和call方法调用相应视图的值 并且为读取值设置自己的window.target}
update(){//由该对象去通知挥动通知视图进行数据的更新 达到依赖收集和响应式的目的}
}
![img](https://vue-js.com/learn-vue/assets/img/3.0b99330d.jpg)
其整个流程大致如下:
Data
通过observer
转换成了getter/setter
的形式来追踪变化。- 当外界通过
Watcher
读取数据时,会触发getter
从而将Watcher
添加到依赖中。 - 当数据发生了变化时,会触发
setter
,从而向Dep
中的依赖(即Watcher)发送通知。 Watcher
接收到通知后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户的某个回调函数等。
对于Observer对象的优化
在递归之时Array这个对象只有对于引用的get和set 对于其中的数据操作并没有相对的set方法
所以我们需要对Array内体的本身方法进行一次重构
![img](https://vue-js.com/learn-vue/assets/img/2.b446ab83.png)
数组方法的访问是按照原型链上的原型方法进行调用的,所以要实现方法的变革就需要在数组实例和数组原型之间添加拦截器(下面是拦截器对象的实现)
只是重构这几个数组方法–>methodsToPatch.forEach函数 对拦截器对象上的方法进行响应式 (js内函数也是对象的特点)这其中就可以进行setter的通知了
然后在递归响应式observer类中改变且插入数组元素的_proto_值(给数组加上了拦截器)
最后就是实现它的通知依赖更新(调用原型上拦截器函数时调用this得到value的—ob—唯一响应式对象 然后进行notify依赖通知)
优化:遍历对象数组再进行一次生成observe类行为-------------switch分支多case值再进行响应式
switch (method) {
case 'push':
case 'unshift':
inserted = args // 如果是push或unshift方法,那么传入参数就是新增的元素
break
case 'splice':
inserted = args.slice(2) // 如果是splice方法,那么传入参数列表中下标为2的就是新增的元素
break
}
if (inserted) ob.observeArray(inserted) // 调用observe函数将新增的元素转化成响应式
遗留问腿:对于一个数组的下标访问问题。