vuecli3.0使用CDN和Gzip压缩

前端时间把项目的加载速度进行了统一的优化,主要使用了 CDN 加载框架和静态文件,还有启用了 Gzip 压缩,这里记录总结一下。

CDN使用

修改配置

打开 vue.config.js 配置文件,添加 externals:

1
2
3
4
5
6
7
8
const externals = {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
'axios': 'axios',
'element-ui': 'ELEMENT',
'vue-i18n': 'VueI18n'
}

这里需要注意,’:’ 号后面对应的文件名必须和项目里面 import 引入的文件昵称相同,例如: import Vue from 'vue' 就是上面对应的 'vue': 'Vue'

这里特别注意一下 element-ui 的昵称必须是: ‘ELEMENT’, 如果使用其它昵称将报错。具体看 element-ui 源码导出方式你就懂了~

然后,再添加 CDN 文件加载路径和不同环境的配置,注意,这里开发环境和生产环节都启用了,正常只有生产环境启用,这里测试是否可用在开发环境也启用了 CDN:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const cdn = {
// 开发环境
dev: {
css: [
'https://unpkg.com/element-ui@2.7.2/lib/theme-chalk/index.css',
'https://cdn.bootcss.com/Swiper/4.5.0/css/swiper.min.css'
],
js: [
'https://unpkg.com/vue@2.6.6/dist/vue.min.js',
'https://unpkg.com/vuex@3.0.1/dist/vuex.min.js',
'https://unpkg.com/vue-router@3.0.1/dist/vue-router.min.js',
'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
'https://unpkg.com/element-ui@2.7.2/lib/index.js',
'https://cdn.bootcss.com/vue-i18n/8.10.0/vue-i18n.min.js'
]
},
// 生产环境
build: {
css: [
'https://unpkg.com/element-ui@2.7.2//lib/theme-chalk/index.css',
'https://cdn.bootcss.com/Swiper/4.5.0/css/swiper.min.css'
],
js: [
'https://unpkg.com/vue@2.6.6/dist/vue.min.js',
'https://unpkg.com/vuex@3.0.1/dist/vuex.min.js',
'https://unpkg.com/vue-router@3.0.1/dist/vue-router.min.js',
'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
'https://unpkg.com/element-ui@2.7.2//lib/index.js',
'https://cdn.bootcss.com/vue-i18n/8.10.0/vue-i18n.min.js'
]
}
}

导入配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
module.exports = {
productionSourceMap: false, // 生产环境是否生成 sourceMap 文件
devServer: {
proxy: 'http://demo.com'
},
chainWebpack: config => {
/**
* 删除懒加载模块的prefetch,降低带宽压力
* https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch
* 而且预渲染时生成的prefetch标签是modern版本的,低版本浏览器是不需要的
*/
config.plugins.delete('prefetch')
/**
* 添加CDN参数到htmlWebpackPlugin配置中, 详见public/index.html 修改
*/
config.plugin('html').tap(args => {
if (process.env.NODE_ENV === 'production') {
args[0].cdn = cdn.build
}
if (process.env.NODE_ENV === 'development') {
args[0].cdn = cdn.dev
}
return args
})
},
configureWebpack: (config) => { // 改webpack devtool 输出方式
config.externals = externals
if (process.env.NODE_ENV === 'development') {
// 开发环境加载source-map
config.devtool = 'source-map'
}
}
}

以上就是 vue.config.js 所需要做的配置了。

修改 index.html 文件

在 head 标签中循环导入 CDN 引入的 CSS 文件:

1
2
3
4
5
<!-- 使用CDN加速的CSS文件,配置在vue.config.js下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
<% } %>

然后在 body 中循环导入 CDN 引入的 js 文件:

1
2
3
4
<!--使用CDN加速的JS文件,配置在vue.config.js下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>

以上就是 CDN 的配置了,这里不需要注释掉原来项目里面 import 导入的代码噢,不影响,很方便,可以随时取消 CDN 的引用而不用修改代码,需要注意的就是上面 externals 配置的昵称必须和 import 导入的别名相同。

Gzip 压缩

如果你觉得采用了 CDN 和各种代码优化后,还觉得首页加载太慢,又不想采用 SSR 的话,可以试试启用 Gzip 压缩噢,可以把文件的传输大小减少一半以上,加快文件的传输速度,自然就加快了首页的加载速度了。这里的配置还是基于 vue-cli3.0:

安装压缩文件插件:

npm i -D compression-webpack-plugin

在文件 vue.config.js 里导入 compression-webpack-plugin,并添加压缩文件类型

1
2
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']

