Vue 源码剖析 —— 变化侦测相关 API 实现原理
栏目: JavaScript · 发布时间: 5年前
内容简介:vm.$watch(expOrFn, callback, [options])由于前面还提到,
vm.$watch(expOrFn, callback, [options])
- 返回值: unwatch { Function }
- 用法:用于观察一个表达式或 computed 函数在 Vue.js 实例上的变化,同时给回调函数传入新数据和旧数据作为参数。
- options参数: {deep, immediate} ,其中 deep 指定是否观察对象内部值的变化, immediate 指定是否立即执行回调函数
实现原理
Vue.prototype.$watch = function(expOrFn, cb, options) { const vm = this options = options || {} const watcher = new Watcher(vm, expOrFn, cb, options) if (options.immediate) { cb.call(vm, watcher.value) } return function unwatch() { watcher.teardown() } } 复制代码
由于 $watch 函数的第一个参数 expOrFn 可以是函数也可以是字符串表达式,所以这里需要改写一下 Watcher 类:
class Watcher { constructor (vm, expOrFn, cb) { this.vm = vm // 新增 if (typeof expOrFn === 'function') { // 为函数时,不止可以返回动态数据,其中所有访问到的数据也都会被 Watcher 观察 this.getter = expOrFn } else { this.getter = parsePath(expOrFn) } this.cb = cb this.value = this.get() } ... } 复制代码
前面还提到, vm.$watch 函数最后返回的是一个 unwatch 函数,顾名思义,它的作用是取消观察函数。执行 unwatch 函数其实就是执行当前 Watcher 实例的 teardown 函数。目前我们并没有在 Watcher 内部记录订阅的 Dep ,所以又要进行一次改写:
class Watcher { constructor (vm, expOrFn, cb) { this.vm = vm this.deps = [] // 新增 this.depIds = new Set() // 新增 if (typeof expOrFn === 'function') { // 为函数时,不止可以返回动态数据,其中所有访问到的数据也都会被 Watcher 观察 this.getter = expOrFn } else { this.getter = parsePath(expOrFn) } this.cb = cb this.value = this.get() } ... addDep (dep) { const id = dep.id if (!this.depIds.has(id)) { this.depIds.add(id) this.deps.push(dep) dep.addSub(this) } } } // 给 Dep 加上 id let uid = 0 class Dep { constructor () { this.subs = [] } addSub (sub) { // 新增 if(somethingToWatch) { somethingToWatch.addDep(this) } //this.subs.push(sub) } ... } 复制代码
到现在,不仅是 Dep 会记录数据发生变化时,需要通知哪些 Watcher ,而 Watcher 中也同样记录了自己会被那些 Dep 通知,也就是说,这是一个多对多的关系。添加了 deps 属性后, teardown 函数的实现就很简单了,这里就省略不写。
最后,可以看下 deep 参数的实现。当 deep 为 true 时,需要监听当前对象的所有子值。实现原理和 Watcher 监听某个值的过程类似,通过访问这个数据,触发当前数据收集依赖的逻辑,把自己收集进去,故可以通过递归遍历当前要监听对象的所有子值来实现。
vm.$set
API 用法
vm.$set(target, key, value)
- 用法: 在 target 中添加属性 key , 如果 target 是响应式的,那么新增的 key 值也是响应式的。
对于数组的处理
数组的处理比较直观,可以直接利用包裹好的 splice 函数实现:
function set(target, key, val) { if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key) target.splice(key, 1, val) return val } } 复制代码
对于对象的处理
function set(target, key, val) { ... // 新增 if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = target.__ob__ // _isVue 标记是否是 Vue 实例, ob.vmCount 标记是否是根数据对象 if (target._isVue || (ob && ob.vmCount)) { // 报错 ... return val } if (!ob) { target[key] = val return val } defineReactive(ob.value, key, val) ob.dep.notify() return val } 复制代码
vm.$delete
API 用法
vm.$delete(target, key)
由于在 Vue.js 中,删除某个属性无法自动向依赖发送通知,所以包装了一个 vm.$delete 避免出现下面这种比较丑陋的代码 =. =:
delete this.obj.name this.obj.__ob__.dep.notify() 复制代码
下面是 vm.$delete 的实现:
function delete(target, key) { if (Array.isArray(target) && isValidArrayIndex(key)) { target.splice(key, 1) return } const ob = target.__ob__ if (!hasOwn(target, key)) return delete ob[key] if (!ob) return ob.dep.notify() } 复制代码
本系列文章均是深入浅出 Vue.js的学习笔记,有兴趣的小伙伴可以去看书哈。
以上所述就是小编给大家介绍的《Vue 源码剖析 —— 变化侦测相关 API 实现原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Vue 源码剖析 —— 对象变化侦测
- 读vue的变化侦测
- Vue侦测相关api
- 海康威视网络摄像头-预览出现绿色移动侦测规则框
- Sophos Intercept X EDR:智能、简单的端点侦测与响应
- 【Java集合源码剖析】ArrayList源码剖析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ant Colony Optimization
Marco Dorigo、Thomas Stützle / A Bradford Book / 2004-6-4 / USD 45.00
The complex social behaviors of ants have been much studied by science, and computer scientists are now finding that these behavior patterns can provide models for solving difficult combinatorial opti......一起来看看 《Ant Colony Optimization》 这本书的介绍吧!