vue3出來也有段時間了,無疑被說的最多應該就是vue的數據攔截用proxy重寫了(以前用的是Object.defineProperty)和 Composition API 了。本篇文,來體驗一下proxy的攔截能力到底有多優秀。javascript
這裏經過vue1.x,vue2.x
時的數據攔截來講一下Object.defineProperty
。前端
提早說明,這裏的數據攔截,不包含vue依賴收集,視圖更新,否則又給可愛的掘友罵死。vue
先來用Object.defineProperty
實現一下對象的攔截。java
let data = {
m:234,
n:[1,34,4,5676],
h:{
c:34
}
}
function observer(data){
if(typeof data === 'object'){
Object.keys(data).forEach(key=>{
defineReactive(data,key,data[key])
})
}
}
function defineReactive(obj,key,val){
Object.defineProperty(obj,key,{
get(){
console.log('get')
return val
},
set(newVal){
console.log('set')
if(newVal !== val ) val = newVal
}
})
}
複製代碼
上面經過遍歷data
的數據,進行了一次簡單的攔截;看似沒有問題,但若是咱們改變data.h.c
是不會觸發set鉤子的
,爲何?由於這裏尚未實現遞歸
,因此只攔截了最表面的一層
,裏面的則沒有被攔截。數組
遞歸攔截對象markdown
function defineReactive(obj,key,val){
observer(val)
Object.defineProperty(obj,key,{
get(){
console.log('get')
return val
},
set(newVal){
console.log('set')
if(newVal !== val ) val = newVal
}
})
}
複製代碼
遞歸攔截,只要在defineReactive函數
再調一次observer函數
把要攔截的值傳給它就行。這樣,就實現了對象的多層攔截。可是呢,如今是攔截不到數組的,當咱們調用push,pop等方法
它是不會觸發set鉤子的
,爲何?由於Object.defineProperty
壓根就不支持數組的攔截。既然它不支持,那麼咱們只能攔截它的這些('push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse')
改變自身數據的方法了。app
function arrayMethods(){
const arrProto = Array.prototype
const arrayMethods = Object.create(arrProto)
const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
methods.forEach(function (method) {
const original = arrProto[method]
Object.defineProperty(arrayMethods, method, {
value: function v(...args) {
console.log('set arrayMethods')
return original.apply(this, args)
}
})
})
return arrayMethods
}
複製代碼
以上就是對這些數組的原型方法進行了一個攔截,而後把它覆蓋要攔截的數組的原型就行,下面簡單修改一下observer函數
function observer(data){
if(typeof data === 'object'){
if(Array.isArray(data)){
data.__proto__ = arrayMethods()
}else{
Object.keys(data).forEach(key=>{
defineReactive(data,key,data[key])
})
}
}
}
複製代碼
在vue
中,還會判斷該key
有沒有__proto__
,若是沒有就直接把這些方法放到這個key的自身上,若是有就直接覆蓋這個__proto__
。post
function arrayMethods(){
const arrProto = Array.prototype
const arrayMethods = Object.create(arrProto)
const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
methods.forEach(function (method) {
const original = arrProto[method]
Object.defineProperty(arrayMethods, method, {
value: function v(...args) {
console.log('set arrayMethods')
return original.apply(this, args)
}
})
})
return arrayMethods
}
function observer(data){
if(typeof data === 'object'){
if(Array.isArray(data)){
data.__proto__ = arrayMethods()
}else{
Object.keys(data).forEach(key=>{
defineReactive(data,key,data[key])
})
}
}
}
function defineReactive(obj,key,val){
observer(val)
Object.defineProperty(obj,key,{
get(){
console.log('get')
return val
},
set(newVal){
console.log('set')
if(newVal !== val ) val = newVal
}
})
}
observer(data)
複製代碼
以上,就完成了對對象和數組
的攔截(說明:vue2.x中的實現會比這裏複雜,但大概思路和大概實現是這樣)
,看似辛苦點,換來一個完美的結果挺不錯的。但真的是你想的那樣嗎?試一下調用data.n[1] = xxx
,它是不會觸發set鉤子的
,這也是在proxy
出現以前,無能無力的,因此在vue中提供了$set,$delete
API。ui
這裏就不介紹proxy了,就當你對它有了解過了。直接上代碼
let data = {
m:234,
n:[1,34,4,5676],
h:{
c:34
}
}
function defineReactive(obj){
Object.keys(obj).forEach((key) => {
if(typeof obj[key] === 'object'){
obj[key] = defineReactive(obj[key])
}
})
return new Proxy(obj,{
get(target,key){
console.log('get')
return target[key]
},
set(target,key,val){
console.log('set')
return target[key] = val
}
})
}
data = defineReactive(data)
複製代碼
就這麼一點代碼就實現了對對象和數組
的攔截(說明:這裏不是vue3的實現方法,vue3怎麼實現的,我還不知道,還沒看過它的源碼,有興趣本身去看一下,而後順便告訴我一下怎麼實現的,哈哈哈)
,Object.defineProperty
實現不了的,它能實現;Object.defineProperty
實現的了,它也能實現。不管你調push,pop等方法
它能攔截,你調data.n[1] = xxx
也能攔截,簡直不要太爽,這個兩個版本的實現,給我我的的感受就是一個韓紅版(肉多腿短),一個迪麗熱巴版(苗條腿長),哈哈哈,本身品。
這裏只是經過對對象和數組
的攔截,來體驗了一下proxy的威力;proxy能作的遠遠不止這樣。
瞭解proxy更多特性,能夠看看我以前整理比較基礎《Proxy入門》
瞭解proxy更多用法,能夠看看,前端小智的《Proxy 的巧用》
若是你想了解更多proxy的特性,和更多的用例能夠,看看以上兩篇文章(自願看哈,我不想給罵,說什麼廣告,瞎jb推薦之類的,🐶保命)。