这是一篇对以往工作的一个整理小结,网上相关的都可以查到,主要方便自己日后查阅。

我们在进行项目打包的时候,如果文件大小过大,会抛出如下警告:

asset size limit: The following asset(s) exceed the recommended size limit (244 KiB). This can impact web performance. Assets: js/chunk-vendors.1e3142a4.js (500 KiB)

通常首次请求中包含 chunk-vendor.***.js 这类的文件,而该文件往往是很大的。这也是 vue spa 下首屏加载慢的原因之一。

使用 bundle-analyzer-plugin 进行可视化分析

安装插件yarn add -D webpack-bundle-analyzer

我使用的是 vue-cli3,所以在 vue.config.js 中配置:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
    chainWebpack: config => {
        config.plugin('webpack-bundle-analyzer')
            .use(new BundleAnalyzerPlugin({
            	openAnalyzer: false	// 设置为 false,则不自动在浏览器中打开结果
            }))
    }
};

其他选项可自行去项目网址查阅。

配置好插件后,无论在开发环境 yarn serve 还是生产环境 yarn build 打包,都会自动运行该分析工具。两种模式下,分析的 chunk 大小是不同的,开发模式下比较大,因为没有经过优化。个人倾向于分析生产环境的打包结果。在浏览器中打开 8888 端口就可以看到分析打包文件的结果图:

打包分析结果图

通过分析图,可以直观看出来打包的代码,哪些地方是还可以优化的。

关闭 productionSourceMap

vue.config.js 文件中配置

module.exports = {
  productionSourceMap: false
}

若不设置 productionSourceMap: false,则打包时会生成一堆 map 文件,map 文件好比对源码进行标记跟踪,当项目运行报错时,会输出报错信息和错误代码的位置。建议生产环境关闭,可以有效减少整体文件大小,同时避免源码暴露风险。

关闭 Prefetch

prefetch 在 vue-cli3 中是默认开启的,它会在浏览器页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。当我们访问首屏时,会看到除了默认请求 app.****.js 文件,还会加载若干个 chunk,其中有一些是暂时还用不到的其他页面的路由文件。

vue-cli3 prefetch

遵循官方的方法,配置 vue.config.js,下面两种二选一:

module.exports = {
  chainWebpack: config => {
    // 移除 prefetch 插件
    config.plugins.delete('prefetch')
    // 修改它的选项:
    config.plugin('prefetch').tap(options => {
      options[0].fileBlacklist = options[0].fileBlacklist || []
      options[0].fileBlacklist.push(/myasyncRoute(.)+?\.js$/)
      return options
    })
  }
}

再次测试进行对比,预加载文件少了很多:

vue-cli3 prefetch关闭

其实这些文件本身都不大,小则几 KB,但请求数多了会对服务器造成一定的压力。如果你想有更多的灵活性的话,关闭prefetch 后也可以手动通过 webpack 的内联注释进行预加载。

import(/* webpackPrefetch: true */ './someAsyncComponent.vue')

webpack 的运行时会在父级区块被加载之后注入 prefetch 链接。

路由懒加载

路由懒加载是最常用的方法,它将不同路由对应的组件代码分割成不同的文件,打包后对应的代码块会从最后生成的app.****.js 文件中剥离出来,只有相应的路由被访问时才进行请求加载。

路由懒加载的语法如下:

export default {
    path: '/projects',
    name: 'route-projects',
    component: () => import('@/views/Projects')	// 路由懒加载
}

按需加载组件

很多前端 UI 框架库都给我们提供了按需加载组件这一方法,与全部导入相比,按需加载只会导入我们用的组件。前者虽然在开发时比较方便,但当我们实际部署时,为了减小打包后文件的大小,就不得不考虑这一问题。

一般都要先安装插件 yarn add -D babel-plugin-component,后面的配置根据不同框架略有不同。这里不展开。

我用的是 bootstrap-vue,它提供以插件组件两种方式导入,插件稍微方便点,会将一组相关的所有组件一并导入,组件则更为精确。测试了一下,将插件导入换成组件导入后,共减少了 100 多 KB。

CSS拆分

vue-cli3 默认开启插件 ExtractTextPlugin,它会将每个模块的 css 抽取出来独立成单个文件,通常来说自定义的 css 文件一般不会太大,但数量上会比较多,过多的文件会增加请求数,给服务器造成压力。我们可以在 vue.config.js 中关闭它:

module.exports = {
    // ......
    css: {
        extract: false
    }
};

