Vue变化检测Object使用DefineProperty、数组使用方法拦截实现。最近,Vue3.0将采用ES6 Proxy的形式重新实现Vue的变化检测,在官方还没给出新方法之前,我们先实现一个基于Proxy的变化检测。
模块划分
参照之前Vue变化检测的代码,将Vue 变化检测的功能分为以下几个部分。
- Observer
- Dep
- Watcher
- Utils
首先,我们要确定的问题是,将Dep依赖搜集存在哪里。Vue 2.x里,Object的依赖收集放在defineRactive,Array的依收集存入到Observer中。ES6 Proxy里,考虑到让handler访问dep,我们将依赖放入到Observer中。
Observer
observer.js功能代码如下:
import Dep from './dep'; import { isObject } from './utils'; export default class Observer { constructor (value) { // 递归处理子元素 this.obeserve(value); // 实现当前元素的代理 this.value = this.proxyTarget(value); } proxyTarget (targetBefore, keyBefore) { const dep = new Dep(); targetBefore.__dep__ = dep; let self = this; const filtersAtrr = val => ['__dep__', '__parent__'].indexOf(val) > -1; return new Proxy(targetBefore, { get: function(target, key, receiver){ if (filtersAtrr(key)) return Reflect.get(target, key, receiver); if (!Array.isArray(target)) { dep.depend(key); } // sort/reverse等不改变数组长度的,在get里触发 if (Array.isArray(target)) { if ((key === 'sort' || key === 'reverse') && target.__parent__) { target.__parent__.__dep__.notify(keyBefore); } } return Reflect.get(target, key, receiver); }, set: function(target, key, value, receiver){ if (filtersAtrr(key)) return Reflect.set(target, key, value, receiver); // 新增元素,需要proxy const { newValue, isChanged } = self.addProxyTarget(value, target, key, self); // 设置key为新元素 Reflect.set(target, key, newValue, receiver); // notify self.depNotify(target, key, keyBefore, dep, isChanged); return true; }, }); } addProxyTarget(value, target, key, self) { let newValue = value; let isChanged = false; if (isObject(value) && !value.__parent__) { self.obeserve(newValue); newValue = self.proxyTarget(newValue, key); newValue.__parent__ = target; isChanged = true; } return { newValue, isChanged, } } depNotify(target, key, keyBefore, dep, isChanged) { if (isChanged && target.__parent__) { target.__parent__.__dep__.notify(keyBefore); return; } if (Array.isArray(target)) { if (key === 'length' && target.__parent__) { target.__parent__.__dep__.notify(keyBefore); } } else { dep.notify(key); } } obeserve(target) { // 只处理对象类型,包括数组、对象 if (!isObject(target)) return; for (let key in target) { if (isObject(target[key]) && target[key] !== null) { this.obeserve(target[key]); target[key] = this.proxyTarget(target[key], key); // 设置__parent__,方便子元素调用 target[key].__parent__ = target; } } } }
在Observer中,针对对象,只需要执行 dep.depend(key)
、 dep.notify(key)
即可。添加 key 是为了能正确的触发收集,不知道怎么说明白为什么要这样做,只能一切尽在不言中了。
Array, 如何实现依赖的收集和触发那。依赖收集与Object类似, dep.depend(key)
完成数组的收集。关于触发,可以分为两个方面,一是改变数组长度、二未改变数组长度的。改变数组长度的,在set里,通过长度属性的设置触发父级元素的notify。为什么要使用父级元素的notify那?我们可以分析以下,在你设置数组的长度时,这时候的target\key\value分别是[]\length*, 这个时候,数组的依赖收集是没有的,你watcher的是数组,并不是数组本身。这个时候只能通过 target.__parent__.__dep__.notify(keyBefore)
触发父级的收集,完成数据变化的检测。二对于未改变数组长度的,这里的做法,虽然是直接 target.__parent__.__dep__.notify(keyBefore) 触发依赖,但是有个严重的问题,实际上更新的数据不是最新的,这个地方暂时还没想到比较好的方法,欢迎大家讨论。
Dep
Dep.js
let uid = 0; export default class Dep { constructor () { this.subs = {}; this.id = uid++; } addSub(prop, sub) { this.subs[prop] = this.subs[prop] || []; this.subs[prop].push(sub); } removeSub(prop, sub) { this.remove(this.subs[prop] || [], sub); } depend(prop) { if (Dep.target) { // 传入的是当前依赖 Dep.target.addDep(prop, this) } } notify(prop) { const subs = (this.subs[prop] || []).slice(); for (let i = 0, l = subs.length; i < l; i++) { subs[i].update(); } } remove(arr, item) { if (arr.length) { const index = arr.indexOf(item); if (index > -1) { return arr.splice(index, 1); } } } } Dep.target = null const targetStack = [] export function pushTarget (_target) { if (Dep.target) targetStack.push(Dep.target) Dep.target = _target } export function popTarget () { Dep.target = targetStack.pop() }
dep 添加prop实现类型的绑定,为什么要这么做那?使用proxy代理后,你假如wahcter对象下的几个元素,此时的deps将同时存在这几个元素,你触发依赖的时候,这些依赖都会执行。因此,通过key值绑定观察事件,触发时,能完成对象的正确触发。
watcher、utils
import { parsePath } from './utils'; import { pushTarget, popTarget } from './dep' export default class Watcher { constructor(vm, expOrFn, cb) { // dep id集合 this.depIds = new Set(); this.vm = vm; this.getter = parsePath(expOrFn); this.cb = cb; this.value = this.get(); } get () { pushTarget(this); let value = this.getter.call(this.vm, this.vm); popTarget(); return value; } update() { const oldValue = this.value; this.value = this.get(); this.cb.call(this.vm, this.value, oldValue); } addDep (prop, dep) { const id = dep.id; if (!this.depIds.has(id)) { this.depIds.add(id); dep.addSub(prop, this); } } }
utils.js
/** * 解析简单路径 */ const bailRE = /[^\w.$]/; export function parsePath (path) { if (bailRE.test(path)) { return; } const segments = path.split('.'); return function (obj) { for (let i = 0; i < segments.length; i++) { if (!obj) return; obj = obj[segments[i]]; } return obj; }; } /** * Define a property. */ export function def (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }) } /** * Quick object check - this is primarily used to tell * Objects from primitive values when we know the value * is a JSON-compliant type. */ export function isObject (obj) { return obj !== null && typeof obj === 'object' } /** * Check whether an object has the property. */ const hasOwnProperty = Object.prototype.hasOwnProperty export function hasOwn (obj, key) { return hasOwnProperty.call(obj, key) }
Utils.js/Watchers.js与Vue 2.x类似,这里就不多介绍了。
测试一下
test.js
import Observer from './observer'; import Watcher from './watcher'; let data = { name: 'lijincai', password: '***********', address: { home: '安徽亳州谯城区', }, list: [{ name: 'lijincai', password: 'you know it Object', }], }; const newData = new Observer(data); let index = 0; const watcherName = new Watcher(newData, 'value.name', (newValue, oldValue) => { console.log(`${index++}: name newValue:`, newValue, ', oldValue:', oldValue); }); const watcherPassword = new Watcher(newData, 'value.password', (newValue, oldValue) => { console.log(`${index++}: password newValue:`, newValue, ', oldValue:', oldValue); }); const watcherAddress = new Watcher(newData, 'value.address', (newValue, oldValue) => { console.log(`${index++}: address newValue:`, newValue, ', oldValue:', oldValue); }); const watcherAddressHome = new Watcher(newData, 'value.address.home', (newValue, oldValue) => { console.log(`${index++}: address.home newValue:`, newValue, ', oldValue:', oldValue); }); const watcherAddProp = new Watcher(newData, 'value.addProp', (newValue, oldValue) => { console.log(`${index++}: addProp newValue:`, newValue, ', oldValue:', oldValue); }); const watcherDataObject = new Watcher(newData, 'value.list', (newValue, oldValue) => { console.log(`${index++}: newValue:`, newValue, ', oldValue:', oldValue); }); newData.value.name = 'resetName'; newData.value.password = 'resetPassword'; newData.value.name = 'hello world name'; newData.value.password = 'hello world password'; newData.value.address.home = 'hello home'; newData.value.address.home = 'hello home2'; newData.value.addProp = 'hello addProp'; newData.value.addProp ={ name: 'ceshi', }; newData.value.addProp.name = 'ceshi2'; newData.value.list.push('1'); newData.value.list.splice(0, 1); newData.value.list.sort(); newData.value.list.reverse(); newData.value.list.push('1'); newData.value.list.unshift({name: 'nihao'}); newData.value.list[0] = { name: 'lijincai', password: 'you know it Array', }; newData.value.list[0].name = 'you know it array after'; newData.value.list.pop(); newData.value.list.shift(); newData.value.list.length = 1;
我们使用对象、数组测试一下我们的ES6 Proxy检测。
20:17:44.725 index.js"ceshi", __dep__: Dep, __parent__: {…}} , oldValue: hello addProp 20:17:44.727 index.js"1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", __dep__: Dep, __parent__: {…}} 20:17:44.728 index.js"1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} 20:17:44.729 index.js"1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} 20:17:44.731 index.js"1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} 20:17:44.734 index.js"1", 1: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", 1: "1", __dep__: Dep, __parent__: {…}} 20:17:44.735 index.js"1", 2: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}} 20:17:44.735 index.js"1", 2: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}} 20:17:44.736 index.js"1", 2: "1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", 2: "1", __dep__: Dep, __parent__: {…}} 20:17:44.737 index.js"1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: Proxy, 1: "1", __dep__: Dep, __parent__: {…}} 20:17:44.738 index.js"1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}} 20:17:44.738 index.js"1", __dep__: Dep, __parent__: {…}} , oldValue: Proxy {0: "1", __dep__: Dep, __parent__: {…}}
我们看到了ES6 Proxy后实现了Object/Array的检测,虽然还存在一些问题,但是基本的侦测变化的功能都已经具备了。
总结
以上所述是小编给大家介绍的ES6 Proxy实现Vue的变化检测问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 【雨果唱片】中国管弦乐《鹿回头》WAV
- APM亚流新世代《一起冒险》[FLAC/分轨][106.77MB]
- 崔健《飞狗》律冻文化[WAV+CUE][1.1G]
- 罗志祥《舞状元 (Explicit)》[320K/MP3][66.77MB]
- 尤雅.1997-幽雅精粹2CD【南方】【WAV+CUE】
- 张惠妹.2007-STAR(引进版)【EMI百代】【WAV+CUE】
- 群星.2008-LOVE情歌集VOL.8【正东】【WAV+CUE】
- 罗志祥《舞状元 (Explicit)》[FLAC/分轨][360.76MB]
- Tank《我不伟大,至少我能改变我。》[320K/MP3][160.41MB]
- Tank《我不伟大,至少我能改变我。》[FLAC/分轨][236.89MB]
- CD圣经推荐-夏韶声《谙2》SACD-ISO
- 钟镇涛-《百分百钟镇涛》首批限量版SACD-ISO
- 群星《继续微笑致敬许冠杰》[低速原抓WAV+CUE]
- 潘秀琼.2003-国语难忘金曲珍藏集【皇星全音】【WAV+CUE】
- 林东松.1997-2039玫瑰事件【宝丽金】【WAV+CUE】