美文网首页
vue性能优化

vue性能优化

作者: 37手游技术部 | 来源:发表于2021-07-06 14:28 被阅读0次

    文章目录

    1.代码优化

    2.项目优化

    3.其它优化

    4.总结

    本文主要针对的是 vue 2.x 版本的性能优化,并且从代码优化 和 其他的优化去讲下在项目开发时应该注意的优化事项。

    首先从项目代码层面方面

    1.代码优化

    v-if / v-show

    这两个指令在不同场景的使用下,会有不同的性能损耗情况,首先简单了解下两者的区别。

    • v-if 指令在编译阶段就会编译成一个三元运算符,通过条件进行渲染。当条件的值变化时,会触发对应的组件更新,即会经过 diff 算法, 组件初始化、渲染 vnode、patch等过程。
    • v-show 指令相比于 v-if 的优势就是它在更新阶段仅仅更新了 DOM 的显隐,少去了很多性能开销的操作。但是初始化的时候会把所有条件节点都渲染出来
    <!-- v-if -->
    <template>
        <section>
        <p v-if="props.value">Hello World!</p>
        <p v-else>Vue yyds</p>
      </section>
    </template>
    
    <!-- v-show -->
    <template>
        <section>
        <p v-show="props.value">Hello World!</p>
        <p v-show="!props.value">Vue yyds</p>
      </section>
    </template>
    
    • 使用场景:

    初始化阶段: v-if 性能优于 v-show
    频繁更新阶段: v-show 性能优于 v-if

    v-for 和 v-if

    避免把这两个指令放在同一个节点

    因为 v-for 指令的优先级比 v-if 高,所以两个指令混用的话会导致每次节点render的结果都带上了条件渲染。当数据量和节点复杂的时候,就会有明显的性能差。

    当我们要循环节点和条件判断的时候,我们可以先对数据进行处理后再进行 v-for 渲染,如下例子:

    <!-- bad -->
    <div v-for="item in list" v-if="item.count > 10" :key="item.id">{{item.name}}</div>
    
    <!-- good -->
    <div v-for="item in filters" :key="item.id">{{item.name}}</div>
    
    computed: {
      filters () {
        return this.list.filter(item => item.count > 10)
      }
    }
    

    从上面例子不难看出,每个 v-for 节点处就加了 key 唯一标识,这也是提升性能的一个小点。使用 key 时,vue会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

    keep-alive

    部分场景可以使用 keep-alive 进行组件缓存

    keep-alive 包裹的组件在经过渲染后的 vnode 以及 DOM 都会被缓存起来,然后下次再次渲染该组件的时,直接从缓存中拿到对应的 vnode 和 DOM 并且进行渲染,并不需要再走一次组件初始化,render 和 patch 等一系列流程,减少了 script 的执行时间,性能更好。

    v-slot:slotName / slot="slotName"

    vue 2.6 版本,可以使用 v-slot:slotName 的语法代替 slot="slotName"

    • 旧的写法在更新时多了一个父组件更新的过程,而新的写法由于直接更新子组件,就会更加高效,性能更好,所以推荐始终使用语法 v-slot:slotName
    函数式组件

    vue 2.x 的版本,需要DOM层复用的情况且场景相对简单时,可以用 函数式组件 代替普通组件

    函数式组件生成的是普通的 vnode,不会存在递归子组件的过程,所以会减少一定程度的性能开销

    场景:我在多个页面都复用了一个页面渲染的组件,不需要一些复杂的操作,只需要根据传参进行条件渲染或者循环渲染等、就可以用如下的例子去进行组件的编写

    <!-- 函数式组件 -->
    <template functional>
      <section>
        <div v-if="props.value">Hello World</div>
        <div v-else>Hi~</div>
        <ul>
          <li v-for="item in props.list" :key="item.id">{{item.name}}</li>
        </ul>
      </section>
    </template>
    
    子组件拆分

    除开代码维护的层面,从性能方面,组件的拆分也是有好处的。

    • 因为vue 的更新是组件力度,如果组件中有数据发生变化,就会执行 render 函数生成新的 vnode 和旧的 vnode 进行 diff。哪怕这个数据只影响到一个元素渲染,其他元素也需要在 diff 过程中进行比较。极端情况是假设这个大组件里面有个倒计时。
    • 如果组件拆分得当的话,大部分的数据改动只会影响到子组件本身进行 diff ==> 渲染。所以合理得对这种大组件进行拆分,应用的更新效率会更高。

    例如:

    <!-- 拆分前 -->
    <template>
      <div :style="{ opacity: number / 300 }">
        <div>{{ heavy() }}</div>
      </div>
    </template>
    
    <script>
    export default {
      props: ['number'],
      methods: {
        heavy () {
          const n = 100000
          let result = 0
          for (let i = 0; i < n; i++) {
            result += Math.sqrt(Math.cos(Math.sin(42)))
          }
          return result
        },
      },
    }
    </script>
    
    
    <!-- 拆分后 -->
    <template>
      <div :style="{ opacity: number / 300 }">
        <ChildComp/>
      </div>
    </template>
    
    <script>
    export default {
      components: {
        ChildComp: {
          methods: {
            heavy () {
              const n = 100000
              let result = 0
              for (let i = 0; i < n; i++) {
                result += Math.sqrt(Math.cos(Math.sin(42)))
              }
              return result
            },
          },
          render (h) {
            return h('div', this.heavy())
          },
        },
      },
      props: ['number'],
    }
    </script>
    
    响应式数据优化
    data的优化

    vue 在组件实例初始化的时候会对data进行响应式处理,能够减少一个数据就是减少一点点的性能开销。而且一些常量数据不应该在data里面进行定义,简单总结为以下三点:

    • 减少无用的data数据

    • 减少数据被observer

    • 数据尽量扁平化

    局部变量缓存响应式数据

    先直接看对比代码

    // 优化前
    // ··········
    computed: {
      base () {
        return 42
      },
        result () {
          let result = this.start
          for (let i = 0; i < 1000; i++) {
            result += Math.sqrt(Math.cos(Math.sin(this.base))) + this.base * this.base + this.base + this.base * 2 + this.base * 3
          }
          return result
        },
    }
    
    // 优化后
    // ·········
    computed: {
      base () {
        return 42
      },
        result ({ base, start }) {
          let result = start
          for (let i = 0; i < 1000; i++) {
            result += Math.sqrt(Math.cos(Math.sin(base))) + base * base + base + base * 2 + base * 3
          }
          return result
        },
    }
    

    优化前每次调用 this.base 的时候,由于它是个响应式数据,每次都会调用都会触发它的 getter,进而执行依赖收集等逻辑。

    优化后先把 this.base 缓存到局部变量,后面重复调用的时候就不会频繁的触发到 getter 的逻辑处理

    computed

    计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 数据 还没有发生改变,多次访问 计算属性会立即返回之前的计算结果,可以极大提升性能。

    例如:

    <template>
      <div>{{count}}</div>
    </template>
    <script>
      export default {
        computed: {
          count () {
          let result
          // 复杂的计算操作
          return result
          }
        }
      }
    </script>
    

    2.项目优化

    路由懒加载

    在vue-router的配置文件里,定义一个能够被 Webpack 自动代码分割的异步组件

    const Hello = () => import('//hello.vue')
    const router = new VueRouter({
      routes: [
        { path: '/hello', component: Hello },
        { path: '/world', component: import('//world.vue') }
      ]
    })
    
    组件按需加载

    element-ui 为例子,有些项目用ui组件库的时候使用的并不多,可以按需加载适当优化项目的体积

    import {Button} from 'element-ui'
    
    Vue.use(Button)
    

    3.其它优化

    1.图片懒加载
    2.节流 / 防抖
    3.长列表的虚拟滚动

    4.总结

    1.减少没必要的渲染机制
    2.减少全量加载,适当的懒加载
    3.正确的使用vue的每个api,本身vue这个框架就对性能方面做了很多处理,正常使用就不会出现性能瓶颈

    相关文章

      网友评论

          本文标题:vue性能优化

          本文链接:https://www.haomeiwen.com/subject/yqbbultx.html