Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

嗨学网 Exam 项目上线自动化静态资源处理复盘分享 #5

Open
ghost opened this issue Mar 24, 2017 · 0 comments
Open

嗨学网 Exam 项目上线自动化静态资源处理复盘分享 #5

ghost opened this issue Mar 24, 2017 · 0 comments
Labels

Comments

@ghost
Copy link

ghost commented Mar 24, 2017

2017.03.24 发布,最后更新于 2017.03.28

PC Web端 题库项目 v3.1.0 于 2017.03.23(周四)成功上线,这个版本我们对静态资源文件做了压缩处理及版本修订,在各位同事的共同努力下成功将静态资源处理部署至服务器并纳入版本发布自动化流程。

这篇博文旨在结合前端自动化构建领域知识对本次静态资源处理工作做知识分享并复盘本版本上线该功能遇到的问题。

(一)为什么要做自动化静态资源处理?

为了构建高性能 Web,我们通常会考虑到以下2个优化方向:

优化方向 优化手段
请求数量 合并脚本及图片(比如合并 css、js,CSS Sprites 技术)
请求带宽 精简脚本体积、移除冗余脚本代码、图片无损压缩技术

同时,我们项目中还存在以下2个问题:

问题 描述
缓存控制 应避免用户浏览器缓存而不去请求新版本资源的情况
代码安全 不应将 js 代码逻辑完好地暴露在公共网络中

而以上问题都可以通过在开发编码阶段后进行静态资源文件处理去解决。

至于自动化很好理解,为了提高生产力,可以解放劳动力嘛 😊

(二)我们做了哪些静态资源处理?

压缩 HTML 文件
  • 删除换行符、空文本结点等
  • 压缩 <style></style>中的 CSS 代码
  • 压缩 <script></script>中的 js 代码
压缩 CSS 文件
  • 删除换行符、空格符、注释及最后一个属性的结尾分号;替换为简写属性等 - 参考
  • IE8+ 兼容模式,控制保留 IE8+ 的 CSS Hack - 参考
压缩、混淆 JS 文件
  • 删除无用字符
  • 连接连续变量声明等
  • 删除未引用的函数和变量、debugger 等
  • 局部变量用 a-z 进行混淆替换

具体参考

压缩图片
  • 对 .jpg, .png, .gif, .svg 格式的图片资源进行无损压缩
版本修订
  • 通过对文件内容进行哈希校验的方式,为资源文件命名加哈希后缀,e.g: chapter.js -> chapter-dbfaeb5f77.js,并输出 rev-manifest.json 文件用来指示文件命名修改前后的路径及文件名变化
  • 检索上步骤输出的 rev-manifest.json,并对模板文件中(.html, .vm, .jsp)对资源的引用 url 同样添加对应的哈希值

(三)query 方式还是 hash 方式?

两种方式都可以达到防止浏览器缓存:

  • query 方式修改链接: a.js -> a.js?v=20170323
  • 根据文件内容进行哈希校验运算修改文件名: a.js -> a-dbfaeb5f77.js

query 方式的问题:

  1. 以覆盖同命名文件的方式进行线上资源替换,会导致引用该资源的页面与该资源发布不同步。比如 index.html 需要引用 a.js,一个先上,一个后上,这段间隙内存在引用资源与页面不匹配的问题
  2. 为更新后文件添加 query 很耗开发成本

hash 方式的优势:

  1. 当且仅当文件内容变化时文件名才做修改,与时间因素无关
  2. 由于哈希值是文件名的一部分,上线时可以先上静态资源,再上 html 文件,因此不存在 query 方式的覆盖间隙
  3. 版本回滚时,无需回滚静态资源,只需回滚页面
  4. 由于静态资源文件名只与文件内容相关,因此可以开启永久缓存,只有文件内容更新缓存才会失效,大大增加缓存利用率

(四)部署实施中遇到的问题

  1. 由于为静态资源进行版本修订需要指定基准路径(base url), 因此项目中对静态资源的引用应避免使用相对路径,引用常出现在 .vm, .html, .jsp, .css 中。可以使用 $STATIC_URL/exam/**/*/exam/**/* 的形式。目前解决措施是通过脚本匹配替换相对路径为相应的绝对路径
  2. 个别第三方库,比如 layer,会在 JS 中为 HTML 以添加 <link> 标签的方式添加对其 CSS 的引用,并动态添加 query。(当时和晓东发现这个的时候也是很无语的... 😢)。对于这种个别情况,文件处理是搞不定的,只能手动排除加入构建黑名单中了...
  3. 应使用转义字符,eg: 使用 &gt; &lt; 而不是 > <
  4. 之前本考虑由于静态处理改动文件范围相当大,我们多测试一个版本再正式上线;发版后意识到,当把项目代码中部分文件引用的 query 去掉并提交过后,实际上静态资源文件处理的上线已经是开弓没有回头箭了 👊

(五)我们还使用了 Source Map 技术

那么问题来了,版本上线,如何在线上环境下的浏览器中调试已经压缩了的代码?

--> Chrome 和 FireFox 浏览器调试工具自带的 Pretty print 工具

那,如何格式化已经混淆了的 JS 呢?

--> Source Map 技术

(六)为什么需要前端自动化构建工具?

很早之前处理各种情景的工具已经出现了,我们需要把能处理各种任务的工具集成起来,实现可配置、可自动按需执行。对于需要反复重复的任务,例如压缩(minification)、编译、单元测试、linting 等,自动化工具可以减轻你的劳动,简化你的工作

gruntgulp:

  • grunt 更早诞生,拥有强大且丰富的社区资源,可配置性极强
  • gulp 是基于文件流的构建系统,使用代码优于配置的策略
  • gulp 的插件更纯粹,单一的功能,并坚持一个插件只做一件事
  • I/O 流程不同: 使用 grunt 的 I/O 过程中会产生临时文件,而 gulp 通过管道将多个任务连接起来,一般只有一次 I/O 过程。当使用流时,gulp 无需产生中间文件,只将最后的输出写入磁盘,整个过程因此变得更快

Node.js 中,在使用 readFile 方法或 readFileSync 方法读取文件内容或者使用 writeFile 方法或 writeFileSync 方法写入文件内容时,Node.js 将该文件内容视为一个整体,为其分配缓存区并且一次性从缓存区操作文件内容,在这个期间,Node.js 将不能执行任何其他处理。

在很多场合下,我们并不关心整个文件的内容,而只关注是否从文件中读取到了某些数据,以及在读取到这些数据时所需要执行的处理。这时,我们可以使用 Node.js 中的文件流来执行这一处理。

-- 《Node.js 权威指南》

(七)除了处理静态资源,自动化构建还可以做什么?

  • 编译: 将 Less、Sass 编译成 CSS;将 Coffeescript、TypeScript 编译成 JS;ES6 编译成 ES5 等,集成 Webpack 等
  • 优化: 移除未使用的 CSS 选择器;图片格式转换;生成不同尺寸图片;通过 SVG icons 创建 icon fonts 等
  • 模板: 编译 Jade(现在叫 pug 了)、Handlerbars、Swig 等模板为 HTML;编译 Handlebars、Nunjucks、Dust 等模板为 JS等
  • 代码质量检测: HTMLHint、CSSLint、JSHint、ESLint 等
  • 实时加载: 浏览器同步刷新甚至热加载
  • 日志: 显示项目大小,监控文件更新等
  • 测试: 运行 Mocha、Jasmine 2、Protractor、Karma 等测试用例
  • 脚手架: 集成项目的脚手架生成工具
@ghost ghost added the Blog label Mar 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

0 participants