淺談Vue響應(yīng)式(數(shù)組變異方法)
前言
很多初使用Vue的同學(xué)會(huì)發(fā)現(xiàn),在改變數(shù)組的值的時(shí)候,值確實(shí)是改變了,但是視圖卻無動(dòng)于衷,果然是因?yàn)閿?shù)組太高冷了嗎?
查看官方文檔才發(fā)現(xiàn),不是女神太高冷,而是你沒用對(duì)方法。
看來想讓女神自己動(dòng),關(guān)鍵得用對(duì)方法。雖然在官方文檔中已經(jīng)給出了方法,但是在下實(shí)在好奇的緊,想要解鎖更多姿勢(shì)的話,那就必須先要深入女神的心,于是乎才有了去探索Vue響應(yīng)式原理的想法。(如果你愿意一層一層地剝開我的心。你會(huì)發(fā)現(xiàn),你會(huì)訝異…… 沉迷于鬼哭狼嚎 無法自拔QAQ)。
前排提示,Vue的響應(yīng)式原理主要是使用了ES5的Object.defineProperty,毫不知情的同學(xué)可以查看相關(guān)資料。
為啥數(shù)組不響應(yīng)?
仔細(xì)一想,Vue的響應(yīng)是基于Object.definePropery的,這個(gè)方法主要是對(duì)對(duì)象屬性的描述進(jìn)行修改。數(shù)組其實(shí)也是對(duì)象,通過定義數(shù)組的屬性應(yīng)該也能產(chǎn)生響應(yīng)的效果呀。先驗(yàn)證一下自己的想法,擼起袖子就開干。
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() { }
然后在控制臺(tái)中分別輸入arr、arr[0] = 2、arr,可以看到如下圖的結(jié)果。
咦,一切居然都如預(yù)想猜想的一樣。
接下來,看到這段代碼,有的同學(xué)可能會(huì)有所疑問,為啥在get()方法里不直接返回this[0]呢?而是要借助val來返回值呢?仔細(xì)一想,臥槽!??!差點(diǎn)特么的死循環(huán)了,你想呀,get()本身就是獲取當(dāng)前屬性的值,在get()里調(diào)用this[0]不是等同于再次調(diào)用了get()方法嗎? 好可怕好可怕,簡(jiǎn)直嚇?biāo)绖谫Y了。
雖然你想象中的女神可能會(huì)這種姿勢(shì),但是你眼前的這個(gè)女神確實(shí)不是這種姿勢(shì)的,像我這種屌絲屬性暴露無疑的人怎么可能猜透女神的心思?為什么不這樣響應(yīng)數(shù)據(jù)呢?或許是因?yàn)閿?shù)組和對(duì)象還是有所差別,定義數(shù)組的屬性可能會(huì)產(chǎn)生一些麻煩與Bug。又或許是因?yàn)樵诮换サ倪^程中可能會(huì)產(chǎn)生大量的數(shù)據(jù),導(dǎo)致整體的性能下降。也有可能是作者權(quán)衡利弊之后用其他方法也可以達(dá)到數(shù)據(jù)響應(yīng)的效果。反正我是猜不透啦。
為啥調(diào)用數(shù)組原生方法就可以響應(yīng)了?
為什么使用了這些數(shù)組的方法就就能讓數(shù)據(jù)響應(yīng)了呢?先看看數(shù)組部分的源碼吧。
簡(jiǎn)單的來講,def的作用就是重新定義對(duì)象屬性的value值。
//array.js import { def } from '../util/index' const arrayProto = Array.prototype export const arrayMethods = Object.create(arrayProto) //arrayMethods是對(duì)數(shù)組的原型對(duì)象的拷貝, //在之后會(huì)將該對(duì)象里的特定方法進(jìn)行變異后替換正常的數(shù)組原型對(duì)象 /** * 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是對(duì)數(shù)組的一些方法進(jìn)行變異,我們以push方法來舉個(gè)例子。首先 就是要用original = arrayProto['push']來保存原生的push方法。
然后就是要定義變異的方法了,對(duì)于def函數(shù),如果不深究的話,def(arrayMethods,method,function(){}),這個(gè)函數(shù)可以粗略的表示為arrayMethods[method] = function mutator(){};
假設(shè)在之后調(diào)用push方法,實(shí)際上調(diào)用的是mutator方法,在mutator方法中,第一件事就是調(diào)用保存了原生push方法的original,先求出實(shí)際的值。一堆文字看起來實(shí)在很抽象,那么寫一段低配版的代碼來表達(dá)源碼的含義。
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);
在控制臺(tái)中查看結(jié)果為:。
那么源碼中的
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()
這段代碼就是對(duì)應(yīng)的doSomething()了
在該代碼中,清清楚楚的寫了2個(gè)單詞的注釋notify change,不認(rèn)識(shí)這2個(gè)單詞的同學(xué)就百度一下嘛,這里就由我代勞了,這倆單詞的意思是發(fā)布改變!每次調(diào)用了該方法,都會(huì)求出值,然后做一些其他的事情,比如發(fā)布改變與觀察新增的元素,響應(yīng)的其他過程在本篇就不討論了。
[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]
目前一共有這么些方法,只要用對(duì)方法就能改變女神的姿勢(shì)喲!
小結(jié)
對(duì)于標(biāo)題,我一改再改,一開始叫淺析Vue響應(yīng)原理,但是后來一看 這個(gè)標(biāo)題實(shí)在太大,那就從最簡(jiǎn)單的入手吧,先從數(shù)組入手,而且本篇也不會(huì)花費(fèi)太多時(shí)間去閱讀。如果本篇有什么地方寫得有誤,誤導(dǎo)了他人,請(qǐng)一定指出,萬分感激。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue/cli?配置動(dòng)態(tài)代理無需重啟服務(wù)的操作方法
vue-cli是vue.js的腳手架,用于自動(dòng)生成vue.js+webpack的項(xiàng)目模板,分為vue?init?webpack-simple?項(xiàng)目名和vue?init?webpack?項(xiàng)目名兩種,這篇文章主要介紹了vue/cli?配置動(dòng)態(tài)代理,無需重啟服務(wù),需要的朋友可以參考下2022-05-05Vue路由this.route.push跳轉(zhuǎn)頁(yè)面不刷新的解決方案
這篇文章主要介紹了Vue路由this.route.push跳轉(zhuǎn)頁(yè)面不刷新的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07vue項(xiàng)目中頁(yè)面跳轉(zhuǎn)傳參的方法總結(jié)
在Vue項(xiàng)目中,你可以使用路由(vue-router)來實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)并傳遞參數(shù),這篇文章主要為大家整理了一些常用的方法,感興趣的小伙伴可以學(xué)習(xí)一下2023-11-11vue-cli 默認(rèn)路由再子路由選中下的選中狀態(tài)問題及解決代碼
這篇文章主要介紹了vue-cli 默認(rèn)路由再子路由選中下的選中狀態(tài)問題及解決代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09vite+vue3中使用mock模擬數(shù)據(jù)問題
這篇文章主要介紹了vite+vue3中使用mock模擬數(shù)據(jù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03vue form表單post請(qǐng)求結(jié)合Servlet實(shí)現(xiàn)文件上傳功能
這篇文章主要介紹了vue form表單post請(qǐng)求結(jié)合Servlet實(shí)現(xiàn)文件上傳功能,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01