vue利用 object.freeze 提升列表渲染性能


一、概述

  我們應該都知道 vue會通過 object.defineProperty 對數據進行劫持,來實現視圖響應數據的變化,然而有些時候我們的組件就是純粹的數據展示,不會有任何改變,我們就不需要 vue來劫持我們的數據,在大量數據展示的情況下,這能夠很明顯的減少組件初始化的時間,那如何禁止 vue 劫持我們的數據呢?可以通過 object.freeze方法來凍結一個對象,一旦被凍結的對象就再也不能被修改了。

Object.freeze() 方法可以凍結一個對象。一個被凍結的對象再也不能被修改;凍結了一個對象則不能向這個對象添加新的屬性,不能刪除已有屬性,不能修改該對象已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結一個對象后該對象的原型也不能被修改。freeze() 返回和傳入的參數相同的對象。

  比方我們需要渲染一個非常大的數組對象,例如用戶列表,對象列表,文章列表等等。

  vue 會將 data 對象中的所有的屬性加入到 vue 的響應式系統中,當這些屬性的值發生改變時,視圖將會產生響應,若對象的體積比較大,會消耗很多瀏覽器解析時間。所以我們可以通過減少數據的響應式轉換來提供前端的性能

  另外需要說明的是:這里只是凍結了 users 的值,引用不會被凍結,所以當我們需要更新數據的時候,我們可以重新給 users賦值,即更改其引用,那么視圖就會更新。

export default { data: () => ({ users: {} }), async created() { const users = await axios.get("/api/users"); // this.users = users; this.users = Object.freeze(users);
} };

二、object.freeze 定義

  在 Vue 的文檔中介紹數據綁定和響應時,特意標注了對於經過 Object.freeze() 方法的對象無法進行更新響應。數據與方法。

  Object.freeze() 方法用於凍結對象,禁止對於該對象的屬性進行修改(由於數組本質也是對象,因此該方法可以對數組使用)。在 Mozilla MDN 中是如下介紹的:

可以凍結一個對象。一個被凍結的對象再也不能被修改;凍結了一個對象則不能向這個對象添加新的屬性,不能刪除已有屬性,不能修改該對象已有屬性的可枚舉性、可配置性、可寫性,以及不能修改已有屬性的值。此外,凍結一個對象后該對象的原型也不能被修改

  該方法的返回值是其參數本身。需要注意的是以下兩點:

1、Object.freeze() 和 const 變量聲明不同,也不承擔 const 的功能。

  const和Object.freeze()完全不同

  • const的行為像 let。它們唯一的區別是, const定義了一個無法重新分配的變量。 通過 const聲明的變量是具有塊級作用域的,而不是像 var聲明的變量具有函數作用域。
  • Object.freeze()接受一個對象作為參數,並返回一個相同的不可變的對象。這就意味着我們不能添加,刪除或更改對象的任何屬性。
  • const和Object.freeze()並不同,const是防止變量重新分配,而Object.freeze()是使對象具有不可變性
   關於不可變對象可以看之前寫的這篇博客:JavaScript 中的不可變對象(Immutable Objects),需要注意的是 object.freeze() 是“淺凍結”,需要做到“深凍結”完全凍結具有嵌套屬性的對象,您可以編寫自己的庫或使用已有的庫來凍結對象,如Deepfreezeimmutable-js
// 深凍結函數.
function deepFreeze(obj) { // 取回定義在obj上的屬性名
  var propNames = Object.getOwnPropertyNames(obj); // 在凍結自身之前凍結屬性
 propNames.forEach(function(name) { var prop = obj[name]; // 如果prop是個對象,凍結它
    if (typeof prop == 'object' && prop !== null) deepFreeze(prop); }); // 凍結自身(no-op if already frozen)
  return Object.freeze(obj); }

  其實就是個簡單的遞歸方法。但是涉及到一個很重要,但是在寫業務邏輯的時候很少用的知識點 Object.getOwnPropertyNames(obj) 。我們都知道在 JS 的 Object 中存在原型鏈屬性,通過這個方法可以獲取所有的非原型鏈屬性。

