vue3中reactive和ref的實現(xiàn)與區(qū)別詳解
前言
reactive和ref都是vue3實現(xiàn)響應(yīng)式系統(tǒng)的api,他們是如何實現(xiàn)響應(yīng)式的呢?reactive和ref又有什么區(qū)別呢,看完這篇文章,相信答案就呼之欲出了。
effect
vue3中的reactive實現(xiàn)了數(shù)據(jù)的響應(yīng)式,說到數(shù)據(jù)響應(yīng)式離不開effect,effect是關(guān)鍵,effect()方法是暴露給創(chuàng)作者的一種方法,參數(shù)1是一個回調(diào)函數(shù),寫在回調(diào)函數(shù)的代碼可以用來模擬setup執(zhí)行函數(shù)的響應(yīng)式數(shù)據(jù)被寫在template模板的數(shù)據(jù),當(dāng)數(shù)據(jù)更新之后,將effect中的回調(diào)函數(shù)再次調(diào)用,達(dá)到數(shù)據(jù)更新便更新視圖的目的。也可以把effect叫做數(shù)據(jù)相關(guān)依賴,即記錄用到響應(yīng)式數(shù)據(jù)的代碼。參數(shù)二是一個對象,里面有l(wèi)azy屬性用來規(guī)定effect的回調(diào)函數(shù)第一次是否執(zhí)行。
<body> <div id="app">hello</div> <script> let data={ name:'草原一匹狼' } effect(()=>{ let app = document.getElementById('app') app.innerHTML=data.name },{lazy:true}) setTimeout(()=>{ data.name='切' },1000) </script> </body>
實現(xiàn)effect方法,保存回調(diào)函數(shù)當(dāng)數(shù)據(jù)更新再次調(diào)用回調(diào)函數(shù)達(dá)到更新視圖
export function effect(fn, options: any = {}) {//更新視圖也就是吧視圖用到數(shù)據(jù)在執(zhí)行一遍 const effect = createReactEffect(fn, options); if (!options.lazy) {//如果lazy:false執(zhí)行 effect(); } return effect; } let uid = 0; //每次調(diào)用都會創(chuàng)建一個effect let activeeffect;//設(shè)置當(dāng)前effect為全局變量好進(jìn)行收集 let effectStack = [];//保存effect 因為可能會有多個effect function createReactEffect(fn, options) { const effect = function reactiveEffect() { if (!effectStack.includes(effect)) { try { effectStack.push(effect); activeeffect = effect; fn(); } finally { effectStack.pop(); activeeffect = effectStack[effectStack.length - 1]; } } }; effect.id = uid++; //區(qū)別effect effect._isEffect = true; //區(qū)別effect 是不是響應(yīng)式的effect effect.raw = fn; //保存回調(diào)函數(shù)到自身 effect.options = options; //保存用戶屬性lazy return effect; }
reactive
了解完effect,接下來是實現(xiàn)reactive的具體過程:
- 響應(yīng)式數(shù)據(jù)用proxy代理時get收集依賴
- 響應(yīng)式數(shù)據(jù)用proxy代理時set執(zhí)行依賴
1.響應(yīng)式數(shù)據(jù)用proxy代理時get收集依賴
function createGetter(isReadonly = false, shall = false) { return function get(target, key, receiver) { const res = Reflect.get(target, key, receiver); //taget[key] if (!isReadonly) { //判斷是不是只讀 收集依賴 Track(target, TackOpType.GET, key);//Track收集依賴 一個響應(yīng)式屬性key對應(yīng)一個effect保存起來 } if (shall) { //只代理一層 return res; } if (isObject(res)) { return isReadonly ? readonly(res) : reactive(res); } return res; }; } //收集effect 獲取數(shù)據(jù)觸發(fā)get 收集依賴數(shù)據(jù)變化更新視圖 key和effect11對應(yīng) let targetMap = new WeakMap(); //棧結(jié)構(gòu)(target ,new Map(key(key effect一一對應(yīng)),new Set(activeeffect))) export function Track(target, type, key) { //對應(yīng)的key if (activeeffect == undefined) { return; } let depMAp = targetMap.get(target); if (!depMAp) { targetMap.set(target, (depMAp = new Map())); } let dep = depMAp.get(key); if (!dep) { depMAp.set(key, (dep = new Set())); } if (!dep.has(activeeffect)) { dep.add(activeeffect); } }
2.響應(yīng)式數(shù)據(jù)用proxy代理時set執(zhí)行依賴
function createSetter(shall = false) { return function set(target, key, value, receive) { const oldValue = target[key]; let hasKey = Array.isArray(oldValue) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key); const result = Reflect.set(target, key, value, receive); //獲取最新的值 對象數(shù)據(jù)已經(jīng)更新 if (!hasKey) { // 新增 trigger(target, TriggerOpTypes.ADD, key, value); } else { // 修改數(shù)組 if (hasChange(value, oldValue)) { trigger(target, TriggerOpTypes.SET, key, value, oldValue); } } return result; }; } //觸發(fā)更新依賴effect export function trigger(target, type, key?, newValue?, oldValue?) { const depsMap = targetMap.get(target); let effectSet = new Set(); if (!depsMap) return; const add = (effectAdd) => { if (effectAdd) { effectAdd.forEach((effect) => effectSet.add(effect)); } }; add(depsMap.get(key)); //獲取當(dāng)前屬性的effect //處理數(shù)組 if (key == "length" && Array.isArray(target)) { depsMap.forEach((dep, key) => { if (key === "length" || key >= newValue) { add(dep); } }); }else{ //處理對象 if(key !== undefined){ add(depsMap.get(key)) } switch(type){ case TriggerOpTypes.ADD: if(Array.isArray(target)&& isIntegerKey(key)){ add(depsMap.get('target')) } } } effectSet.forEach((effect: any) => { if(effect.options.sch){ effect.options.sch(effect) }else{ effect() } });
- 被reactive包裹的數(shù)據(jù)實現(xiàn)響應(yīng)式,首先得effec保存回調(diào)函數(shù),方便數(shù)據(jù)更新再次調(diào)用來更新視圖,
- 當(dāng)用proxy對對象代理時get方法中將視圖用到的代碼片段即相關(guān)effect與key一一對應(yīng)保存起來
- 當(dāng)數(shù)據(jù)改變用到proxy的set方法時,用key找到對應(yīng)effect執(zhí)行
ref
相信大家都知道用reactive實現(xiàn)復(fù)雜數(shù)據(jù)代理用ref實現(xiàn)簡單數(shù)據(jù)的代理,因為vue用es6的proxy代理整個對象,而ref是給簡單數(shù)據(jù)用一個對象包裹起來用object.defineproperty方式代理并且用.value訪問,如果用ref對一個對象進(jìn)行響應(yīng)式處理則會調(diào)用crtoReactive方法再次對.value的屬性進(jìn)行代理。 根據(jù)上面已經(jīng)實現(xiàn)的reactive api實現(xiàn)ref就簡單多了。
具體步驟為:
1.如果是簡單數(shù)據(jù)就用object.defineproperty代理
2.如果是復(fù)雜數(shù)據(jù)就用createReactive方法再次代理
export function ref(target){ return creatRef(target) } //創(chuàng)建實例對象 class RefImpl{ public _v_isRef=true public _value public _shallow public _rawValue constructor(public target,public shallow){ this._shallow=shallow this._rawValue = shallow ? target : toRaw(target) this._value = shallow ? target : toReactive(target) } get value(){ Track(this,TackOpType.GET,"value") return this.value } set value(newVAlue){ if(newVAlue!==this._value) this._rawValue=newVAlue this._value = isObject(newVAlue) ? newVAlue : toReactive(newVAlue)//這是ref不失去響應(yīng)式的關(guān)鍵如果新的值就重新代理 trigger(this,TriggerOpTypes.SET,"value",newVAlue) } } function creatRef(target,shallow=false){ //創(chuàng)建ref實例對象 return new RefImpl(target,shallow) }
到此這篇關(guān)于vue3中reactive和ref的實現(xiàn)與區(qū)別詳解的文章就介紹到這了,更多相關(guān) vue3 reactive ref內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Vue3.0中Ref與Reactive的區(qū)別示例詳析
- vue3?中ref和reactive的區(qū)別講解
- 前端vue3中的ref與reactive用法及區(qū)別總結(jié)
- Vue3 的ref和reactive的用法和區(qū)別示例解析
- Vue3中ref和reactive的基本使用及區(qū)別詳析
- Vue3中ref和reactive的區(qū)別及說明
- vue3.0中ref與reactive的區(qū)別及使用場景分析
- Vue3中關(guān)于ref和reactive的區(qū)別分析
- vue3 ref 和reactive的區(qū)別詳解
- vue3中ref和reactive的區(qū)別舉例詳解
相關(guān)文章
vue+vuex+axios+echarts畫一個動態(tài)更新的中國地圖的方法
本篇文章主要介紹了vue+vuex+axios+echarts畫一個動態(tài)更新的中國地圖的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12vue-cli 自定義指令directive 添加驗證滑塊示例
本篇文章主要介紹了vue-cli 自定義指令directive 添加驗證滑塊示例,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-10-10解決vue2中使用axios http請求出現(xiàn)的問題
下面小編就為大家分享一篇解決vue2中使用axios http請求出現(xiàn)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03在vue框架下使用指令vue add element安裝element報錯問題
這篇文章主要介紹了在vue框架下使用指令vue add element安裝element報錯問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10Laravel 如何在blade文件中使用Vue組件的示例代碼
這篇文章主要介紹了Laravel 如何在blade文件中使用Vue組件,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06Vue中的transition封裝組件的實現(xiàn)方法
這篇文章主要介紹了Vue中的transition封裝組件的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08