手寫組合api reactive和ref

大家都知道在vue3中響應(yīng)式原理有原先的object.defineProperty換成了es6的新特性Proxy桑驱。
那Proxy到底是什么呢,他和object.defineProperty又有什么區(qū)別脉顿,他的出現(xiàn)解決了vue2的哪些痛點?
首先來回答Proxy到底是什么:
可以簡單的理解為一個攔截器点寥,他可以對任何對象的絕大部分行為和操作進行干涉艾疟,也就是我們常說的攔截。一提到攔截是不是就有感覺了敢辩。有了攔截不就可以實現(xiàn)響應(yīng)式了嘛蔽莱!也的確如此,對new Proxy返回的代理對象進行大部分操作如增刪改讀都可以被攔截到戚长,我們就可以在其中做一些其他的事情盗冷。Proxy當然不止這些功能,他提供了多達13種行為的攔截同廉,具體的可以自行查閱文檔


image.png

本文只介紹get set 以及deleteProperty仪糖。

回到第二個問題以及第三個問題,他和object.defineProperty的區(qū)別在哪兒迫肖,我們都知道object.defineProperty只能攔截到對象自身以有的屬性锅劝,而后續(xù)新增的屬性是無法攔截到的,所以vue2提供了$set等一系列的api來幫助完善響應(yīng)式蟆湖。并且object.defineProperty并沒有辦法攔截到數(shù)組的一些變更故爵,而vue2則采用了重寫js種能修改源數(shù)據(jù)的數(shù)組api來實現(xiàn)數(shù)組的響應(yīng)式,這些問題在proxy中都不存在了隅津,這就是二者區(qū)別以及他的出現(xiàn)解決了vue2的哪些問題

那接下來就一起來看看他是怎么使用的吧稠集。
語法如下

const proxyObj = new Proxy(target,{
get(target,key){
},
set(target,key,val){
},
deleteProperty(target,key){
}
...
})

其中target就是需要被代理的對象,而當我們修改讀取新增或刪除代理對象的某些值的時候就會觸發(fā)一系列相關(guān)的函數(shù)饥瓷,也就是我們在new Proxy的時候傳入的第二個對象中的函數(shù)。

這里會有一個疑問痹籍,修改proxyObj的時候會不會影響到原來的對象呢铆,以及直接修改原來的對象,proxyObj會被修改嗎蹲缠。
經(jīng)過實驗我發(fā)現(xiàn)他們兩個對象是互通的棺克,也就是你改了我也會變我改了你也會變,只不過如果直接修改原對象則不會觸發(fā)攔截线定。
至于是為什么娜谊,我的理解是在所有的攔截操作中,他都接收了target也就是原對象作為參數(shù)斤讥,那實際的操作也是在target上進行的纱皆,所以可以理解為target和proxyObj其實就是同一個對象,只不過proxyObj攔截你的各種操作,然后分發(fā)到原對象身上去而已派草。不知道理解的是不是有誤搀缠。。

接下來我們就來手動實現(xiàn)一下vue3中的王牌響應(yīng)式api reactive和ref
要實現(xiàn)他們倆就得先知道他們倆是干什么的近迁,其實兩個api都是包裝數(shù)據(jù)用的艺普,使數(shù)據(jù)變?yōu)轫憫?yīng)式,唯一的不同點就是reactive用于包裝對象和數(shù)組ref則可以用于包裝各種類型鉴竭。經(jīng)過ref包裝后的值需要通過.value的方式獲取歧譬。其實ref在包裝對象和數(shù)組類型的時候也是調(diào)用的reactive,具體的步驟看后續(xù)實現(xiàn)就行搏存。

現(xiàn)在實現(xiàn)一個reactive

const reactiveHandler = {
    get(target,key){
        console.log('攔截到了讀取數(shù)據(jù)',key)
        return Reflect.get(target,key)
    },
    set(target,key,val){
        console.log('攔截到了更新或新增數(shù)據(jù)',key)
        return Reflect.set(target,key,val)
    },
    deleteProperty(target,key){
        console.log('攔截到了刪除數(shù)據(jù)',key)
        return Reflect.deleteProperty(target,key)
    }
}
function reactive(target) {
    if(target&&typeof target==='object'){
        if(Array.isArray(target)){
            target.forEach((d,index)=>{
                target[index] = reactive(d)
            })
        }else{
            Object.keys(target).forEach(d=>{
                target[d] = reactive(target[d])
            })
        }
        return new Proxy(target,reactiveHandler)
    }
    return target
}

