Vue项⽬如何部署?实战教你
前⾔
使⽤vue、react、angular等技术开发过程中,我们都会遇到以下问题:
1. ⾸屏加载慢
2. 每⼀次更新都需要清除浏览器缓存才能看到效果(经常被测试吐槽)
这两个问题可以从很多⽅⾯进⾏优化,今天我就从前端页⾯部署阶段来优化⼀下这两个问题。PS:以下内容都基于vue-cli3+。
3.光理论是不够的。在此赠送2020最新企业级 Vue3.0/Js/ES6/TS/React/node等实战视频教程,想学的可进裙 519293536 免费获取,⼩⽩勿进哦!
前端页⾯⽂件缓存⽅案
从vue-cli3打包说起
路由使⽤按需加载后,打包⽣成的⽂件,每⼀个路由页⾯都对应⼀个js和css⽂件,⼊⼝main.js及其依赖
则打包成了app.js和app.css,公共依赖都放到了chunk-vendors.js。
vue-cli3打包后的dist/js⽂件夹:
可以看到,打包⽣成的js/css/img等⽂件的⽂件名都带有hash值,当源⽂件内容改变时,重新打包后对应的⽂件hash值也会改变。举个栗⼦,我们修改了about.vue中js的内容,重新打包时about.js的hash值会改变,以及依赖about.vue的⽂件app.js的hash值也会改变,⽽其他没有修改的⽂件,打包后的hash值都不会改变。
我们知道,⽂件名带hash是为了消除缓存带来的影响的,但是所有⽂件都不缓存肯定不是⼀个很好的解决⽅案。
vue-cli3打包⽣成的⽂件名带hash值的作⽤
为了缓存的最优体验
我们先来简单回顾下http缓存的知识():
1. HTTP1.0是通过Expires(⽂件过期时间)和Last-Modified(最近修改时间)来告诉浏览器进⾏缓存的,这两个字段都是UTC时间(绝对时
间)。Expires过期控制不稳定,因为浏览器端可以随意修改本地时间,导致缓存使⽤不精准。⽽且Last-Modified过期时间只能精确到秒。
2. HTTP1.1通过Cache-Contorl和Etag(版本号)进⾏缓存控制。浏览器先检查Cache-Control,如果有,则以Cache-Control为准,忽略Expires。
如果没有Cache-Control,则以Expires为准。
Cache-Control除了可以设置max-age(相对过期时间,以秒为单位)以外,还可以设置如下⼏种常⽤值:
public,资源允许被中间服务器缓存。浏览器请求服务器时,如果缓存时间没到,中间服务器直接返回给浏览器内容,⽽不必请求源服务器。
private,资源不允许被中间代理服务器缓存。浏览器请求服务器时,中间服务器都要把浏览器的请求透传给服务器。
no-cache,不管本地副本是否过期,每次访问资源,浏览器都要向服务器询问,如果⽂件没变化,服务器只告诉浏览器继续使⽤缓存(304)。
no-store,浏览器和中间代理服务器都不能缓存资源。每次访问资源,浏览器都必须请求服务器,并且,服务器不去检查⽂件是否变化,⽽是直接返回完整的资源。nginx部署前端项目
must-revalidate,本地副本过期前,可以使⽤本地副本;本地副本⼀旦过期,必须去源服务器进⾏有效性校验。
proxy-revalidate,要求代理服务器针对缓存资源向源服务器进⾏确认。
s-maxage:缓存服务器对资源缓存的最⼤时间。
现在99%的浏览器都是HTTP1.1及以上版本,我们配置缓存就使⽤Cache-Contorl和Etag配合就好了。
那么问题来了,检查⽂件是否最新不是⽤etag吗,为什么⽂件名还需要有hash值?
(1)如果⽂件名不带hash值,⽂件版本得⽤etag来标记,浏览器需要先去检查下是否过期,服务器则需要检查⽂件是否最新。
(2)⽽⽂件名带有hash值,可以直接将⽂件的过期时间设置为1年,浏览器就不⽤检查是否过期,直接使⽤。
原因是,如果页⾯源⽂件有修改,⽣成的js/css的hash值就会修改,对应的请求js/css地址也会变化,htpp地址改了,也就不⽤检查是否过期。没修改的⽂件的hash则不变,可以使⽤缓存⽂件。
所以利⽤⽂件名带hash来做缓存,即能保证,页⾯有修改浏览器能请求到最新的⽂件,⼜能节省服务器的请求(检查是否过期的请求)。
实现⽆感知发版
只有⼀台服务器的情况下,我们的页⾯⽂件需要更新,通常操作是:先删掉旧⽂件,然后上传新⽂件,这段时间系统将不可⽤,对⽤户有⼀定的影响。
仅更新前端页⾯的前提下,⽂件名带有hash值还可以实现⽤户⽆感知发版:系统更新时,只需要将打包之后的⽂件除index.html以外的⽂件(js/css/img),全部上传到服务器⽹站⽬录,未修改⽂件(即重名⽂件)直接跳过,有修改的⽂件由于⽂件的hash值不同会被上传,上传完毕我们再将index.html覆盖掉旧版就⾏。这段时间⽤户已请求旧版本index.html的⽆影响(不会出现⽂件404,因为新旧版本js/css同时存在),⽽新访问⽤户则请求的是新版index.html,访问旧页⾯⽤户刷新也会请求新版⽂件,并且⽆缓存影响,即对⽤户使⽤0影响。⼀段时间之后,我们只需要按⽂件⽣成时间对⽐⼀下删除旧⽂件即可。PS:替换前端⽂件不需要重启服务器。
总结:凡是⽂件名带有hash值的的⽂件都可以设置为“永久缓存”(⼀年),其他不带hash的⽂件使⽤etag来设置缓存,由Nginx判断是否过期。优化打包结果
页⾯部署的时候,有个问题,如何区分⽂件名是否带有hash值呢?正则匹配显然不是很好的办法。其实办法很简单,打包⽣成的⽂件都带
有hash值,⽽public⽬录⾥⾯的⽂件不会经过打包处理。所以只需要将public⽬录⾥⾯的⽂件除了index.html全部放到⼀个static⽬录(注意引⼊路径)
那么打包后的⽂件⽬录就会变成这样:
static⽬录⾥⾯的⽂件和index.html的⽂件名是不带hash值的,其他的⽂件都是带有hash值的
补充:打包后发现⼀些页⾯⽂件很⼩,只有⼏K
如下图所⽰,虽然是按需加载,但是感觉浪费服务器请求
这时,我们可以配置webpack的特殊注释(需要Webpack > 2.4),将⼀些按需加载的路由打包到同⼀个js⽂件
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
复制代码
这⾥需要注意⼀下,虽然每个⽂件单独打包都是1k,但是1k+1k不等于2k,也就是说,打包到⼀起的体积会⽐原来分开的⼤,4个1k的⽂件打包到⼀起,体积⼤约是10k,体积达到10k,刚好就会触发gzip压缩,压缩之后体积在4k左右,所以并没有什么影响。
服务器配置缓存
理论知识有了,现在我们来实际操作⼀下:⽂件名带hash的(即css、js、font和img⽬录下的所有⽂件)设置⼀个⽉缓存,浏览器可以直接使⽤缓存不需要请求服务器。其他的⽂件(index.html和static⽬录下的⽂件)设置为no-cache,即每次都来服务器检查是否最新。
为什么缓存时间是⼀个⽉,刚才不是说设置⼀年?设置为⼀年,当然没有任何问题。不变的⽂件可以⼀直使⽤,有改动的⽂件,会重新请求,但是有该动的旧⽂件已经没有⽤了,由于过期时间是⼀年,所以不会被删的,⼀直占⽤⽤户的硬盘,系统更新越频繁,⽆⽤旧⽂件越多,占⽤的存储也越多,这样是不好的(⽤户看了想打⼈)。所以设置⼀个合理的时间⽐较好,⼀个⽉就挺好。
废话不说,以Nginx服务器为例,配置如下(配置⽂件f的http模块):
server {
location = /index.html {
add_header Cache-Control no-cache;
}
location ~ /static/ {
add_header Cache-Control no-cache;
}
location ~ /(js/*|css/*|img/*|font/*) {
expires 30d;
add_header Cache-Control public;
}
}
复制代码
效果如下图:当我们修改index.html内容时,会重新请求,没有修改就会304,⽂件名带hash的都是直接从本地缓存读取。
有两点需要注意的地⽅:
1. 项⽬⾥⾯不要⽤service-worker,这会影响我们的缓存设置,浏览器会优先使⽤service-worker缓存。vue-cli4的pwa插件⽣成的模板⾃带service-
worker
2. 调试的时候记得允许缓存
前端⽂件设置gzip压缩
webpack配置⽣成gzip压缩的⽂件
webpack有⼀个⽂件压缩的插件,可以将⼤⽂件压缩成gzip的格式。使⽤起来也⾮常简单,先安装:npm install --save-dev compression-webpack-plugin,
然后修改webpack配置(fig.js):
const CompressionWebpackPlugin = require("compression-webpack-plugin");
// 可加⼊需要的其他⽂件类型,⽐如json
// 图⽚不要压缩,体积会⽐原来还⼤
const productionGzipExtensions = ["js", "css"];
configureWebpack: config => {
if (v.NODE_ENV === "production"){
return {
plugins: [
new CompressionWebpackPlugin({
// filename: '[path].gz[query]',
algorithm: "gzip",
test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"),
threshold: 10240, //对超过10k的数据进⾏压缩
minRatio: 0.6 // 压缩⽐例,值为0 ~ 1
})
]
};
}
}
};
复制代码
打包完的js/css⽂件,都会多⼀份对应的gzip⽂件,部署的时候需要配置⼀下,启⽤gzip,这样⽀持gzip压缩的浏览器请求的就是压缩⽂件,不⽀持的浏览器请求的就是源⽂件,gzip压缩⽂件体积会⼩很多。
字体⽂件是否需要gzip?
⽹站中常见的图⽚的格式有jpg(jpeg)、png、gif、webp,这些格式的图⽚本⾝已经优化了,所以不再需要gzip。实际上对图⽚进⾏gzip压缩,不仅没有效果,反⽽可能使图⽚体积更⼤。那么字体⽂件呢,是不是和图⽚⼀样?
从阿⾥巴巴⽮量图库⽣成的图标字体的css中我们可以看出,⼀般常见的字体⽂件有:eot、woff、ttf、svg,另外woff2是以base64的格式存储的。
@font-face {font-family: "iconfont";
src: url('?t=1587624344896'), /* IE9 */
url('iconfont.woff?t=1587624344896') format('woff'),
url('data:application/x-font-woff2;charset=utf-8;base64,...') format('woff2'),
url('f?t=1587624344896') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1587624344896#iconfont') format('svg'); /* iOS 4.1- */
}
复制代码
查阅资料后发现:eot和ttf格式⼀般情况下本⾝不压缩,也就是说可以进⾏gzip压缩。⽽woff格式具有内建压缩,不需要gzip压缩。
实际测试⼀下,发现eot和ttf可以进⾏压缩,效果还不错,⽽woff格式的,CompressionWebpackPlugin插件根本不⽀持压缩,即使你写了配置了压缩woff⽂件,它也不会⽣成gz⽂件。
并且实验发现,svg虽然是图⽚,但是也可以进⾏gzip压缩,压缩效果还不错:
结论:svg、eot和ttf这三种格式的字体⽂件可以使⽤CompressionWebpackPlugin进⾏压缩,并且配合Nginx的gzip_types配置,woff和woff2格式的字体⽂件不需要gzip。
服务器配置gzip压缩
Nginx是前端⽂件常⽤的服务器,Nginx服务器的配置⽂件f的http模块:
server {
# 开启gzip on为开启,off为关闭
gzip on;
# 检查是否存在请求静态⽂件的gz结尾的⽂件,如果有则直接返回该gz⽂件内容,不存在则先压缩再返回
gzip_static on;
# 设置允许压缩的页⾯最⼩字节数,页⾯字节数从header头中的Content-Length中进⾏获取。
# 默认值是0,不管页⾯多⼤都压缩。
# 建议设置成⼤于10k的字节数,配合compression-webpack-plugin
gzip_min_length 10k;
# 对特定的MIME类型⽣效,其中'text/html’被系统强制启⽤
gzip_types text/javascript application/javascript text/css application/json;
# Nginx作为反向代理的时候启⽤,开启或者关闭后端服务器返回的结果
# 匹配的前提是后端服务器必须要返回包含"Via"的 header头
# off(关闭所有代理结果的数据的压缩)
# expired(启⽤压缩,如果header头中包括"Expires"头信息)
# no-cache(启⽤压缩,header头中包含"Cache-Control:no-cache")
# no-store(启⽤压缩,header头中包含"Cache-Control:no-store")
# private(启⽤压缩,header头中包含"Cache-Control:private")
# no_last_modefied(启⽤压缩,header头中不包含"Last-Modified")
# no_etag(启⽤压缩,如果header头中不包含"Etag"头信息)
# auth(启⽤压缩,如果header头中包含"Authorization"头信息)
# any - ⽆条件启⽤压缩
gzip_proxied any;
# 请求加个 vary头,给代理服务器⽤的,有的浏览器⽀持压缩,有的不⽀持,所以避免浪费不⽀持的也压缩
gzip_vary on;
# 同 compression-webpack-plugin 插件⼀样,gzip压缩⽐(1~9),
# 越⼩压缩效果越差,但是越⼤处理越慢,⼀般取中间值
gzip_comp_level 6;
# 获取多少内存⽤于缓存压缩结果,‘16 8k’表⽰以8k*16 为单位获得。
# PS: 如果没有.gz⽂件,是需要Nginx实时压缩的
gzip_buffers 16 8k;
# 注:99.99%的浏览器基本上都⽀持gzip解压了,所以可以不⽤设这个值,保持系统默认即可。
gzip_http_version 1.1;
}
复制代码
检查gzip是否⽣效
浏览器⽂件请求的请求头包含字段Accept-Encoding: gzip代表浏览器⽀持gzip压缩⽂件
⽂件响应头包含字段Content-Encoding: gzip代表返回的是压缩⽂件
同时NetWork⼀栏还可以查看到⽂件的实际⼤⼩和实际的请求(gzip)⽂件⼤⼩
检查Nginx是否使⽤了我们提供的gz⽂件
Nginx⾃带gzip压缩功能,如果我们没提供,它会实时压缩(例如index.html⽂件),这就很浪费服务器资源了。现在我们已经提供js和css的gz⽂件,如何判断Nginx是使⽤了我们提供的gz⽂件,⽽不是⾃⼰压缩的呢?
上⾯有⼀个配置项:gzip_static on;,开启之后Nginx会优先使⽤我们的gz⽂件,但是还是不能确定,Nginx有没有使⽤gz⽂件。
查看network请求发现,每⼀个⽂件都有etag响应头,如果Nginx使⽤了已有的gz⽂件,那么这个请求的etag值不带有W/,反之,如果是⽂件
是Nginx压缩的,etag值则会带有W/。
例如index.html:
拿chunk-vendors.js做⼀个实验,这个⽂件本⾝是带有gz⽂件的,请求的etag如下(不带有W/):
这时候我们删掉服务器上chunk-vendors.js对应的gz⽂件,刷新页⾯,请求如下:
综上,我们就可以验证,只要我们配置了gzip_static on;,Nginx就会优先使⽤了我们提供的gz⽂件。
附录 - windows安装Nginx服务器
1. 下载windows下Nginx的安装包:
2. 解压压缩包
3. 在Nginx的⽬录下使⽤cmd命令⾏,启动命令:start nginx,关闭命令:nginx -s stop
总结
1.页⾯⽂件合理的设置缓存和gzip压缩是实实在在能提升⽤户体验的操作,⽽且⽐少写⼏个循环、删除⼏⾏代码优化强得多,但是需要前端和运维的密切配合,才能实现最佳⽅案。
service worker是⽤来实现离线应⽤的,⽂章中没有详细赘述。vue-cli4的pwa插件⽣成的模板⾃带service worker,或许这才是vue项⽬缓存的最佳实
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论