关于Vue的优化

388 阅读5分钟

代码优化使我们平时工作中比较容易忽视的一个点,这篇笔记就简单谈一谈vue代码层面的一些优化点

路由懒加载

  • 定义:懒加载简单来说就是延迟加载或按需加载,即在需要的时候的时候进行加载。

  • 作用:当路由被访问的时候才加载对应组件,这样更加高效

  • 使用:

    1.ES6 提出的import方法(最常用)

    const router = new VueRouter({
        routes: [
            {path: '/foo', component:() => import('./Foo.vue') }
        ]
    })
2.vue异步组件实现懒加载
    const router = new Router({
      routes: [
        {
          path: '/',
          name: 'HelloWorld',
          component: resolve=>(require(["@/components/HelloWorld"],resolve))
        }
      ]
    })

官方文档:router.vuejs.org/zh/guide/ad…

keep-alive缓存页面

  • 定义:keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 transition 相似,keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。

  • 作用:在组件切换过程中将状态保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。

  • 使用:

    1.缓存所有页面

    //App.vue
    <template>
      <div id="app">
      	<keep-alive>
          <router-view/>
        </keep-alive>
      </div>
    </template>
    

    2.根据特定条件缓存页面

    <template>
      <div id="app">
      	//1. 将缓存 name 为 test 的组件
      	<keep-alive include='test'>
          <router-view/>
        </keep-alive>
    	
    	//2. 将缓存 name 为 a 或者 b 的组件,结合动态组件使用
    	<keep-alive include='a,b'>
      	  <router-view/>
    	</keep-alive>
    	
    	//3. 使用正则表达式,需要使用 v-bind
    	<keep-alive :include='/a|b/'>
      	  <router-view/>
    	</keep-alive>	
    	
    	//4.动态判断
    	<keep-alive :include='includedComponents'>
      	  <router-view/>
    	</keep-alive>
    	
    	//5. 将不缓存 name 为 test 的组件
    	<keep-alive exclude='test'>
      	  <router-view/>
    	</keep-alive>
      </div>
    </template>
    

    3.结合vue-Router,缓存部分页面

    const router = new Router({
          routes: [
            {
              path: 'user',
              name: 'user',
              component: User,
              meta: {
            	keepAlive: true  // 需要缓存
          	  }
            },
            {
              path: 'list',
              name: 'list',
              component: List,
              meta: {
            	keepAlive: false  // 不需要缓存
          	 }
          ]
        })
        
    

    4.通过$route.meta来设置是否缓存

    <template>
      <div id="app">
      	<keep-alive>
          <router-view v-if="$route.meta.keepAlive"></router-view>
        </keep-alive>
        <router-view v-if="!$route.meta.keepAlive"></router-view>
      </div>
    </template>
    

参考资料:cn.vuejs.org/v2/api/#kee…

v-show复用DOM

这里要说明一个问题:什么时候用v-if什么时候用v-show

<template>
  <div class="cell">
  	<!--这种情况用v-show复用dom,比v-if好-->
    <div v-show="value" class="on">
    	<!--Heavy是一个比较繁重的dom元素-->
    	<Heavy :n="10000"/>
    </div>
    <div v-show="!value" class="off">
    	<Heavy :n="10000"/>
    </div>
  </div>
</template>

个人理解:如果需要很频繁的重复显示隐藏的dom就用v-show,因为v-show底层是使用的display来控制的显示隐藏,

而v-if是删除重新添加一个dom元素来控制显示隐藏,也是从dom优化角度来看,更为频繁的显示隐藏v-show更加合适。

并且如果该dom元素比较繁重庞大,也是使用v-show更加合适。

v-for遍历避免同时使用v-if

 <template>
     <ul>
         <li v-for="user in activeUsers"
         :key="user.id"
         >
         	{{user.name}}
         </li>
     </ul>
 </template>
<script>
export default {
  computed: {
    activeUsers:function () {
      return this.users.filter(function(user){
        return user.isActive
      })
    }
  }  
};
</script>

如果在v-for中需要进行if判断,建议做一个计算属性,将要显示的数组先进行一次筛选,只循环需要展示的数据,这样就避免了每次循环都进行if判断

​ 参考资料:cn.vuejs.org/v2/style-gu…

长列表性能优化

对长列表的展示情况进行判断,做不同的处理

  • 如果列表是纯粹的数据展示,不会有任何变化,就不需要做响应化

    export default {
      data: () => ({
        user: [],
      }),
      async created () {
        const users = await axiox.get('/api/users');
        //Object.freez进行冻结数据
        this.users = Object.freeze(users);
      }
    };
    
  • 如果是大数据长列表,可采用虚拟滚动,只渲染少部分区域的部分

    <recycle-scroller
          class="items"
          :items="items"
          :item-size="24"
        >
          <template v-slot="{item}">
            <FetchItemView
              :item="item"
              @vote="voteItem(item)"
            >
            </FetchItemView>
          </template>
      </recycle-scroller>
    

    参考资料:vue-virtual-scrollervue-virtual-scroll-list

事件的销毁

Vue组件销毁时,会自动解绑它的全部指令及事件监听器,但是仅限于组件本身的事件

 created () {
    this.timer = setInterval(this.refresh, 2000);
  },
  beforeDestroy () {
    clearInterval(this.timer)
  }

在这个组件设置了一个定时器,以函数式的方式绑定在了组件的created生命周期里面,建议在beforeDestroy中清除这个定时器,防止内存泄露

图片懒加载

对于图片过多的页面,为了优化页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载,等到滚动到可视区域后再去加载

<img v-lazy="/static/img/1.png">

参考插件:vue-lazyload

第三方插件按需引入

在实际开发中一些ui库,比如Ant Design 或者element UI这种很大的ui库时,可以按需引入,不必整个引入,减少负担

import Vue from 'vue'
import { Button, Select } from 'element-ui'

Vue.use(Button)
Vue.use(Select)

无状态的组件标记为函数式组件

把一些只是展示数据的组件设置为函数式组件,这类组件没有组件实例,在运行时消耗的资源会较小。

<template functional>
    <div class="cell">
        <div v-if="props.value" class="on"></div>
        <section v-else class="off"></section>
    </div>
</template>

export default {
  props: [value],
};

子组件分割

如果在子组件中有一些比较耗时的任务,那么就建议把它处理为独立的组件,让它自己渲染自己

<template>
  <div>
    <ChildComp/>
  </div>
</template>

export default {
  components:{
    ChildComp:{
      methods: {
        heavy() { /* 耗时任务 */}
      },
      render: (h) {
        return h('div',this.heavy())
      }
    }
  },
};

变量本地化

例子:

<template>
    <div :style="{opacity:start / 300}">
    	{{result}}
    </div>
</template>
<script>
import {heavy} from '@/utils'

export default {
  props: ['start'],
  computed: {
    base() {return 42},
    result(){
      const base = this.base //不要频繁引用this.base
      let result = this.start
      for (let i = 0; i < 1000; i++) {
        result += heary(base);
      }
      return result
    }
  },
};

</script> 

上面这种情况就是将base本地化 防止多次使用this.base访问计算属性

SSR

  • SSR是什么? 简单理解是将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序

  • SSR的服务端渲染

    优点:

  1. 更友好的SEO:

    由于搜索引擎爬虫抓取的工具可以直接查看完全渲染的页面

  2. 更利于首屏渲染

首屏的渲染是node发送过来的html字符串,并不依赖于js文件了,这就会使用户更快的看到页面的内容。尤其是针对大型单页应用,打包后文件体积比较大,普通客户端渲染加载所有所需文件时间较长,首页就会有一个很长的白屏等待时间。

缺点:

  1. 项目复杂度: 由于需要做node中间处理,需要更多文件处理分服务,和浏览端文件

  2. 学习成本相对较高

    除了对webpack、vue要熟悉,还需要掌握node、Koa2等相关技术。相对于客户端渲染,项目构建、部署过程更加复杂。

  3. 服务端压力较大

    本来是通过客户端完成渲染,现在统一到服务端node服务去做。尤其是高并发访问的情况,会大量占用服务端CPU资源;

  4. 开发条件受限

    在服务端渲染中,只会执行到componentDidMount之前的生命周期钩子,因此项目引用的第三方的库也不可用其它生命周期钩子,这对引用库的选择产生了很大的限制;

  • 应用场景:
    1. 单页适用场景:后台管理页面,对seo要求不高的网页
    2. SSR适用场景: 官网以及广告页,等需要很快速的看到效果的页面