三、利用Object.freeze()提升性能原理

  除了組件上的優化,我們還可以對vue的依賴改造入手。初始化時,vue會對data做getter、setter改造,在現代瀏覽器里,這個過程實際上挺快的,但仍然有優化空間。

  當你把一個普通的 JavaScript 對象傳給 Vue 實例的  data  選項,Vue 將遍歷此對象所有的屬性,並使用  Object.defineProperty  把這些屬性全部轉為 getter/setter,這些 getter/setter 對用戶來說是不可見的,但是在內部它們讓 Vue 追蹤依賴,在屬性被訪問和修改時通知變化。

  但 Vue 在遇到像 Object.freeze() 這樣被設置為不可配置之后的對象屬性時,不會為對象加上 setter getter 等數據劫持的方法。參考 Vue 源碼

1、性能提升效果對比

  在基於 Vue 的一個big table benchmark里,可以看到在渲染一個一個 1000 x 10 的表格的時候,開啟Object.freeze() 前后重新渲染的對比。

  開啟優化之前

  開啟優化之后

  在這個例子里,使用了 Object.freeze()比不使用快了 4 倍

2、為什么 Object.freeze() 的性能會更好

  不使用 Object.freeze() 的CPU開銷

  使用 Object.freeze()的CPU開銷

  對比可以看出,使用了 Object.freeze() 之后,減少了 observer 的開銷。

3、Object.freeze()應用場景

  由於 Object.freeze()會把對象凍結,所以比較適合展示類的場景,如果你的數據屬性需要改變,可以重新替換成一個新的 Object.freeze()的對象

四、Javascript對象解凍

  修改 props 生成的對象是不能修改props的, 但實踐中遇到需要修改props的情況。如果直接修改, js代碼將報錯, 原因是props對象被凍結了, 可以用Object.isFrozen()來檢測, 其結果是true,說明該對象的屬性是只讀的。

  那么, 有方法將props對象解凍,從而進行修改嗎?

  事實上, 在javascript中, 對象凍結后, 沒有辦法再解凍, 只能通過克隆一個具有相同屬性的新對象, 通過修改新對象的屬性來達到目的。

// 可以這樣
ES6: Object.assign({}, frozenObject); lodash: _.assign({}, frozenObject);

  來看實際代碼

function modifyProps(component) { let condictioin = this.props.condictioin, newComponent = Object.assign({}, component), newProps = Object.assign({}, component.props) if (condictioin) { if (condictioin.add) newProps.add = true
    if (condictioin.del) newProps.del = true } newComponent.props = newProps return newComponent }

  鎖定對象的方法

  • Object.preventExtensions()

  no new properties or methods can be added to the project 對象不可擴展, 即不可以新增屬性或方法, 但可以修改/刪除

  • Object.seal()

  same as prevent extension, plus prevents existing properties and methods from being deleted 在上面的基礎上,對象屬性不可刪除, 但可以修改

  • Object.freeze()

  same as seal, plus prevent existing properties and methods from being modified 在上面的基礎上,對象所有屬性只讀, 不可修改

  以上三個方法分別可用Object.isExtensible(), Object.isSealed(), Object.isFrozen()來檢測

五、總結

  Object.freeze()是ES5新增的特性,可以凍結一個對象,防止對象被修改。

  vue 1.0.18+對其提供了支持,對於data或vuex里使用freeze凍結了的對象,vue不會做getter和setter的轉換。如果你有一個巨大的數組或Object,並且確信數據不會修改,使用Object.freeze()可以讓性能大幅提升。

  在我的實際開發中,這種提升大約有5~10倍,倍數隨着數據量遞增。並且,Object.freeze()凍結的是值,而不是引用,所以你仍然可以將變量的引用替換掉來達到更新視圖的目的。

  vue的文檔沒有寫上這個特性,但這是個非常實用的做法,對於純展示的大數據,都可以使用Object.freeze提升性能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM