詳解Vue響應式的部分實現(xiàn)
什么是響應式
簡單來說當數(shù)據(jù)發(fā)生變化時,對數(shù)據(jù)有依賴的代碼會重新執(zhí)行。
例如在Vue中,當我們的數(shù)據(jù)發(fā)生改變,界面上對該數(shù)據(jù)的引用組件會重新渲染
組件data的數(shù)據(jù)一旦變化,立即出發(fā)視圖的更新;
computed屬性在依賴發(fā)生變化時,自動重新計算新值;提供watch監(jiān)聽器,可以監(jiān)聽到數(shù)據(jù)的變化
Vue2與Vue3響應式之間的區(qū)別
- Vue2使用ES5的defineProperty實現(xiàn)
- Vue3使用的是ES6的propxy.(PS:這也就是為什么Vue2不支持IE7/8,而Vue3不支持IE11.)
使用Object.defineProperty監(jiān)聽對象
該方法允許精確地添加或修改對象的屬性,并返回此對象。
Object.defineProperty()方法會在直接在一個對象上定義一個新屬性,或者修改一個對象的現(xiàn)有屬性,并返回此對象
備注:(應當直接在object構造器對象上調用此方法,而不是在任意一個object類型的實例上調用)
語法:Object.defineProperty(obj, prop, descriptor)
- obj:要設置屬性的對象;
- prop:要設置的屬性名,這個屬性可以是已存在也可以是不存在的;
- descriptor:要定義或修改的屬性描述符。該參數(shù)接收一個對象,用來對屬性進行描述。如value(值),writable(是否可重寫),enumerable(是否可枚舉)等
枚舉時使用for...in 或 Object.keys方法可以改變這些屬性的值,默認情況下,使用 Object.defineProperty() 添加的屬性值是不可修改(immutable)的。
對象里目前存在的屬性描述符有兩種主要形式:數(shù)據(jù)描述符和存取描述符
- 數(shù)據(jù)描述符:是一個具有值的屬性,該值是可寫的,也可以是不可寫的
- 存取描述符:由getter函數(shù)和setter函數(shù)所描述的屬性
一個描述符只能是這兩者其中之一,不能同時是兩者
使用Object.defineProperty監(jiān)聽對象
利用 Object.defineProperty 重寫 get 和 set,將對象屬性的賦值和獲取變成函數(shù),我們可以實現(xiàn)一個簡單的雙向綁定
- get: 屬性的 getter 函數(shù),如果沒有 getter,則為 undefined。當訪問該屬性時,會調用此函數(shù)。該函數(shù)的返回值會被用作屬性的值。默認為 undefined。
- set: 屬性的 setter 函數(shù),如果沒有 setter,則為 undefined。當屬性值被修改時,會調用此函數(shù)。默認為 undefined。
//實現(xiàn)一個簡單的雙向綁定
const data = {}
const name = 'xiaowanzi'
Object.defineProperty(data, 'name', {
get: function () {
console.log('get')
return name
},
set: function (newVal) {
console.log('set')
name = newVal
}
})
//測試
console.log(data.name)//get xiaowanzi
data.name = 'list'//set如果我們想讓對象的所有屬性都具有響應式,就需要對全部屬性進行遍歷,實現(xiàn)getter和setter:
//實現(xiàn)Vue響應式原理
let obj = {
name: 'aaa',
age: 18
}
//獲取obj對象的所有key
const keys = Object.keys(obj)//Object.keys()返回一個由一個給定對象的資深可枚舉屬性組成的數(shù)組,數(shù)組中的屬性名的排列順序和正常循環(huán)遍歷該對象時返回的順序一致
//遍歷Key數(shù)組,對obj對象的每一個屬性進行處理
keys.forEach(key => {
//使用value變量保存key對應的屬性值
let value = obj[key]
//使用Object.defineProperty
Object.defineProperty(obj, key, {
get() {//當獲取屬性時,回來到這里
console.log(`${key}屬性被獲取`)
return value
},
set(newValue) {//當修改屬性時,會來到這里,并且設置的值會傳給newValue
console.log(`${key}屬性被修改`)
//這里不能寫成obj[key]=newValue
//如果這樣寫相當于又對該屬性進行修改值,又會進入set,就死循環(huán)了
value = newValue
}
})
})
?
//現(xiàn)在我們已經(jīng)可以實現(xiàn)監(jiān)聽obj對象的讀取與修改了
console.log(obj.name)//在打印'aaa'之前會先打印'name被獲取',也就是說監(jiān)聽到屬性的獲取。
obj.name = 'bbb'//打印name屬性被修改,也就是說監(jiān)聽到了屬性的改變
?
//實現(xiàn)Vue響應式原理
let obj={
name:'aaa',
age:18
}
//獲取obj對象的所有key
const keys=object.keys(obj)//Object.keys()返回一個由一個給定對象的資深可枚舉屬性組成的數(shù)組,數(shù)組中的屬性名的排列順序和正常循環(huán)遍歷該對象時返回的順序一致
//遍歷Key數(shù)組,對obj對象的每一個屬性進行處理
keys.forEach(key=>{
//使用value變量保存key對應的屬性值
let value = obj[key]
//使用Object.defineProperty
Object.defineProperty(obj,key,{
get(){//當獲取屬性時,回來到這里
console.log(`${key}屬性被獲取`)
return value
},
set(newValue){//當修改屬性時,會來到這里,并且設置的值會傳給newValue
console.log(`${key}屬性被修改`)
//這里不能寫成obj[key]=newValue
//如果這樣寫相當于又對該屬性進行修改值,又會進入set,就死循環(huán)了
value=newValue
}
})
})
?
//現(xiàn)在我們已經(jīng)可以實現(xiàn)監(jiān)聽obj對象的讀取與修改了
console.log(obj.name)//在打印'aaa'之前會先打印'name被獲取',也就是說監(jiān)聽到屬性的獲取。
obj.name='bbb'//打印name屬性被修改,也就是說監(jiān)聽到了屬性的改變
缺點
可以實現(xiàn)監(jiān)聽對象的屬性,但是它沒有辦法做到對對象新增的屬性進行監(jiān)聽,同時也沒有辦法做到對數(shù)據(jù)進行監(jiān)聽
使用ES6的Proxy實現(xiàn)監(jiān)聽對象
該API就是用來實現(xiàn)監(jiān)聽對象的,而且該API對數(shù)組同樣也是有效果的,在使用Proxy時,通常會搭配Reflect一起使用 Proxy
用于創(chuàng)建代理對象,從而實現(xiàn)基本操作的攔截和自定義(如屬性的查找,賦值,枚舉,函數(shù)調用等)
術語:
- handler:包含捕捉器(trap)的占位符對象,可譯為處理器對象。
- traps:提供屬性訪問的方法。這類似于操作系統(tǒng)中捕獲器的概念。
- target:被 Proxy 代理虛擬化的對象。它常被作為代理的存儲后端。根據(jù)目標驗證關于對象不可擴展性或不可配置屬性的不變量(保持不變的語義)
語法:
const p = new Proxy(target, handler)
1.第一個參數(shù)target:要包裝的目標對象
2.第二個參數(shù)handle:接收一個對象,內部定義了操作目標對象時的方法;
參數(shù):
- target:要使用
Proxy包裝的目標對象(可以是任何類型的對象,包括原生數(shù)組,函數(shù),甚至另一個代理)。 - handler:一個通常以函數(shù)作為屬性的對象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時代理
p的行為。
- Proxy.revocable()
- 創(chuàng)建一個可撤銷的
Proxy對象。
Reflect
是一個內置的對象,它提供攔截 JavaScript 操作的方法。Reflect不是一個函數(shù)對象,因此它是不可構造的。
通過給對象設置代理,我們可以攔截對象屬性的取值/賦值操作。
舉個例子:
const student = {
age: 23
}
const handler = {
get(target, prop) {
console.log("讀值:", key, value);
target[key] = value;
return target[prop]
},
set(target, key, value) {
console.log("設置值", key, value);
target[key] = value;
return true
}
}
const proxy = new Proxy(studengt, handler)
console.log(proxy.age)//23
//proxy.age=32 //32
實現(xiàn)代碼
//Proxy+Reflect
let obj = {
name: 'aaa',
age: 18
}
//第一個參數(shù)為要代理的對象,第二個參數(shù)位hander
const proxy = new Proxy(obj, {
//當訪問第一個屬性的時候會得到getter
//同時會傳遞三個參數(shù)
//target要進行代理對象,這里就是obj
//key被訪問的屬性
//receiver用來綁定this
get(target, key, receiver) {
console.log(`${key}屬性被訪問`)
return Reflect.get(target, key, receiver)
},
//當某一屬性修改的時,回來到Setter
// 同時會傳遞四個參數(shù)
// target要進行代理的對象,這里就是obj
// 可以被訪問的屬性
// newValue新修改的值
// receiver用來綁定this
set(target, key, newValue, receiver) {
console.log(`${key}屬性修改`)
return Reflect.set(target, key, newValue, receiver)
}
})
// 以上代碼執(zhí)行完,得到的就是proxy對象就是obj對象的代理
// 我們只需要修改代理對象的就可以做到修改原型對象的效果
// 而且我們對代理對象的修改使我們能夠監(jiān)聽到的
console.log(proxy.name)
proxy.name = 'bbb'到此這篇關于詳解Vue響應式的部分實現(xiàn)的文章就介紹到這了,更多相關Vue響應式內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring mvc Controller和RestFul原理解析
這篇文章主要介紹了Spring mvc Controller和RestFul原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-03-03
SpringBoot整合Mybatis-plus案例及用法實例
mybatis-plus是一個 Mybatis 的增強工具,在 Mybatis 的基礎上只做增強不做改變,為簡化開發(fā)、提高效率而生,下面這篇文章主要給大家介紹了關于SpringBoot整合Mybatis-plus案例及用法實例的相關資料,需要的朋友可以參考下2022-11-11

