Vue性能提高之Object.freeze()

在 Vue 的文檔中介紹數據綁定和響應時,特地標註了對於通過 Object.freeze() 方法的對象沒法進行更新響應。所以,特地去查了 Object.freeze() 方法的具體含義。javascript

含義

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

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

該方法的返回值是其參數自己。java

須要注意的是如下兩點git

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

    const和Object.freeze()徹底不一樣vuex

  • const的行爲像 let。它們惟一的區別是, const定義了一個沒法從新分配的變量。 經過 const聲明的變量是具備塊級做用域的,而不是像 var聲明的變量具備函數做用域。
  • Object.freeze()接受一個對象做爲參數,並返回一個相同的不可變的對象。這就意味着咱們不能添加,刪除或更改對象的任何屬性。
  • const和Object.freeze()並不一樣,const是防止變量從新分配,而Object.freeze()是使對象具備不可變性。

如下代碼是正確的:數組

  1. Object.freeze() 是「淺凍結」,如下代碼是生效的:

實例

常規用法瀏覽器

明顯看到,a 的 prop 屬性未被改變,即便從新賦值了。bash

延伸

"深凍結"

要徹底凍結具備嵌套屬性的對象,您能夠編寫本身的庫或使用已有的庫來凍結對象,如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改造,在現代瀏覽器裏,這個過程實際上挺快的,但仍然有優化空間。

Object.freeze() 能夠凍結一個對象,凍結以後不能向這個對象添加新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該對象已有屬性的可枚舉性、可配置性、可寫性。該方法返回被凍結的對象。

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

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

Vue observer 源碼

性能提高效果對比

在基於 Vue 的一個 big table benchmark 裏,能夠看到在渲染一個一個 1000 x 10 的表格的時候,開啓Object.freeze() 先後從新渲染的對比。

big table benchmark

開啓優化以前

開啓優化以後

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

爲何Object.freeze() 的性能會更好

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

使用 Object.freeze()的CPU開銷

對比能夠看出,使用了 Object.freeze() 以後,減小了 observer 的開銷。

Object.freeze()應用場景

因爲 Object.freeze()會把對象凍結,因此比較適合展現類的場景,若是你的數據屬性須要改變,能夠從新替換成一個新的 Object.freeze()的對象。

Javascript對象解凍

修改 React props React生成的對象是不能修改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( ) 阻止Vue沒法實現 響應式系統

當一個 Vue 實例被建立時,它向 Vue 的響應式系統中加入了其 data 對象中能找到的全部的屬性。當這些屬性的值發生改變時,視圖將會產生「響應」,即匹配更新爲新的值。可是若是使用 Object.freeze(),這會阻止修改現有的屬性,也意味着響應系統沒法再追蹤變化。

具體使用辦法舉例:

<template>
  <div>
     <p>freeze後會改變嗎
        {{obj.foo}}
     </p>
      <!-- 兩個都不能修改??爲何?第二個理論上應該是能夠修改的-->
      <button @click="change">點我確認</button>
  </div>
</template>

<script>
var obj = {
  foo: '不會變'
}
Object.freeze(obj)
export default {
  name: 'index',
  data () {
    return {
      obj: obj
    }
  },
  methods: {
    change () {
      this.obj.foo = '改變'
    }
  }
}
</script>
複製代碼

運行後:

從報錯能夠看出只讀屬性foo不能進行修改,Object.freeze()凍結的是值,你仍然能夠將變量的引用替換掉,將上述代碼更改成:

<button @click="change">點我確認</button>

change () {
      this.obj = {
        foo: '會改變'
      }
    }
複製代碼

Object.freeze()是ES5新增的特性,能夠凍結一個對象,凍結指的是不能向這個對象添加新的屬性,不能修改其已有屬性的值,不能刪除已有屬性,以及不能修改該對象已有屬性的可枚舉性、可配置性、可寫性。防止對象被修改。 若是你有一個巨大的數組或Object,而且確信數據不會修改,使用Object.freeze()可讓性能大幅提高。

實踐心得和技巧

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

vue 1.0.18+對其提供了支持,對於data或vuex裏使用freeze凍結了的對象,vue不會作getter和setter的轉換。

若是你有一個巨大的數組或Object,而且確信數據不會修改,使用Object.freeze()可讓性能大幅提高。在個人實際開發中,這種提高大約有5~10倍,倍數隨着數據量遞增。

而且,Object.freeze()凍結的是值,你仍然能夠將變量的引用替換掉。舉個例子:

<p v-for="item in list">{{ item.value }}</p>
複製代碼
new Vue({
    data: {
        // vue不會對list裏的object作getter、setter綁定
        list: Object.freeze([
            { value: 1 },
            { value: 2 }
        ])
    },
    created () {
        // 界面不會有響應
        this.list[0].value = 100;

        // 下面兩種作法,界面都會響應
        this.list = [
            { value: 100 },
            { value: 200 }
        ];
        this.list = Object.freeze([
            { value: 100 },
            { value: 200 }
        ]);
    }
})
複製代碼

vue的文檔沒有寫上這個特性,但這是個很是實用的作法,對於純展現的大數據,均可以使用Object.freeze提高性能。

相關文章
相關標籤/搜索