可以看到還是比較簡單的瑰步,這里只實現(xiàn)了一下攔截,并沒有實現(xiàn)更新視圖祭埂。因為Proxy只能攔截到目標對象自身屬性的變化面氓,但是如果自身某個屬性也是引用類型的話,他就攔截不到了蛆橡。
比如當我們操作obj.user的時候舌界,user是obj的屬性,這個時候Proxy是完全可以攔截到任意行為的泰演,但是如果user也是一個對象呻拌,我們要去操作obj.user.name的時候。此時Proxy就只能攔截到obj.user睦焕,并不能攔截到name屬性的任意行為了藐握。所以上文中reactive函數(shù)做了一個遞歸處理,將所有的對象和數(shù)組都轉(zhuǎn)為代理形式垃喊。
vue3中還有個與reactive對應(yīng)的api叫shallowReactive猾普,他就是淺層的響應(yīng)式,也就是只作用于目標對象自身屬性本谜,再往深了去就不進行代理了初家,他的實現(xiàn)就更簡單了

function shallowReactive(target) {
    if(target&&typeof target==='object'){
        return new Proxy(target,reactiveHandler)
    }
    return target
}

接下來去實現(xiàn)ref。
我們都知道proxy是用來代理對象的乌助,但是js中可不止有對象和數(shù)組這兩個數(shù)據(jù)類型溜在。那當我們需要對基本數(shù)據(jù)類型進行響應(yīng)式的時候該怎么辦呢。這個時候ref就出現(xiàn)了他托。他其實就是利用了對象的get props 和set props函數(shù)掖肋。

{
        _num:0,
        get num(){
            console.log('劫持到了get')
            return this._num
        },
        set num(val){
            console.log('劫持到了set',val)
            this._num= val
        }
    }

是不是很驚訝,對象中還有這種用法赏参,get 和set可以指定一個屬性名志笼,當我們在訪問這個屬性名的時候就會觸發(fā)get和set方法沿盅。就可以實現(xiàn)攔截的效果拉。現(xiàn)在是不是明白了為什么ref包裹的數(shù)據(jù)要.value的形式去訪問了籽腕?

function ref(target) {
    return{
        _value:reactive(target),
        get value(){
            console.log('劫持到了get')
            return this._value
        },
        set value(val){
            console.log('劫持到了set',val)
            this._value = val
        }
    }
}

其實ref 的內(nèi)部就是這樣的拉嗡呼。我們在訪問value屬性的時候,所有的操作都會分發(fā)到_value身上去皇耗。那如果這個時候傳入的是對象或者數(shù)組的話怎么辦呢南窗,很簡單。用之前寫好的reactive函數(shù)包裹一次就行拉郎楼。
因為對象中的get和set同樣是只能監(jiān)聽到你指定的那個屬性的變化的万伤,如果你指定的那個屬性是對象的話,那么深層的變化的是監(jiān)聽不到的呜袁。所以需要借助reactive來完善敌买。
如果你不想攔截那么深層次的話,vue3也提供了一個shallowRef阶界,他就只攔截value的變化了虹钮,value再往深了去就不會被監(jiān)聽到了。

function shallowRef(target) {
    return{
        _value:target,
        get value(){
            console.log('劫持到了get')
            return this._value
        },
        set value(val){
            console.log('劫持到了set',val)
            this._value = val
        }
    }
}

以上就是本文全部內(nèi)容膘融。僅供參考學習

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芙粱,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子氧映,更是在濱河造成了極大的恐慌春畔,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岛都,死亡現(xiàn)場離奇詭異律姨,居然都是意外死亡,警方通過查閱死者的電腦和手機臼疫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門择份,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人烫堤,你說我怎么就攤上這事缓淹。” “怎么了塔逃?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長料仗。 經(jīng)常有香客問我湾盗,道長,這世上最難降的妖魔是什么立轧? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任格粪,我火速辦了婚禮躏吊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘帐萎。我一直安慰自己比伏,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布疆导。 她就那樣靜靜地躺著赁项,像睡著了一般。 火紅的嫁衣襯著肌膚如雪澈段。 梳的紋絲不亂的頭發(fā)上悠菜,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音败富,去河邊找鬼悔醋。 笑死,一個胖子當著我的面吹牛兽叮,可吹牛的內(nèi)容都是我干的芬骄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼鹦聪,長吁一口氣:“原來是場噩夢啊……” “哼账阻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起椎麦,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤宰僧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后观挎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琴儿,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年嘁捷,在試婚紗的時候發(fā)現(xiàn)自己被綠了造成。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡雄嚣,死狀恐怖晒屎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缓升,我是刑警寧澤鼓鲁,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站港谊,受9級特大地震影響骇吭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歧寺,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一燥狰、第九天 我趴在偏房一處隱蔽的房頂上張望棘脐。 院中可真熱鬧,春花似錦龙致、人聲如沸蛀缝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屈梁。三九已至,卻和暖如春像啼,著一層夾襖步出監(jiān)牢的瞬間俘闯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工忽冻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留真朗,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓僧诚,卻偏偏與公主長得像遮婶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子湖笨,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容