[译]未雨绸缪之:静态资源处理

原文地址: secure.phabricator.com/book/phabfl…php

译者水平有限,若是有错误欢迎指正!前端

将来你会编写愈来愈多的 JS 和 CSS ,而且最终将找到一个合适的系统来管理它们。web

这个是未雨绸缪专栏,汇总了一些web工程化问题,你应该在遇到它们以前就思考它们。ajax

自动化处理依赖数据库

在页面中添加静态资源最简单的办法,就是在页面渲染以前,在页面顶部一个个的引入它们,看 Facebook 以前的方式:浏览器

<?php

require_js('js/base.js');
require_js('js/utils.js');
require_js('js/ajax.js');
require_js('js/dialog.js');
// ...
复制代码

这样作在早期是可行的,但从 2007 年开始状况变得不可控了。由于你须要按正确的顺序逐条手动列出这些静态文件,致使其余人须要把这一坨文件复制粘贴到各个页面。这样页面须要加载大量的 JS,影响了前端性能。缓存

因而咱们转向了一个被称做 Haste 的系统,该系统使用相似 docblock 的头部声明 JS 依赖:bash

/**
 * @provides dialog
 * @requires utils ajax base
 */
复制代码

咱们给每一个文件手动添加注释,虽然理论上能够用静态分析工具代替(咱们没有真正这么作是由于咱们的 JS 是非结构化的)。这样咱们能够经过一次调用请求组件的全部依赖链:服务器

require_static('dialog');
复制代码

...而不是复制粘贴那一大坨依赖文件。前端性能

按需加载

早期的方法带来的另外一个问题是,全部的静态资源都在页面顶部引入,而不是在实际须要的时候引用。这意味着2点:

  • 你须要引入全部可能在页面上出现的资源;
  • 若是你要在2个及以上的页面添加新内容,你最好把它放到 base.js 里。

这样每一个页面都会有一坨蠢代码,例如 CAPTCHA(由于某些工做流涉及未验证的用户,理论上应该在全部页面上向全部用户展现CAPTCHA),以及其余人在 base.js 里时不时添加的东西。

咱们转用了一个系统,JS 和 CSS 标签在页面加载完以后被输出(它们仍是出如今页面顶部,只是在输出到浏览器以前被预先准备好,而不是被附加进去,这里有些复杂,超出本文讨论范围),因此require_static()可能出如今代码的任何地方。而后咱们移动全部的require_static()到调用的地方(只有对话框代码会引入其依赖的 CSS 和 JS,由于并非每一个页面都有对话框),而且把 base.js 拆分红一系列更小的文件。

打包

大部分状况下最大的前端性能杀手是大量的http请求,针对这个问题最有力的武器是把单独的 JS 和 CSS 打包成一个大文件,这样就能够加载一个大的 JS 核心文件而不是加载一堆小文件。一旦其余基本工做到位,这是一个相对容易的更改。咱们从手动定义包开始,最终转向基于生产数据的自动生成。

缓存与服务器内容

用最简单的方法引入静态资源,好比用 src="/js/base.js" 写下一个原生 JS 标签。随着站点规模的扩大,这将带来灾难性的问题,由于客户端使用的多是老版本资源。这会引发一系列微妙的问题(尤为是使用了 CDN 时),最大的问题是当你 push/deploy 站点时用户正访问你的站点,客户端不会为已有的缓存资源发起请求,所以即便你的服务器能正确响应 If-None-Match (ETags) 和 If-Modified-Since (Expires),当你正将静态资源变动 push 到站点时,此时的浏览者将见不到这些变化后的静态资源。

解决这个问题最好的方法是给 URI 加版本号,这样各版本资源文件对应一个独立的 URI,如:

rsrc/af04d14/js/base.js

当你 push 站点时,用户将访问引用新 URI 的页面,浏览器会从新加载资源。

可是,还有一个大问题,一旦你有一堆前端页面:

当你 push 站点时,用户可能会发出一个请求,这个请求由运行新版本代码的服务器处理,返回了一个包含新 URI 的页面。而后浏览器发起了对新 URI 的请求,却被分发到了还未安装新版本代码的服务器上,服务器返回了旧版本的资源。这样客户端就有了脏缓存:新版本的 URI 下缓存了旧版本代码。

有不少巧妙的方法来解决这个问题,Facebook 解决的方法是用数据库而不是硬盘提供静态资源。当一个 push 开始以前,新的静态资源被写在数据库里,这样每一个服务器均可以同时处理新旧资源的请求。

这种方式也使一些处理流程变得相对容易(例如删除注释和空格),只须要把压缩/加工的 CSS 和 JS 版本插入数据库便可。

参考实现:Celerity

这里讨论的一些想法在 Phabricator's 的 Celerity 系统中实现,该系统本质上是 Facebook使用的 Haste 系统简化版。

相关文章
相关标签/搜索