pocket-manual icon indicating copy to clipboard operation
pocket-manual copied to clipboard

Vue 首屏渲染优化

Open FishPlusOrange opened this issue 7 years ago • 0 comments

优化首屏渲染速度对提升用户体验有很多大的帮助,尤其是 SPA 项目。以下介绍一下本人在做 Vue SPA 项目过程中尝试过的首屏渲染优化方法。

异步组件

异步组件可以理解为组件的懒加载,在大型应用中,我们可以将应用发分割成小的代码块,只有在需要的时候才从服务器加载一个模块。Vue 提供了相关的支持:

为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

业务组件为例(基于 Vue CLI,使用局部注册,以下例子同):

<script>
export default {
  components: {
    AsyncComponent: (resolve, reject) => {
      setTimeout(() => { // setTimeout 模拟请求延时
        // resolve 回调接收组件定义
        resolve({
          template: '<div>AsyncComponent</div>'
        })
      }, 250)
    }
  }
}
</script>

以上代码中,AsyncComponent组件对应的工厂函数接收一个 resolve 回调,该回调在从服务器得到组件时被调用。

对于组件的获取可以使用以下方式:

  • 搭配 Webpack Code Splitting
<script>
export default {
  components: {
    AsyncComponent: resolve => require(['./AsyncComponent'], resolve)
  }
}
</script>

以上代码中,require 告诉 webpack 在项目构建时将 AsyncComponent 组件单独切割成一个包,在需要被渲染时,再通过 AJAX 请求异步加载。

  • 搭配 ES6 import

由于工厂函数接收一个 resolve 回调,所以我们也可以在工厂函数中返回一个 promise

<script>
export default {
  components: {
    AsyncComponent: () => import('./AsyncComponent')
  }
}
</script>

以上代码中,import 返回一个 promise,故还可以和 async/await 一起使用:

<script>
export default {
  components: {
    AsyncComponent: async () => await import('./AsyncComponent')
  }
}
</script>

除了业务组件,路由组件也支持懒加载。我们可以把不同路由对应的组件切割成不同的代码块,在路由被访问时,再加载相应组件。示例代码如下:

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

const router = new Router({
  routes: [
    {
      path: '/',
      component: resolve => require(['./AsyncComponent'], resolve)
    }
  ]
})

第三方框架按需引入

一般我们在使用第三方框架时都是在入口文件中直接全部引入,比如 iView:

// vue CLI @/main.js
import Vue from 'vue'
import iView from 'iview'
import 'iview/dist/styles/iview.css'

Vue.use(iView)

// ...

但是,在开发过程中,我们只是使用其中一部分组件,这时候我们可以通过按需引入,以减少文件体积,从而提高首屏渲染速度。

比如 iView,借助插件 babel-plugin-import 就可以实现按需加载组件:

# 安装 babel-plugin-import
npm install babel-plugin-import --save-dev
// 配置 .babelrc
{
  "plugins": [["import", {
    "libraryName": "iview",
    "libraryDirectory": "src/components"
  }]]
}
// 在项目中按需引入
import { Button, Table } from 'iview'
Vue.component('Button', Button)
Vue.component('Table', Table)

其他框架配置方式大同小异。

开启 GZIP

开启 GZIP 压缩资源可以减少传输文件体积,由浏览器接收到资源后再进行相应解压解析,从而提高网络传输速度,进而提高首屏渲染速度。

如果项目是基于 Vue CLI 搭建的,可以在默认配置文件中开启 GZIP:

// config/index.js
module.exports = {
  // ...
  build: {
    // ...
    productionGzip: true, // 设置为 true
    productionGzipExtensions: ['js', 'css'],
    // ...
  }
}

在把 productionGzip 设置成 true 之前记得先安装相关依赖:

npm install --save-dev compression-webpack-plugin

详细配置在 build/webpack.prod.conf.js

// ...

if (config.build.productionGzip) {
  const CompressionWebpackPlugin = require('compression-webpack-plugin')

  webpackConfig.plugins.push(
    new CompressionWebpackPlugin({
      asset: '[path].gz[query]',
      algorithm: 'gzip',
      test: new RegExp(
        '\\.(' +
        config.build.productionGzipExtensions.join('|') +
        ')$'
      ),
      threshold: 10240,
      minRatio: 0.8
    })
  )
}

// ...

除了修改 Webpack 配置以外,还需修改服务器配置以开启 GZIP 支持。如果 Response HeadersContent-Encoding 字段为 gzip 则表示服务器支持 GZIP。

服务器支持 GZIP 的方式大概有两种:

  1. 前端打包时即生成 .gz 文件,浏览器请求某一个 JS 文件时,服务器返回对于的 .gz 文件。

  2. 浏览器请求某一个 JS 文件时,服务器对该 JS 文件进行 GZIP 压缩后返回给浏览器。

其他

这里推荐一个 Webpack 包分析神器:webpack-bundle-analyzer

如果项目是基于 Vue CLI 搭建的,已经默认配置了 webpack-bundle-analyzer,只需在打包时添加参数即可启动:

npm run build --report

详细配置在 build/webpack.prod.conf.js

// ...

if (config.build.bundleAnalyzerReport) {
  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}

// ...

参考:

FishPlusOrange avatar Aug 14 '18 14:08 FishPlusOrange