Thinking系列,旨在利用10分鐘的時間傳達一種可落地的編程思想,
Vue 遍歷物件所有的 property,并使用 Object.defineProperty 把這些 property 全部轉為 getter/setter,getter/setter 對用戶來說是不可見的,但是在內部 Vue 能夠追蹤依賴,在 property 被訪問和修改時通知變更,依此做到了資料的回應式,
vue 通過 Object.defineProperty 的實作思路,值得我們思考,下面提到的屬性延遲加載就是其中一個引發點,
常規寫法示例
async function getData() {
let data = await fetch(new Request('./data.json')).then(res => res.json())
return data
}
let res = { data: [] }
res.data = await getData()
// res.data 用于頁面渲染
render(res.data)
上述寫法,在 javascript 編碼中可能出現比較少,但在 vue 等開發中,卻會經常看到類似的寫法:
<script>
data () {
return {
res: {data: []}
}
},
methods: {
async function getData() {
this.res.data = await fetch(new Request('./data.json')).then(res => res.json())
}
},
created () {
this.getData()
}
</script>
- 如果
res.data在頁面渲染時,直接首屏呈現,上述寫法沒有問題; - 如果
res.data是通過某些操作觸發才呈現,那上述寫法需要優化,
關于第2種假設,優化點在于**「延遲加載」**,你有可能會說,可以監聽觸發動作(如click,scroll),然后在相應事件中觸發,
document.querySelector('#btn').addEventListener('click', async () => render(await getData()))
上述處理沒有問題,但這里想要提到的是屬性自動觸發的方式 – 借助 Object.defineProperty,
延遲加載物件屬性
let res = {
get data() {
return fetch(new Request('./data.json')).then(res => res.json())
}
}
// 注意上述是異步
await res.data
這樣可以做到在呼叫 res.data 時,才會執行相關獲取資料操作,
快取結果,避免重復執行
延遲加載(將計算推遲到第一次讀取屬性時),然后快取結果以供后續使用,避免重復執行相同的作業是提高性能的最佳方式之一,直接利用快取結果可以加快運行速度,
let res = {
get data() {
let _data = fetch(new Request('./data.json')).then(res => res.json())
Object.defineProperty(this, 'data', {
value: _data,
writable: false,
configurable: false,
enumerable: false
})
return _data
}
}
console.log(object.hasOwnProperty("data")) // true
const data = await object.data
console.log(object.hasOwnProperty("data")) // true
vue commputed
「延遲加載、快取結果」這個和 vue computed 實作的效果一樣!計算屬性是通過 getter 函式延遲加載,基于它們的回應式依賴進行快取的,
Vue 中對于 computed 實作也是借助 defineProperty - https://github1s.com/vuejs/vue/blob/2.6/dist/vue.js#L3580-L3581
function defineComputed (
target,
key,
userDef
) {
var shouldCache = !isServerRendering();
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: createGetterInvoker(userDef);
sharedPropertyDefinition.set = noop;
}
if (sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
("Computed property \"" + key + "\" was assigned to but it has no setter."),
this
);
};
}
Object.defineProperty(target, key, sharedPropertyDefinition);
}
class
class 可以同樣實作延遲加載,并對結果做相應的快取處理,
class MyClass {
constructor() {
Object.defineProperty(this, "data", {
get() {
// 開銷大的操作(如資料請求)
const actualData = someExpensiveComputation()
Object.defineProperty(this, "data", {
value: actualData,
writable: false,
configurable: false
})
return actualData
},
// ①
configurable: true,
enumerable: true
})
}
}
這里①將 data 設定成可配置尤為重要,因為我們需要對 data 再次呼叫 Object.defineProperty()
const obj = new MyClass();
console.log(obj.hasOwnProperty("data")) // true
const data = obj.data;
console.log(obj.hasOwnProperty("data")) // true
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/294735.html
標籤:其他
