vue中defineProperty和Proxy的區(qū)別詳解
Proxy的出現(xiàn),給vue響應(yīng)式帶來了極大的便利,比如可以直接劫持?jǐn)?shù)組、對象的改變,可以直接添加對象屬性,但是兼容性可能會有些問題
Proxy可以劫持的數(shù)組的改變,defineProperty 需要變異
defineProperty 中劫持?jǐn)?shù)組變化的變異的方法
可以理解為在數(shù)組實(shí)例和原型之間,插入了一個(gè)新的原型的對象,這個(gè)原型方法實(shí)現(xiàn)了變異的方法,也就真正地?cái)r截了數(shù)組原型上的方法
我們來看下vue2.x的源碼
// vue 2.5.0
var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto); // Array {}
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
var methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
];
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function(method) {
// cache original method
var original = arrayProto[method];
// 比如 method是push,則結(jié)果為
// ƒ push() { [native code] }
def(arrayMethods, method, function mutator() {
var args = [],
len = arguments.length;
while (len--) args[len] = arguments[len];
var result = original.apply(this, args);
var ob = this.__ob__;
var inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break
case 'splice':
inserted = args.slice(2);
break
}
if (inserted) {
ob.observeArray(inserted);
}
// notify change
ob.dep.notify();
return result
});
});
/**
* Observe a list of Array items.
*/
Observer.prototype.observeArray = function observeArray(items) {
for (var i = 0, l = items.length; i < l; i++) {
observe(items[i]); // 后續(xù)的邏輯
}
};
Proxy可以直接劫持?jǐn)?shù)組的改變
let proxy = new Proxy(fruit, {
get: function (obj, prop) {
return prop in obj ? obj[prop] : undefined
},
set: function (obj, prop, newVal) {
obj[prop] = newVal
console.log("newVal", newVal) // 輸出{ name: "lemon", num: 999 }
return true;
}
})
proxy.push({ name: "lemon", num: 999 })
console.log(fruit)

Proxy代理可以劫持對象的改變,defineProperty需要遍歷
defineProperty
let fruit = {
"apple": 2,
"pear": 22,
"peach": 222
}
Object.keys(fruit).forEach(function (key) {
Object.defineProperty(fruit[i], key, {
enumerable: true,
configurable: true,
get: function () {
return val;
},
set: function (newVal) {
val = newVal; // 輸出 newVal 888
console.log("newVal", newVal)
}
})
})
fruit.apple = 888
Proxy
let fruit = {
"apple": 2,
"pear": 22,
"peach": 222
}
let proxy = new Proxy(fruit, {
get: function (obj, prop) {
return prop in obj ? obj[prop] : undefined
},
set: function (obj, prop, newVal) {
obj[prop] = newVal
console.log("newVal", newVal) // 輸出 newVal 888
return true;
}
})
proxy.apple = 888
Proxy代理可以劫持對象屬性的添加,defineProperty用this.$set來實(shí)現(xiàn)
defineProperty,如果屬性不存在,則需要借助this.$set
<div id="app">
<span v-for="(value,name) in fruit">{{name}}:{{value}}個(gè) </span>
<button @click="add()">添加檸檬</button>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
new Vue({
el: '#app',
data() {
return {
fruit: {
apple: 1,
banana: 4,
orange: 5
}
}
},
methods: {
add() {
this.fruit.lemon = 5; // 不會讓視圖發(fā)生變化
// this.$set(this.fruit,"lemon",5) // this.$set可以
}
}
})
</script>
Object.keys(fruit).forEach(function (key) {
Object.defineProperty(fruit, key, {
enumerable: true,
configurable: true,
get: function () {
return val;
},
set: function (newVal) {
val = newVal;
console.log("newVal", newVal) // 根本沒有進(jìn)去這里
}
})
})
Proxy 直接可以添加屬性
// vue 3
<div id="app">
<span v-for="(value,name) in fruit">{{name}}:{{value}}個(gè) </span>
<button @click="add()">添加檸檬</button>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
data() {
return {
fruit: {
apple: 1,
banana: 4,
orange: 5
}
}
},
methods: {
add() {
this.fruit.lemon = 5; // 這樣子是可以的
}
}
}).mount('#app') // vue 3 不再是使用el屬性,而是使用mount
</script>
let proxy = new Proxy(fruit, {
get: function (obj, prop) {
return prop in obj ? obj[prop] : undefined
},
set: function (obj, prop, newVal) {
obj[prop] = newVal
console.log("newVal", newVal) // lemon, 888
return true;
}
})
proxy.lemon = 888
Proxy
其他屬性

應(yīng)用場景 promisify化
用Proxy寫一個(gè)場景,請求都是通過回調(diào),如果我們需要用promise包一層的話,則可以
// server.js
// 假設(shè)這里都是回調(diào)
export const searchResultList = function (data, callback, errorCallback) {
axios.post(url, data, callback, errorCallback)
}
// promisify.js
import * as server from './server.js'
const promisify = (name,obj) => (option) => {
return new Promise((resolve, reject) => {
return obj[name](
option,
resolve,
reject,
)
})
}
const serverPromisify = new Proxy(server, {
get (target,prop) {
return promisify(prop, server)
}
})
export default serverPromisify
使用
// index.js
import serverPromisify from './serverPromisify'
serverPromisify.searchResultList(data).then(res=>{
})
如有不正確,望請指出

留下一個(gè)疑問,既然兼容性不是很好,那么尤大是怎么處理polyfill呢
到此這篇關(guān)于vue中defineProperty和Proxy的區(qū)別詳解的文章就介紹到這了,更多相關(guān)vue defineProperty和Proxy內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
關(guān)于vue3.0使用axios報(bào)錯(cuò)問題
這篇文章主要介紹了vue3.0使用axios報(bào)錯(cuò)問題記錄,vue-cli3.0安裝插件的時(shí)候要注意區(qū)分vue-cli2.0的命令,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
vue elementUI tree樹形控件獲取父節(jié)點(diǎn)ID的實(shí)例
今天小編就為大家分享一篇vue elementUI tree樹形控件獲取父節(jié)點(diǎn)ID的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09
vue自定義指令用法經(jīng)典實(shí)例小結(jié)
這篇文章主要介紹了vue自定義指令用法,結(jié)合實(shí)例形式總結(jié)分析了vue自定義指令常見寫法與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-03-03

