關注若川視野
,回復"pdf" 領取資料,回復"加群",可加群長期交流學習
本文結構
????- 關于Vue3
????- Vue2響應式原理回顧
????-?Vue3響應式方案
????-?Vue3響應式原理
????- 手寫mini版Vue3響應式
本文共計:2349字2圖
預計閱讀時間:4min30s
關于Vue3
話說,Vue3已經進行到rc4版本了,4月份beta發布的時候前端圈紅紅火火
不會吧不會吧,不會你還沒開始學吧????
整理了一些資源,現在開始學習應該還不算晚
vue-next倉庫[1]
20200723 Vue3 官方發布的beta文檔[2]
Vue3 Roadmap & FAQ[3]
Vue3倉庫已經合并的780多個PR[4]
尤大在Vue Mastery的Vue3課:Vue 3 Deep Dive with Evan You[5]
202007 尤大在前端會客廳節目關于Vue3的訪談[6]
202005 The process: Making Vue 3[7]
202004 尤大 - 聊聊 Vue.js 3.0 Beta 官方直播[8]
2018 VueConf 杭州 尤大關于Vue3的演講視頻[9]
拉到文章底部找到上述鏈接,以下正文探討一下Vue3響應式原理
Vue2 響應式原理回顧
對象響應化:遍歷每個key,通過
Object.defineProperty
API定義getter,setter
//?偽代碼
function?observe(){if(typeof?obj?!='object'?||?obj?==?null){return}if(Array.isArray(obj)){Object.setPrototypeOf(obj,arrayProto)}else{const?keys?=?Object.keys()for(let?i=0;i<keys.length;i++){const?key?=?keys[i]defineReactive(obj,key,obj[key])}}
}
function?defineReactive(target,?key,?val){observe(val)Object.defineProperty(obj,?key,?{get(){//?依賴收集dep.depend()return?val},set(newVal){if(newVal?!==?val){observe(newVal)val?=?newVal//?通知更新dep.notify()}}})
}
數組響應化:覆蓋數組的原型方法,增加通知變更的邏輯
//?偽代碼
const?originalProto?=?Array.prototype
const?arrayProto?=?Object.create(originalProto)
['push','pop','shift','unshift','splice','reverse','sort'].forEach(key=>{arrayProto[key]?=?function(){originalProto[key].apply(this.arguments)notifyUpdate()}
})
vue2響應式痛點
遞歸,消耗大
新增/刪除屬性,需要額外實現單獨的API
數組,需要額外實現
Map Set Class等數據類型,無法響應式
修改語法有限制
vue3響應式方案
使用ES6的 `Proxy`[10] 進行數據響應化,解決上述Vue2所有痛點
Proxy可以在目標對象上加一層攔截/代理,外界對目標對象的操作,都會經過這層攔截
相比 Object.defineProperty
,Proxy支持的對象操作十分全面:get、set、has、deleteProperty、ownKeys、defineProperty......等等
//?reactive?偽代碼
function?reactice(obj){return?new?Proxy(obj,{get(target,?key,?receiver){const?ret?=?Reflect.get(target,?key,?receiver)return?isObject(ret)???reactice(ret)?:?ret},set(target,?key,?val,?receiver){const?ret?=?Reflect.set(target,?key,?val,?receiver)return?ret},deleteProperty(target,?key){const?ret?=?Reflect.deleteProperty(target,?key)return?ret},})
}
Vue3響應式原理
通過
effect
?聲明依賴響應式數據的函數cb ( 例如視圖渲染函數render函數),并執行cb函數,執行過程中,會觸發響應式數據getter
在響應式數據
getter
中進行track
依賴收集:建立 數據&cb 的映射關系存儲于targetMap
當變更響應式數據時,觸發
trigger
**,**根據targetMap
找到關聯的cb執行映射關系
targetMap
結構:
targetMap:?WeakMap{?target:Map{?key:?Set[cb1,cb2...]?}
}
手寫vue3響應式
大致結構
//?mini-vue3.js/*?建立響應式數據?*/
function?reactice(obj){}/*?聲明響應函數cb(依賴響應式數據)?*/
function?effect(cb){}/*?依賴收集:建立?數據&cb 映射關系?*/
function?track(target,key){}/*?觸發更新:根據映射關系,執行cb */
function?trigger(target,key){}
reactive
/*?建立響應式數據?*/
function?reactive(obj){//?Proxy:http://es6.ruanyifeng.com/#docs/proxy//?Proxy相當于在對象外層加攔截//?Proxy遞歸是惰性的,需要添加遞歸的邏輯//?Reflect:http://es6.ruanyifeng.com/#docs/reflect//?Reflect:用于執行對象默認操作,更規范、更友好,可以理解成操作對象的合集//?Proxy和Object的方法Reflect都有對應if(!isObject(obj))?return?objconst?observed?=?new?Proxy(obj,{get(target,?key,?receiver){const?ret?=?Reflect.get(target,?key,?receiver)console.log('getter?'+ret)//?跟蹤?收集依賴track(target,?key)return?reactive(ret)},set(target,?key,?val,?receiver){const?ret?=?Reflect.set(target,?key,?val,?receiver)console.log('setter?'+key+':'+val?+?'=>'?+?ret)//?觸發更新trigger(target,?key)return?ret},deleteProperty(target,?key){const?ret?=?Reflect.deleteProperty(target,?key)console.log('delete?'+key+':'+ret)//?觸發更新trigger(target,?key)return?ret},})return?observed
}
effect
/*?聲明響應函數cb?*/
const?effectStack?=?[]
function?effect(cb){//?對函數進行高階封裝const?rxEffect?=?function(){//?1.捕獲異常//?2.fn出棧入棧//?3.執行fntry{effectStack.push(rxEffect)return?cb()}finally{effectStack.pop()}}//?最初要執行一次,進行最初的依賴收集rxEffect()return?rxEffect
}
track
/*?依賴收集:建立?數據&cb 映射關系?*/
const?targetMap?=?new?WeakMap()
function?track(target,key){//?存入映射關系const?effectFn?=?effectStack[effectStack.length?-?1]??//?拿出棧頂函數if(effectFn){let?depsMap?=?targetMap.get(target)if(!depsMap){depsMap?=?new?Map()targetMap.set(target,?depsMap)}let?deps?=?depsMap.get(key)if(!deps){deps?=?new?Set()depsMap.set(key,?deps)}deps.add(effectFn)}
}
trigger
/*?觸發更新:根據映射關系,執行cb */
function?trigger(target,?key){const?depsMap?=?targetMap.get(target)if(depsMap){const?deps?=?depsMap.get(key)if(deps){deps.forEach(effect=>effect())}}
}
測試demo
<!--?test.html?-->
<div?id="app">{{msg}}
</div><script?src="./mini-vue3.js"></script><script>//?定義一個響應式數據const?state?=?reactive({msg:'message'})//?定義一個使用到響應式數據的?dom更新函數function?updateDom(){document.getElementById('app').innerText?=?state.msg}//?用effect聲明更新函數effect(updateDom)//?定時變更響應式數據setInterval(()=>{state.msg?=?'message'?+?Math.random()},1000)
</script>
效果:
如果想獲取上述代碼,放在了這個倉庫:mini-vue3-reactive[11]
參考資料
[1]
vue-next倉庫: https://github.com/vuejs/vue-next
[2]20200723 Vue3 官方發布的beta文檔: https://v3.vuejs.org/
[3]Vue3 Roadmap & FAQ: https://github.com/vuejs/vue/projects/6
[4]Vue3倉庫已經合并的780多個PR: https://github.com/vuejs/vue-next/pulls?q=is%3Apr+is%3Amerged
[5]尤大在Vue Mastery的Vue3課:Vue 3 Deep Dive with Evan You: https://www.vuemastery.com/courses/vue3-deep-dive-with-evan-you/vue3-overview
[6]202007 尤大在前端會客廳節目關于Vue3的訪談: https://www.bilibili.com/video/BV1qC4y18721
[7]202005 The process: Making Vue 3: https://increment.com/frontend/making-vue-3/
[8]202004 尤大 - 聊聊 Vue.js 3.0 Beta 官方直播: https://www.bilibili.com/video/BV1Tg4y1z7FH
[9]2018 VueConf 杭州 尤大關于Vue3的演講視頻: https://www.bilibili.com/video/BV1Et41197L4
[10]Proxy
: https://es6.ruanyifeng.com/#docs/proxy
倉庫:mini-vue3-reactive: https://github.com/scarsu/mini-vue3-reactive
- END -推薦閱讀
我在阿里招前端,我該怎么幫你?(文末有福利)
如何拿下阿里巴巴 P6 的前端 Offer
如何準備阿里P6/P7前端面試--項目經歷準備篇
大廠面試官常問的亮點,該如何做出?
如何從初級到專家(P4-P7)打破成長瓶頸和有效突破
若川知乎問答:2年前端經驗,做的項目沒什么技術含量,怎么辦?
末尾
你好,我是若川,江湖人稱菜如若川,歷時一年只寫了一個學習源碼整體架構系列~(點擊藍字了解我)
關注
若川視野
,回復"pdf" 領取優質前端書籍pdf,回復"加群",可加群長期交流學習我的博客地址:https://lxchuan12.gitee.io?歡迎收藏
覺得文章不錯,可以點個
在看
呀^_^另外歡迎留言
交流~
小提醒:若川視野公眾號面試、源碼等文章合集在菜單欄中間
【源碼精選】
按鈕,歡迎點擊閱讀,也可以星標我的公眾號,便于查找