在configureWebpack 里配置如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
configureWebpack: {
plugins: [
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 1024,
minRatio: 0.8,
deleteOriginalAssets: false // 不删除源文件
})
]
}

注意,上面的通用模式,可以只在生产环境使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
// 生产环境
config.plugins.push(
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 1024,
minRatio: 0.8,
deleteOriginalAssets: false // 不删除源文件
})
);
}
if (process.env.NODE_ENV === 'development') {
// 开发环境加载
}
return config
}

或者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (process.env.NODE_ENV === 'production') {
return {
plugins: [
new CompressionWebpackPlugin({
filename: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 1024,
minRatio: 0.8,
deleteOriginalAssets: false // 不删除源文件
})
]
}
}

配置参数解释:

  1. asset: 目标资源名称。 [file] 会被替换成原始资源。[path] 会被替换成原始资源的路径, [query] 会被替换成查询字符串。默认值是 “[path].gz[query]”。
  2. algorithm: 可以是 function(buf, callback) 或者字符串。对于字符串来说依照 zlib 的算法(或者 zopfli 的算法)。默认值是 “gzip”。
  3. test: 所有匹配该正则的资源都会被处理。默认值是全部资源。
  4. threshold: 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。
  5. minRatio: 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。
  6. deleteOriginalAssets: false // 不删除源文件

打包后会同时保留原文件和压缩后的文件,存储等条件允许的情况下,原文件也建议发布到服务器以支持不兼容gzip的浏览器。

以上就是配置 Gzip 压缩前端需要完成的工作了,接下来还需要后端在 Nginx 上开启 Gzip 压缩:

Nginx 配置:

在 http {} 包裹的配置中配置参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 开启gzip
gzip on;

# 如果存在gz文件则服务器不压缩,直接采用gz文件。不知道为啥我开启了这个就报错
# gzip_static默认是没有的,参考http://nginx.org/en/docs/http/ngx_http_gzip_static_module.html
gzip_static on;

# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;

# 设置用于处理请求压缩的缓冲区数量和大小,比如4 16K表示按照内存页(one memory page)大小以16K为单位(即一个系统中内存页为16K),申请4倍的内存空间。建议此项不设置,使用默认值。
gzip_buffers 4 16k;

用于识别http协议的版本,早期的浏览器不支持gzip压缩,用户会看到乱码,所以为了支持前期版本加了此选项。默认在http/1.0的协议下不开启gzip压缩。
gzip_http_version 1.0;

# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间
gzip_comp_level 1;

# 进行压缩的文件类型。javascript有多种形式。
# 其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/css text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;

# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary off;

# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\.";

注意:不是压缩级别越高越好,其实gzip_comp_level 1的压缩能力已经够用了,后面级别越高,压缩的比例其实增长不大,反而很吃处理性能。

这里启用了 gzip_static on,如果服务器存在 .gz 文件则不压缩直接传输 .gz 文件,节省服务器带宽,需要注意的是 gzip_static 命令默认是没有的,参考nginx文档,需要在安装 nginx 的时候检查模块支持中是否含有 --with-http_gzip_static_module 模块,没有此模块的话,配置会报错不存在该指令。

另一方面,压缩一定要和静态资源缓存相结合,缓存压缩后的版本,否则每次都压缩高负载下服务器肯定吃不住。

开启 nginx 缓存

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ { 
access_log off;
expires 30d;
}

location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
access_log off;
expires 24h;
}

location ~* ^.+\.(html|htm)$ {
expires 1h;
}

其中的缓存时间可以自己根据需要修改。

关于字体

为静态资源开启缓存能够较少服务器带宽的消耗,特别是在css中使用字体时,同时配合gzip压缩能够大大减少下载字体造成的带宽影响。

设置字体缓存:

需要注意的是,字体有很多格式,为所有字体格式设置缓存是很有必要的:

1
2
3
4
location ~* ^.+\.(eot|ttf|otf|woff|svg)$ {
access_log off;
expires max;
}

启用字体 gzip:

只需要为 ttf、otf 和 svg 字体启用 gzip,对其他字体格式进行 gzip 压缩时效果不明显。

1
gzip_types  font/ttf font/otf image/svg+xml

测试命令:

curl -I -H "Accept-Encoding: gzip, deflate" "http://39.108.73.162:8082/vendor.js"

测试结果(需要出现Content-Encoding: gzip):

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Server: nginx/1.7.2
Date: XXXXX
Content-Type: application/javascript
Last-Modified: Web, XXXX
Connection: keey-alive
ETag: XXXXXXXXXXXX
Content-Encoding: gzip

注意最后面这句: Content-Encoding: gzip 就OK~

nginx 参数说明请参考nginx文档