前端静态资源部署

前几天厂里的网突然渣掉了,某些网页变得极度卡顿,但是划水网站依旧流畅;我觉得很有趣就打开 DevTools 对比了一下,结果看到某卡顿网页的 Network 状况如下,我大概猜到了一些缘由。这期就借机讲讲前端部署里的一些小技巧吧。

Network

原始前端

OK,这期讨论的原始前端当然也没那么“原始”,我无意追述到 JSP,thymeleaf 这类传统后端渲染的技术。只是说“原始前端”开发比较简单,只需要一页文本编辑器,一份 html 模版和一款打包工具(webpack);部署更简单,webpack 构建并打包出 html、css、js,然后一股脑丢到服务器上就行。当请求到来时,服务器返回 index.html, 然后再把它的相关引用(js、css)返回给客户端即可。

Server

但是这里有个问题是,引用文件名——onion.js——是固定的:一旦部署更新后,就得防止客户端缓存旧文件,所以一般会设置Cache-Control: no-cache。这就是文章开头提到的一个问题,每次打开页面都得重新加载一遍引用文件,一旦网络渣渣了,首页渲染就会十分缓慢。

版本管理

看样子缓存策略还是需要保留的,可如何兼顾更新呢?有人就想到了给引用路劲加个版本号:

Version

p.s. index.html 始终是 no-cache

新版本上线后,浏览器请求 html,发现资源路劲更改了,便主动放弃相关缓存,重新加载引用文件。这个小小的改变很实用,兼顾了缓存策略和新旧版本文件冲突。至于这个版本号怎么添加,各色打包工具都能实现,最简单的策略就是在 index.html 模版里加个时间戳。

摘要算法

当然,随着业务增长,js 和 css 文件会变得愈发巨大,仅仅加载单个文件就可能需要数秒。遇到问题就解决问题,把大文件拆成小份就行了!利用浏览器异步加载多个文件的机制,拆分 JS 能迅速提速。(当然,浏览器并发加载数有限,拆太细会适得其反)。

multi-link

这时候有聪明人就发现了:每次更新时,某些 js 文件并没有被修改,为什么也要增加版本号呢?让客户无端加载一个一模一样的 js 文件,不是很浪费吗?

反正版本号就是一个 unique 值——防冲突用的,不如换成别的数值吧?比如,经典的MD5(消息摘要)算法生成的content hash值,该哈希值与文件内容一一对应的,这就有了精确到单个文件的版本控制信息了。

Content Hash

我自己开发时,会利用 webpack 的 splitChunks 功能,把 Vue 全家桶打包成单个 js 文件、UI 框架也打包成单个 js;这种文件体积不小,但基本不会变动,cache-control/max-age可以写大一些,客户端除了首次访问会慢一点,其他时候基本就disk cache了。

CDN

上面提到服务器存放静态资源文件,但这种形式在性能上也有缺陷:

  • 受地理环境影响,离服务器越远资源加载越慢
  • 频繁请求资源对服务器压力较大

为了进一步提升性能,大家开始把动态网页(index.html)和静态资源(js、css、image...)分开部署。静态资源被放置于 CDN 上。

CDN

但是 CDN 也有缓存策略:新的静态资源发布后,需要一定的时间去覆盖各个边缘站点的旧资源。若某客户端获得了新的动态网页,但是附近的 CDN 节点尚未更新最近发布的静态资源,客户端即便放弃本地缓存,它加载的依旧是位于 CDN 上的“脏数据”。怎么办呢?干脆把文件名也给改了——让摘要信息成为文件名的一部分!具体实现还是可以仰仗 webpack,将 output.filename 设为 [name].[contenthash].js,输出文件和 html 模版都会帮你更改好。

Hash Name

用摘要信息重命名后的资源文件,与旧资源就不同名了,不再需要以覆盖旧文件的形式主动更新各个地区的边缘站点。新版本发布后,浏览器首次请求资源,若 CDN 不存在该资源,便会向就近的边缘站点加载该文件,同时更新 CDN 缓存;这就彻底避免了 CDN 脏数据的问题。

Build

关于构建,我自己所在项目组的做法是前端单独部署:git 接收 MR 后触发 AWS 的 code pipeline 和 code build;Webpack 自动打包生成dist文件夹(里面包含 html、js、css 等资源),html 部署到服务器上,其他静态资源发布到 CDN 上。这一套下来也不贵:一个月十几刀,每次部署也就几分钟。但是,但是!我后来看到有些厂的做法,觉得实在是太“抠”了——成本控制得真好!

他们的做法是这样的:build 在开发环境就完成了,然后把打包生成的dist文件夹也 push 到 git 上。好处很明显:

  • 节省了一套 code pipeline + code build 的花费:大公司就不是一个月十几刀的事了
  • 大大缩短 build 时间:构建环境是不包含node_modules的,大型项目光npm install就要很久,build 时间更是感人

而开发人员本地 build,可以很完美地绕开上面两个缺点。配置上写个 hook,在git commit时多加如下三个 changes 即可:

modified: dist/index.html
delete:   dist/garlic.6b58.js
new file: dist/garlic.e5i1.js

最后的牺牲只是 git repo 上包含了一个 dist 文件夹而已,真的是很“抠门”啊。

小结

这期内容很简单,讲了讲静态资源文件名的那些事,算是螺蛳壳里做道场吧。一直在想一件事,我们追求新技术的本质是什么?我觉得是降低成本!软件本质上还是属于“工业产品”。新闻上能看到一些重工业单位通过调整工艺大大降低成本的案例;我们做软件的也应该不断调整工艺,降低成本、减少设备消耗,从而帮助公司赚取更多的利润。我曾看过有人形容软件开发就是一种缓慢地破坏+重建的过程,这种观点还是挺有道理的,分享给大家。

你可能感兴趣的:(前端静态资源部署)