本篇文章為大家帶來了vue2&vue3數據響應式原理分析及手動實現的相關知識,數據響應式視圖跟數據是自動更新的,數據更新的時候視圖是自動的更新的追踪數據的變化,希望對大家有幫助。
var obj = {}var age Object.defineProperty(obj, 'age', { get: function() { consoel.log('get age ...') return age }, set: function(val) { console.log('set age ...') age = val }})obj.age =100 //set age ...console.log(obj.age)//get age ...
物件obj在取age屬性的時候會呼叫資料劫持的get方法
在給age屬性賦值的時候會呼叫set方法
那怎麼使用Object.defineProperty實作一個資料回應式呢
function defineReactive(data) { if (!data || Object.prototype.toString.call(data) !== '[object Object]') return; for (let key in data) { let val = data[key]; Object.defineProperty(data, key, { enumerable: true, //可枚举 configurable: true, //可配置 get: function() { track(data, key); return val; }, set: function() { trigger(val, key); }, }); if (typeof val === "object") { defineReactive(val); } }}function trigger(val, key) { console.log("sue set", val, key);}function track(val, key) { console.log("sue set", val, key);}const data = { name:'better', firends:['1','2']}defineReactive(data)console.log(data.name)console.log(data.firends[1])console.log(data.firends[0])console.log(Object.prototype.toString.call(data))
這個函數defineReactve
用來對Object.defineProperty
進行封裝,從函數名稱可以看出,運作就是定義一個響應式資料,封裝後只需要傳遞data,key和val就行
每當從data讀取key的時候觸發track函數,往data的key設定資料時,set函數中的trigger函數觸發
我們透過Array原型上的方法來改變陣列的內容不會觸發getter和setter
整理發現Array原型中可以改變陣列本身內容
的方法有7個,分別push
pop
#shift
unshift
splice
sort
reverse
vue2 改寫了這7種方法
實作方式:
以Array.propertype為原型建立一個arrayMethods物件,再使用Object.setPropertypeOf(o, arryMethods)
將o的__proto__指向arrayMethods
使用
<template><p>{{name}}</p></template>
該範本中使用資料name
, 我們要觀察資料, 當資料的屬性改變的時候, 可以通知哪些使用的地方,
這就是我們要先收集依賴,即把用到資料name的地方收集起來,然後等資料變化的時候,把之前收集好的依賴循環觸發一遍,總結來說就是getter中收集依賴,在setter中觸發依賴
Proxy物件用於建立一個物件的代理, 從而實現基本操作的攔截和定義(如屬性查找、賦值、枚舉、函數掉用等)
const p = new Proxy(target, handler)
target
要使用 Proxy
包裝的目標物件(可以是任何類型的對象,包括原生數組,函數,甚至另一個代理) 。
handler
一個通常以函數為屬性的對象,各屬性中的函數分別定義了在執行各種操作時代理 p
的行為。
reflect是內建對象, 他提供攔截javascript操作的方法, 這些方法和Proxy handlers相同
Reflect.set將值分配給屬性的函數。傳回一個Boolean 如果更新成功則回傳true
Reflect.get取得物件身上某個屬性的值,類似target[name]
如何實作劫持
const dinner = { meal:'111'}const handler = { get(target, prop) { console.log('get...', prop) return Reflect.get(...arguments) }, set(target, key, value) { console.log('get...', prop) console.log('set',key,value) return Reflect.set(...arguments) }}const proxy = new Proxy(dinner, handler)console.log(proxy.meal)console.log(proxy.meal)
程式碼中dinner 物件代理到handler上
跟defineProperty
區別defineProperty
的屬性需要遍歷才能監管所有屬性
使用proxy可以將物件所有屬性進行代理
用proxy實作一個模擬響應式
function reactive(obj) { const handler = { get(target, prop, receiver) { track(target, prop); const value = Reflect.get(...arguments); if(typeof value === 'Object') { reactive(value) }else { return value } }, set(target,key, value, receiver) { trigger(target,key, value); return Reflect.set(...arguments); }, }; return new Proxy(obj,handler)}function track(data, key) { console.log("sue set", data, key);}function trigger(data, key,value) { console.log("sue set", key,':',value);}const dinner = { name:'haochi1'}const proxy =reactive(dinner)proxy.name proxy.list = []proxy.list.push(1)
執行後自動列印
#思考:為啥只在get中使用遞歸,set不使用呢?
賦值也需要先get
使用proxy 進行代理;攔截data任意屬性的任意操作(13種), 包括屬性的讀寫, 屬性的添加, 屬性的刪除等等
使用Reflect進行反射;動態對被代理的物件的對應屬性進行特定的操作
代理物件(proxy)的反射物件(reflect)必須相互配合才能實現響應式
Proxy能劫持整個物件,而Object.defineProperty只能劫持物件的屬性; 前者遞歸回傳屬性對應的值的代理即可實現響應式,後者需要深度遍歷每個屬性,後者對數組的操作很不友好.
更多編程相關知識,請訪問:編程入門! !
以上是vue2&vue3資料響應式原理分析及手動實作(實例詳解)的詳細內容。更多資訊請關注PHP中文網其他相關文章!