关闭后打包就不会生成 .css 文件,这些 css 代码会被合并到 js 文件中。这样带来的一个问题就是,有的 js 文件本身就已经很大了,合并后会更大,可能会影响首屏加载速度。拆不拆分,看个人取舍。

服务端开启Gzip

安装插件 yarn add -D compression-webpack-plugin

首先在 vue 项目的 vue.config.js 文件中配置:

const CompressionPlugin = require("compression-webpack-plugin");

module.exports = {
    configureWebpack: () => {
        if (process.env.NODE_ENV === 'production') {
            return {
                plugins: [
                    new CompressionPlugin({
                        test: /\.js$|\.html$|\.css$|\.jpg$|\.jpeg$|\.png|\.svg|\.ttf/, // 压缩的文件类型
                        threshold: 10240, // 阈值,超过大小就压缩,单位:B
                        deleteOriginalAssets: false // 是否删除原文件
                    })
                ]
            }
        }
    }
};

为了避免有请求不支持 gzip 的情况,这里设置 deleteOriginalAssets: false 来保留原文件,原文件同 .gz 文件最后一并上传到服务器。使用 yarn build 打包后,在 dist 文件夹下就可以看到大小超过阈值的文件都会对应有一个压缩的 .gz版本:

vue-cli3.0设置gzip

后端 nginx 进行如下配置:

http {

	# 开启gzip服务
    gzip on;
    gzip_disable "msie6";
    gzip_static on;
    gzip_proxied any;
    gzip_http_version 1.1;
}

$gzip_ratio:压缩率。

gzip:gzip开关,默认 off。

gzip_buffers:设置缓冲区的数量和大小,用于存储压缩结果。默认值是 32 4K 或 16 8K。缓冲区的大小默认等于内存页的大小,具体是 4K 还是 8K 取决于平台。

gzip_comp_level:设置压缩等级。范围从 1 到 9,数字越大,压缩效果越好,但压缩时间会更长,且更加消耗 cpu。默认为 1。

gzip_disable:后面跟正则表达式,若请求头中的 “User-Agent” 匹配,则会取消 gzip 压缩。常见的写法有:gzip_disable “msie6”,这里的 “msie6” 对应正则表达式 “MSIE [4-6]\.”,它用于在 IE5.5 和 IE6 SP1 中禁止 gzip 压缩。相比较而言,直接写 “msie6” 匹配速度更快。

gzip_http_version:设置压缩可以响应的最低 HTTP 版本。默认是 HTTP 1.1。

gzip_min_length: 设置允许压缩的页面最小字节数(从响应头中的 Content-Length 中获取)。默认是 20,建议大于 1K。

gzip_proxied:nginx 作为反向代理时启用,开启或者关闭后端服务器返回的结果,匹配的前提是后端服务器必须要返回包含 “Via” 的 header 头。gzip_proxy 支持如下参数:

  • off:对所有的代理请求取消压缩;
  • expired:若 header 头中包含 “Expired” 信息,则启用;
  • no-cache:若 header 头中包含 “Cache-Control:no-cache” 信息,则启用;
  • no-store:若 header 头中包含 “Cache-Control:no-store” 信息,则启用;
  • private:若 header 头中包含 “Cache-Control:private” 信息,则启用;
  • no_last_modified:若 header 头中不包含 “Last-Modified” 头信息,则启用;
  • no_etag:若 header 头中不包含 “ETag” 头信息,则启用;
  • auth:若 header 头中中包含 “Authorization” 头信息,则启用;
  • any:对任何代理请求均启用压缩。

gzip_types:gzip 支持的类型。

gzip_vary:如果设置了 gzip、gzip_static 或 gunzip 指令,则启用响应头 “Vary: Accept-Encoding”。默认 off。

gzip_static:开启后,nginx 将不会动态地对每个请求先压缩再传输。考虑到项目不大,而且前端打包时已经进行过压缩,我们完全可以将打包好 .gz 上传到服务器上,这样 nginx 会直接传输 .gz 文件,减少了很多服务器资源的浪费。具体用法可参考 Module ngx_http_gzip_static_module

当我们请求的数据的响应头出现 content-encoding:gzip 时,就表明 gzip 压缩设置成功:

响应头显示gzip

跟以前没设置 gzip 相比,传输效率明显提升了很多:

gzip设置前后比较

第一张是以前部署的服务,下面一个则是最新部署的,尽管原始文件更大,但速度反而更快。

在本篇的种种方法中,该方法的效果是最明显的。

此外,还有一些其他方法,比如使用 CDN 等等,这里不做展开。

参考