• 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提升性能。

  • 相关阅读:
    export和import实现模块化
    Net Core
    DockerCon 2016
    NET Core 构成体系
    Cloud Engine
    JVM内存结构
    Signalr
    Rabbit.Rpc
    遍历指定包名下所有的类(支持jar)(转)
    httpd的简单配置(转)
  • 原文地址:https://www.cnblogs.com/goloving/p/13969685.html
Copyright © 2020-2023  润新知