一、什么是響應式
1、響應式英文reactive
當你get/set一個變量時,你有辦法可以“捕獲到”這種行為。
2、一個普通對象和一個響應式對象對比
(1)普通對象
<script>// 這種普通對象不具備響應式var obj1 = {a: 1,b: 2}
</script>
普通對象,當我們獲取、修改屬性的時候沒辦法捕獲get/set行為(這種普通對象是不具備響應式的):
(2)響應式對象
<script>var obj2 = {}var a = 1var b = 2// 給obj2這個對象添加一個新屬性Object.defineProperty(obj2, 'a', {get() {console.log(`有人訪問了a`)return a},set(val) {console.log(`有人修改了a`)a = val}}) // 給obj2對象加一個a屬性Object.defineProperty(obj2, 'b', {get() {console.log(`有人訪問了b`)return b},set(val) {console.log(`有人修改了b`)b = val}}) // 給obj2對象加一個b屬性</script>
當獲取、修改obj2屬性的時候,會觸發鉤子函數。obj2對象多了get和set函數。obj2具有了響應式:
二、響應式原理
1、流程圖
2、代碼
<html>
<head><title>響應式原理</title></head>
<body>輸入:<input id="ipt" type="text" />輸出:<h2 id="h2" style='display:inline-block;'></h2><hr><h1 id="h1"></h1><button id="btn">自增</button><script>// 這是模擬data選項(普通的對象)const data = {name: '張三',num: 1}// 這是模擬vue組件實例const app = {}// 對data進行遍歷,['name', 'num']// 遍歷完了把數組放到app上// 生命周期的第一階段(劫持,就是添加get/set)Object.keys(data).forEach(k=>{Object.defineProperty(app, k, {// handle加劫持get() {console.log(`getter ${k}`) // touch操作return data[k]},set(val) {console.log(`setter ${k}`) // notify操作data[k] = valwatcher(k) // 通知更新界面}})})// dep對象專門用于依賴收集的const dep = {name: [],num: []}// 生命周期的第二階段(相當于掛載階段)// Collect as Dependency依賴收集function init() {// 模擬v-model='name'dep['name'].push(() => { // 依賴收集document.getElementById('ipt').value = app.name // get功能})// 綁定input事件document.getElementById('ipt').addEventListener('input', ev => {app.name = ev.target.value // set功能})// 模擬v-text='name'dep['name'].push(() => {document.getElementById('h2').innerText = app.name // get功能})dep['num'].push(()=> {document.getElementById('h1').innerText = app.num // get功能})document.getElementById('btn').addEventListener('click', ev => {app.num++ //set功能})// 第一次更新DOMObject.keys(dep).forEach(k=>watcher(k))}// 封裝一個Watcherfunction watcher(k) {dep[k].forEach(fn=>fn()) // 循環調用變量依賴的function}// 調用initinit()</script></body>
</html>
3、說明
(1)生命周期的第一階段(劫持,就是添加get/set)
"Touch"和Notify
(2)生命周期的第二階段(相當于掛載階段)
把DOM放在真實的DOM上渲染,Collect as Dependency依賴收集
收集的結果是得到一個Watcher,通過re-render更新DOM(以后變量有變化,我通知Watcher,讓Watcher再去更新界面)
(3)依賴收集
我要改變表單的值,并且要push一個依賴
二、vue的響應式原理小結
1、正常的對象是沒有辦法做更新的,必須要給它加鉤子,才能知道它被訪問
2、解釋幾個重要概念
劫持:使用Object.defineProperty對data選項進行遍歷并添加getter/setter的鉤子
touch:當指令第一次與聲明式變量綁定時,第一次觸發聲明式變量的get鉤子
依賴收集:當第一次touch時,把當前聲明式變量的更新方法添加到dep依賴數組中
watcher:與聲明式變量對應的DOM方法
re-render:當聲明式變量被set時,Vue通知watcher更新DOM
3、響應式原理
當vue組件被創建時,在生命周期的第一階段,vue使用Object.defineProperty()對data選項進行遍歷劫持并添加get/set鉤子。
在生命周期第二階段,指令第一次與聲明式變量touch時,發生依賴收集,再調用當前組件的watch第一次更新DOM,DOM視圖就顯示出來了。
當聲明式變量發生變化時,vue再次通知watcher更新視圖,這就是響應式。
?