Vue 前端项目性能优化

527 阅读8分钟

前言

  • Vue 是一个渐进式的 javascript 开发框架,通过数据双向绑定和虚拟 DOM 使我们前端小伙伴在开发过程中无需考虑如何高效的来操作 DOM,大大提高了我们的开发效率
  • 但是项目中仍然存在首屏加载优化、webpack 编译配置优化等问题,所以我们仍然需要去关注 Vue 项目性能方面的优化,使项目具有更高效的性能、更好的用户体验
  • 本文内容分以下三部分组成
    • Vue 代码层面的优化;
    • webpack 配置层面的优化;
    • 基础的 Web 技术层面的优化;

一、Vue 代码层面的优化

v-show 和 v-if 的使用

  • 区别:
    • v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建
    • v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块
    • 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行 display 属性切换控制显示隐藏
  • 使用场景:
    • 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销
    • 因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好比如管理系统的权限列表的展示

computed 和 watch 的使用

  • 区别:
    • computed 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有依赖它的属性的值发生变化,下一次获取 computed 的值才会重新计算
    • watch 更多的是观察作用,类似于某些数据的监听回调,当监听的数据变化时会执行回调进行后续操作
  • 使用场景:
    • 当我们需要数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免了每次获取值都需要重新计算
    • 当我们需要在某个数据变化时做一些事情,使用 watch 来观察这个数据变化

v-for 遍历必须为 item 添加 key 属性,且避免同时使用 v-if

  • 在列表数据进行渲染时,需要为每一项 item 设置唯一的 key 值,方便 vue 内部机制更准确、更快速的找到该条数据 更多 key 的知识总结请戳这里。。。
  • v-for 比 v-if 的优先级要高,如果每次都需要遍历一整个数组将会影响渲染的速度,尤其是很大的 list 筛选出几条数据的时候,必要的情况下可以用 computed 来替换
    • 过滤后的列表只会在 users 数组发生相关变化时才被重新运算,过滤更高效
    • 使用 v-for="user in activeUsers" 之后,我们在渲染的时候只遍历活跃用户,渲染更高效
    • 解藕渲染层的逻辑,可维护性 (对逻辑的更改和扩展) 更强
// 推荐
<ul>
  <li
    v-for="user in activeUsers"
    :key="user.id">
    {{ user.name }}
  </li>
</ul>
computed: {
  activeUsers: function () {
    return this.users.filter(function (user) {
	 return user.isActive
    })
  }
}
// 不推荐
<ul>
  <li
    v-for="user in users"
    v-if="user.isActive"
    :key="user.id">
    {{ user.name }}
  </li>
</ul>

长列表性能的优化

  • Vue 会通过 Object.defineProperty 对数据进行劫持,来进行双向绑定实现视图响应数据的变化
  • 有些时候我们的组件就是纯粹的展示功能不涉及任何的改变,不需要劫持操作,在数据量很大的情况下可以明显的减少组件初始化的时间
  • 通过 Object.freeze 方法来冻结一个对象,来达到我们的目的
export default {
  data: () => ({
    users: {}
  }),
  async created() {
    const users = await axios.get("/api/users");
    this.users = Object.freeze(users);
  }
};

事件的销毁

  • Vue 组件销毁时会自动与其它实例的连接,解绑它的全部指令以及事件监听器,但这仅限于组件本身的事件
  • 在 js 内使用 addEventListener 注册的监听是不会自动销毁的,我们需要手动移除这些事件的监听,以免造成内存泄漏
created() {
  addEventListener('click', this.click, false)
},
beforeDestroy() {
  removeEventListener('click', this.click, false)
}

vue-lazyload 实现图片懒加载

  • 为了加速页面的加载速度,我们需要将未出现在可视区域内的图片先不做加载,等滚动到可视区域后在进行加载,这样也可以有效提高用户的体验
  • 在 vue 文件中将 img 标签的 src 属性直接改为 v-lazy ,从而将图片显示方式更改为懒加载显示
// 安装 
npm install vue-lazyload --save-dev
// 引入并使用
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
// 指令替换
<img v-lazy="/static/img/1.png">
// 添加自定义选项
Vue.use(VueLazyload, {
    preLoad: 1.3,
    error: 'dist/error.png',
    loading: 'dist/loading.gif',
    attempt: 1
})

路由懒加载

  • 在 SPA 单页应用中会有很多的路由引入,导致 webpack 打包后的文件很大,进入首页的时候加载的资源过多页面出现白屏情况,不利于用户体验
  • 需要将不同路由对应的组件分割成不同的代码块,当路由被访问的时候才加载对应的组件,大大提升首页的加载速度,提高用户体验
const Foo = () => import('./Foo.vue')
const router = new VueRouter({
  routes: [
    { path: '/foo', component: Foo }
  ]
})

第三方插件的按需引入

  • 借助 babel-plugin-component 只引入需要的组件,减小项目的体积
// 安装
npm install babel-plugin-component -D
// 配置
{
  "presets": [["es2015", { "modules": false }]],
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}
// 使用
import Vue from 'vue';
import { Button, Select } from 'element-ui';

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

优化无限列表性能

  • 如果项目中的列表数据量非常大我们需要渲染少部分区域的内容,减少渲染和 dom 创建的时间
  • 推荐参考以下开源项目 vue-virtual-scroll-listvue-virtual-scroller 来优化这种无限列表的场景

SSR 服务端渲染、预渲染

  • 服务端渲染是指 Vue 在客户端将标签渲染成的整个 html 片段的工作在服务端完成,服务端形成的 html 片段直接返回给客户端这个过程就叫做服务端渲染
    • 首屏加载更快:SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到达时间;
    • 更好的 SEO:因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好的页面;
    • 更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;
    • 更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源,因此如果你预料在高流量环境下使用,请准备相应的服务器负载,并明智地采用缓存策略
  • 预渲染,在构建时 (build time) 简单地生成针对特定路由的静态 HTML 文件。优点是设置预渲染更简单,并可以将你的前端作为一个完全静态的站点,具体你可以使用 prerender-spa-plugin 就可以轻松地添加预渲染
  • 两者的区别和联系:
    • 用户请求前的服务器渲染即为「预渲染」
    • 用户请求后的服务器渲染即为「服务端渲染」
    • 预渲染不像服务器渲染那样即时编译 HTML,它只在构建时为了特定的路由生成特定的几个静态页面,等于我们可以通过 Webpack 插件将一些特定页面组件 build 时就编译为 html 文件,直接以静态资源的形式输出给搜索引擎
    • 预渲染不执行 js,只适应于纯静态页面

webpack 配置层面的优化

  • 每部分详细内容亟待整理,敬请期待。。。

压缩代码体积

缩小打包作用域

提取页面公共资源

基础包的分离

充分利用缓存提升二次构建速度

Tree shaking

Scope Hoisting


基础的 Web 技术层面的优化

  • 每部分详细内容亟待整理,敬请期待。。。

开启 gzip 压缩

浏览器缓存

CDN 的使用

使用 Chrome Performance 查找性能瓶颈


回到顶部

回到顶部