1、什么是变化侦测?

通常,在运行时应用内部的状态会不断发生变化,此时需要不停地重新渲染页面,这时如何确定状态中发生了什么变化?

变化侦测就是用来解决这个问题的,它分为两种类型,一种是“推”(push),另一种是拉(pull)。

angular和react的变化侦测属于“拉”,即当状态发生变化时,它不知道那个状态变了,只知道状态可能变了,然后会发送一个信号通知框架,框架内部收到信号后,就会进行对比找出哪些dom需要重新渲染,在angular中称为脏检查,在react中则使用虚拟dom。

而vue框架的变化侦测属于“推”。当状态发生变化时,vue立刻就知道了,而且在一定程度上知道哪些状态变了。因此,它知道的信息更多,也就可以进行更细粒度的更新。

所谓细粒度的更新,也就是说,假如一个状态绑定了好几个依赖,每个依赖表示一个具体的dom节点,那么当这个状态发生变化时,向这个状态的所有依赖发送通知进行dom更新操作,也可以看出“拉”的粒度是最粗的。

2、如何追踪变化?

两种方法,Object.defineProperty和es6的Proxy。由于es6在浏览器中的支持度并不理想,所以vue使用的是第一种。

知道了Object.defineProperty可以侦测对象的变化,那就可以写出以下的代码:

function defineReactive (data, key, val) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
return val
},
set: function (newval) {
if (val === newval) {
return
}
val = newval
}
})
}

该函数作用是定义一个响应式数据,也就是在这个函数中进行追踪变化,当从data的key中读取数据时,get函数被触发;当从data的key中设置数据时,set函数被触发。

3、如何收集依赖?

我们之所以要观察数据,目的是当数据的属性发生变化时,可以通知那些曾经使用了该数据的地方。

例如:

<template>
<h1>{{name}}</h1>
</template>

该模版中使用了name,当它发生变化时,要向使用了它的地方发送通知。

实现的方法是,把用到数据name的地方收集起来,然后属性发生变化时,把之前收集好的依赖循环触发一遍。也就是在getter中收集依赖,setter中触发依赖。

4、依赖收集在哪里?

首先想到的是每个key都有一个数组,用来存储当前key的依赖。假设依赖是一个函数,保存在window.target上,现在可以把上面的defineReactive函数改造下:

function defineReactive (data, key, val) {
let dep = [] //new
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
dep.push(window.target)
return val
},
set: function (newval) {
if (val === newval) {
return
}
// new
for (let i=0;i<dep.length;i++) {
dep[i](newval, val)
}
val = newval
}
})
}

这里新增了数组dep,用来存储被收集的依赖,然后在set被触发时,循环dep以触发收集到的依赖。

这里可以专门封装一个帮助我们管理依赖的类,用来收集、删除和发送通知的类:

export default class dep {
constructor () {
this.subs = []
} addSub (sub) {
this.subs.push(sub)
} removeSub (sub) {
remove(this.sub, sub)
} depend () {
if (window.target) {
this.addSub(window.target)
}
} notify () {
const subs = this.subs.slice()
for (let i=0,l = subs.length;i<1;i++) {
subs[i].update()
}
}
} function remove (arr, item) {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
}

再改造下之前的函数:

function defineReactive (data, key, val) {
let dep = new dep() //update
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function () {
dep.depend()
return val
},
set: function (newval) {
if (val === newval) {
return
}
val = newval
// new
dep.notify()
}
})
}

这样就清晰很多了

最新文章

  1. windows命令
  2. SQL Server获取自增列下一个Id
  3. Win8 安装 Scrapy
  4. [全文检索]Lucene基础入门.
  5. Centos安装Memcached和(Nginx)Memcache扩展详细教程
  6. Java中静态类型检查是如何进行的
  7. java:如何用代码控制H2 Database启动
  8. Android手机的上网功能需要用到APN(网络接入点)的设置 电信
  9. Leetcode 155 Min Stack 小顶堆+栈,优先队列实现 难度:0
  10. leetcode 127. Word Ladder ----- java
  11. Nginx工作原理和优化、漏洞
  12. 基于注解的EF
  13. lucene 简单搜索步骤
  14. Adreno OpenCL坑——bool转int
  15. 使用Apache服务部署静态网站
  16. CentOS7 安装FastDFS分布式文件系统
  17. Python基础-python数据类型之集合(四)
  18. 20170912xlVBA批量导入txt文件
  19. 转 cousera computational neuroscience week5 学习笔记(part 1)
  20. Hive 的基本概念

热门文章

  1. MySQL | Xtrabackup 安装
  2. 生成Dll在Unity中使用
  3. GYM101471D「ICPC2017 WF」Money for Nothing
  4. CF277E Binary Tree on Plane
  5. 抓包工具-Charles
  6. java使用IO读写文件
  7. height设置100%不起作用
  8. 最近被旷视的YOLOX刷屏了!
  9. 网络损伤仪WANsim--不同的部署方式
  10. HTML5(五)——Canvas API