深入詳解Vue3 ref底層實(shí)現(xiàn)原理
前言
隨著現(xiàn)在vue3越來(lái)越普及,相應(yīng)的面試題也多了起來(lái)
說(shuō)到vue3的面試題,有一個(gè)最經(jīng)典的就是ref和reactive的區(qū)別,用法上的區(qū)別很明顯,大家都理解,對(duì)于實(shí)現(xiàn)這兩個(gè)方法的底層原理網(wǎng)上卻眾說(shuō)紛壇,各有說(shuō)法。
其中vue3的reactive的是用Proxy實(shí)現(xiàn)的這一點(diǎn)是明確的
這里講的就是這個(gè)ref,有說(shuō)是使用Object.defineProperty實(shí)現(xiàn)的,有說(shuō)是使用Proxy實(shí)現(xiàn)的,說(shuō)法不一,到底哪些是正確的呢
下面說(shuō)一下我自己的理解,如有誤請(qǐng)?jiān)谠u(píng)論區(qū)指出!謝謝
源碼解析
既然各方說(shuō)法不一,那首先想到的就是直接去看vue3官方源碼不就好了,看別人不如自己實(shí)際動(dòng)起來(lái)
通過(guò)node_modules依賴文件找到vue中實(shí)現(xiàn)ref的源碼
源碼如下:
為方便理解,把相關(guān)涉及到的源碼以及代碼含義加上注釋
function ref(value) { // ref方法 return createRef(value, false); } function shallowRef(value) { // 淺層ref return createRef(value, true); } function createRef(rawValue, shallow) { // 創(chuàng)建ref if (isRef(rawValue)) { // 判斷是否為ref return rawValue; } return new RefImpl(rawValue, shallow); // 返回RefImpl實(shí)例對(duì)象 } class RefImpl { constructor(value, __v_isShallow) { // 值,是否淺層ref this.__v_isShallow = __v_isShallow; this.dep = undefined; this.__v_isRef = true; this._rawValue = __v_isShallow ? value : toRaw(value); this._value = __v_isShallow ? value : toReactive(value); // 判斷是否為淺層ref,否則調(diào)用toReactive,方法在下面 } get value() { // getter方法 獲取value值 trackRefValue(this); return this._value; } set value(newVal) { // setter方法 設(shè)置value值 const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal); newVal = useDirectValue ? newVal : toRaw(newVal); if (hasChanged(newVal, this._rawValue)) { this._rawValue = newVal; this._value = useDirectValue ? newVal : toReactive(newVal); // 在value值更新時(shí)進(jìn)行判斷是否為淺層ref,否則調(diào)用toReactive triggerRefValue(this, newVal); } } } const toReactive = (value) => isObject(value) ? reactive(value) : value; // 是否為對(duì)象,如果是則調(diào)用reactive
經(jīng)過(guò)一番閱讀理解,我們只需抓取關(guān)鍵信息即可
可以看到通過(guò) ref(1)
的值在 RefImpl
類(lèi)中為 _value
,然后使用class中的get
和set
語(yǔ)法糖對(duì)該value值進(jìn)行相應(yīng)的操作,獲取和賦值
再判斷為對(duì)象時(shí)才使用reactive()
方法
實(shí)踐操作
從源碼上看,我們看到了,使用了class的get和set來(lái)對(duì)這個(gè)value值進(jìn)行操作,那么我們自己動(dòng)手實(shí)踐一下,看看怎么實(shí)現(xiàn)
這里把源碼的_value
的口頭約定私有屬性形式改為es9新增加的#value
形式
class RefImpl { #value = '' // #value 私有屬性 constructor(value) { this.#value = value } get value() { console.log('觸發(fā)獲取', this.#value) return this.#value } set value(newVal) { console.log('觸發(fā)更新', newVal) this.#value = newVal } } function ref(value) { return new RefImpl(value) } const test = ref('我是小濤測(cè)試') setTimeout(() => { test.value = '我設(shè)置了值' }, 2000)
可以看到,這個(gè)簡(jiǎn)單的實(shí)例,也可以劫持?jǐn)?shù)據(jù)的更新方便我們進(jìn)行其他操作
class類(lèi)的get和set是什么
到了這里,可以確定ref是使用class里的get/set進(jìn)行數(shù)據(jù)劫持和更新的
而這個(gè)get/set實(shí)際是語(yǔ)法糖,本質(zhì)是js的特性,是劫持property(屬性)的一種方式
對(duì)象內(nèi)分為數(shù)據(jù)屬性
和訪問(wèn)器屬性
,訪問(wèn)器屬性不包含數(shù)據(jù),是一對(duì)get和set方法
Getter 屬性訪問(wèn)器(accessor)和 Setter 屬性修改器(mutator)
結(jié)論
綜上所述
所以一剛開(kāi)始說(shuō)的使用Object.defineProperty
說(shuō)法并不正確,因?yàn)?code>Object.defineProperty()可以用來(lái)給修改對(duì)象屬性,然后使用到了getter/setter
所以使用了class
的說(shuō)法也并不正確,也是在對(duì)象內(nèi)使用訪問(wèn)器屬性,使用到了getter/setter這兩個(gè)方法
Object.defineProperty() 方法會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改一個(gè)對(duì)象的現(xiàn)有屬性,并返回此對(duì)象。
Vue 將遍歷此對(duì)象所有的屬性,并使用Object.defineProperty把這些屬性全部轉(zhuǎn)為 getter/setter。
附上一張官方說(shuō)法 官方鏈接
在對(duì)象內(nèi)也可以使用訪問(wèn)器屬性
const test = { _value: '', get value() { console.log('觸發(fā)獲取', this._value) return this._value }, set value(newVal) { console.log('觸發(fā)更新', newVal) this._value = newVal } }
最后,其實(shí)簡(jiǎn)化到最后發(fā)現(xiàn),都不會(huì)難以理解,所以保持探索態(tài)度,多看多學(xué),方是正途
到此這篇關(guān)于深入詳解Vue3 ref底層實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Vue3 ref內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue實(shí)現(xiàn) 點(diǎn)擊顯示再點(diǎn)擊隱藏效果(點(diǎn)擊頁(yè)面空白區(qū)域也隱藏效果)
這篇文章主要介紹了Vue實(shí)現(xiàn) 點(diǎn)擊顯示 再點(diǎn)擊隱藏 點(diǎn)擊頁(yè)面空白區(qū)域也隱藏效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01element-ui圖像組件、上傳組件以及分頁(yè)組件實(shí)現(xiàn)代碼
工作中碰到需要多圖上傳,在使用element-ui解決過(guò)程中碰到一些問(wèn)題,在這里分享給大家,這篇文章主要給大家介紹了關(guān)于element-ui圖像組件、上傳組件以及分頁(yè)組件實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-02-02vue中tab選項(xiàng)卡的實(shí)現(xiàn)思路
今天給大家分享vue中tab 選項(xiàng)卡的一些套路,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2018-11-11vue-cli3 項(xiàng)目?jī)?yōu)化之通過(guò) node 自動(dòng)生成組件模板 generate View、Component
這篇文章主要介紹了vue-cli3 項(xiàng)目?jī)?yōu)化之通過(guò) node 自動(dòng)生成組件模板 generate View、Component的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04對(duì)類(lèi)Vue的MVVM前端庫(kù)的實(shí)現(xiàn)代碼
這篇文章主要介紹了對(duì)類(lèi)Vue的MVVM前端庫(kù)的實(shí)現(xiàn)代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09vue 使用 sortable 實(shí)現(xiàn) el-table 拖拽排序功能
這篇文章主要介紹了vue 使用 sortable 實(shí)現(xiàn) el-table 拖拽排序功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12