您好,登錄后才能下訂單哦!
前言
很多初使用Vue的同學會發現,在改變數組的值的時候,值確實是改變了,但是視圖卻無動于衷,果然是因為數組太高冷了嗎?
查看官方文檔才發現,不是女神太高冷,而是你沒用對方法。
看來想讓女神自己動,關鍵得用對方法。雖然在官方文檔中已經給出了方法,但是在下實在好奇的緊,想要解鎖更多姿勢的話,那就必須先要深入女神的心,于是乎才有了去探索Vue響應式原理的想法。(如果你愿意一層一層地剝開我的心。你會發現,你會訝異…… 沉迷于鬼哭狼嚎 無法自拔QAQ)。
前排提示,Vue的響應式原理主要是使用了ES5的Object.defineProperty,毫不知情的同學可以查看相關資料。
為啥數組不響應?
仔細一想,Vue的響應是基于Object.definePropery的,這個方法主要是對對象屬性的描述進行修改。數組其實也是對象,通過定義數組的屬性應該也能產生響應的效果呀。先驗證一下自己的想法,擼起袖子就開干。
const arr = [1,2,3]; let val = arr[0]; Object.defineProperty(arr,'0',{ enumerable: true, configurable: true, get(){ doSomething(); return val; }, set(a){ val = a; doSomething(); } }); function doSomething() { }
然后在控制臺中分別輸入arr、arr[0] = 2、arr,可以看到如下圖的結果。
咦,一切居然都如預想猜想的一樣。
接下來,看到這段代碼,有的同學可能會有所疑問,為啥在get()方法里不直接返回this[0]呢?而是要借助val來返回值呢?仔細一想,臥槽!!!差點特么的死循環了,你想呀,get()本身就是獲取當前屬性的值,在get()里調用this[0]不是等同于再次調用了get()方法嗎? 好可怕好可怕,簡直嚇死勞資了。
雖然你想象中的女神可能會這種姿勢,但是你眼前的這個女神確實不是這種姿勢的,像我這種屌絲屬性暴露無疑的人怎么可能猜透女神的心思?為什么不這樣響應數據呢?或許是因為數組和對象還是有所差別,定義數組的屬性可能會產生一些麻煩與Bug。又或許是因為在交互的過程中可能會產生大量的數據,導致整體的性能下降。也有可能是作者權衡利弊之后用其他方法也可以達到數據響應的效果。反正我是猜不透啦。
為啥調用數組原生方法就可以響應了?
為什么使用了這些數組的方法就就能讓數據響應了呢?先看看數組部分的源碼吧。
簡單的來講,def的作用就是重新定義對象屬性的value值。
//array.js import { def } from '../util/index' const arrayProto = Array.prototype export const arrayMethods = Object.create(arrayProto) //arrayMethods是對數組的原型對象的拷貝, //在之后會將該對象里的特定方法進行變異后替換正常的數組原型對象 /** * Intercept mutating methods and emit events */ [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ] .forEach(function (method) { // cache original method //將上面的方法保存到original中 const original = arrayProto[method] def(arrayMethods, method, function mutator (...args) { const result = original.apply(this, args) const ob = this.__ob__ let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify() return result }) })
貼出def部分的代碼
/** * Define a property. */ export function def (obj: Object, key: string, val: any, enumerable?: boolean) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }) }
array.js是對數組的一些方法進行變異,我們以push方法來舉個例子。首先 就是要用original = arrayProto['push']來保存原生的push方法。
然后就是要定義變異的方法了,對于def函數,如果不深究的話,def(arrayMethods,method,function(){}),這個函數可以粗略的表示為arrayMethods[method] = function mutator(){};
假設在之后調用push方法,實際上調用的是mutator方法,在mutator方法中,第一件事就是調用保存了原生push方法的original,先求出實際的值。一堆文字看起來實在很抽象,那么寫一段低配版的代碼來表達源碼的含義。
const push = Array.prototype.push; Array.prototype.push = function mutator (...arg){ const result = push.apply(this,arg); doSomething(); return result } function doSomething(){ console.log('do something'); } const arr = []; arr.push(1); arr.push(2); arr.push(3);
在控制臺中查看結果為:。
那么源碼中的
const ob = this.__ob__ let inserted switch (method) { case 'push': case 'unshift': inserted = args break case 'splice': inserted = args.slice(2) break } if (inserted) ob.observeArray(inserted) // notify change ob.dep.notify()
這段代碼就是對應的doSomething()了
在該代碼中,清清楚楚的寫了2個單詞的注釋notify change,不認識這2個單詞的同學就百度一下嘛,這里就由我代勞了,這倆單詞的意思是發布改變!每次調用了該方法,都會求出值,然后做一些其他的事情,比如發布改變與觀察新增的元素,響應的其他過程在本篇就不討論了。
[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]
目前一共有這么些方法,只要用對方法就能改變女神的姿勢喲!
小結
對于標題,我一改再改,一開始叫淺析Vue響應原理,但是后來一看 這個標題實在太大,那就從最簡單的入手吧,先從數組入手,而且本篇也不會花費太多時間去閱讀。如果本篇有什么地方寫得有誤,誤導了他人,請一定指出,萬分感激。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。