将多个静态资源打包为单个资源以减少请求数目,是提高页面加载速度的常用手段。于是上个星期,我就在实现网站静态资源的自动打包功能,原以为是个比较简单的问题,实现起来也没有遇到什么障碍,不过在开发完毕投入使用的时候却让我跌了下眼镜。由于静态资源在打包以后,它们的访问路径势必会改变,这样其他一些依赖于原有路径的资源就访问不到了。这方面最常见的例子,便是CSS样式表中引用的图片路径是相对于CSS文件路径的。当意识到这个问题以后,还真是让人手忙脚乱了一把。
例如,有一个描述对话框组件的CSS文件,它的原有访问路径为/styles/dialog/core.css,其中有行样式表为url(header.png),这意味这幅图片的访问路径为/styles/dialog/header.png。同样,在另一个文件/styles/menu/core.css中也有url(bg.png)这样的代码。那么好,如果我们将这两个文件打包在一起,叫做/packed/styles/dialog-menu.css,那么浏览器就会去加载/packed/styles下的header.png和bg.png,为什么?因为url(…)这段代码,在打包后出现的位置就不同了。
解决这个问题似乎有多种办法,例如将资源文件复制到/packed/styles下面,但这就要处理文件重名的问题。如果使用组件化开发的方式,两个组件之间应该不去考虑资源的重名,否则就产生了依赖。另一种方式,是在CSS内部使用绝对路径,例如在/styles/menu/core.css中使用url(/styles/menu/bg.png)而不是url(bg.png)。这么做可以解决打包上的问题,但是却破坏了组件化的开发方式。例如此时dialog组件就不是独立的了,它的图片路径是死的。而在理想情况下,一个组件的CSS文件和图片等资源应该是统一的,并独立与其他条件,这也是CSS中url(…)含义的设计目的。
当然,我并不是说这种“组件化”的实践是必须遵守的,如果只是在开发一个独立的项目,便可以将“绝对路径”作为项目中的约定。但是在实际情况下,这种组件化及相对路径的使用是客观存在的。例如jQuery成百上千的插件,它们的CSS样式中一定不会使用绝对路径。那么我们又该如何对它进行打包呢?
其实路径问题也很容易解决,只要在“打包”的时候修改掉CSS文件内容里的路径即可。例如我们可以知道,如果浏览器是在/packed/styles/dialog-menu.css文件中,访问到原本在/styles/dialog/core.css里的内容,那么原本的url(bg.png)就必须改写成url(/styles/dialog/bg.png)这样的绝对路径,或者是url(../…/styles/dialog/bg.png)这样相对于新地址的“相对路径”。
这样的修改其实并不复杂,例如在CSS中,似乎需要替换的也只有url(…)这样的地址了,一个普通的正则表达式便可以解决,而比较麻烦的可能是JavaScript文件了。在某些组件化的JavaScript中,它需要的一些资源也是根据脚本文件的地址相对计算出来的。此时,其中的路径可能只是一些普通的字符串而已,没有规律保险的替换规则。如果要解决这个问题,我们可以在项目中形成某种约定,例如,计算相对路径时都不是简单的字符串拼接,而是使用一个函数调用。而这个函数调用,在进行“打包”时便可以看作是一个待替换的标志。
相对路径的问题似乎就这么解决了,我想前面这段描述也已经足够清楚,比写代码还要清楚,所以暂时就先不提供我在项目里使用的动态打包机制了。不过这里还有个小小的花絮可以一谈。
负责前端开发的同事很重视静态资源的打包问题,因此也就一直催促我实现这方面的功能。之前他说,他需要一个工具,输入一个配置文件,便可以将指定文件打包,然后在正式站点发布时修改页面上引用的地址就行了。我说这样不行,会死人的。例如,一个组件会在多个页面上使用,那么它的更新会导致多次打包,还要修改多个页面文件,这对于站点的快速升级更新也是一个灾难。此外,在页面上所使用的静态资源信息,还需要在打包工具的配置文件中重复,这也是一种违反DRY的情况。
我理想中的打包机制要有以下几个条件:
- 遵守DRY原则,例如,一张页面需要加载哪些脚本,此类信息只会出现在对应页面中。
- 在开发系统和生产系统上部署同一套代码,而不会为了生产系统修改代码。
- 自动打包,自动版本升级或回退。打包结果易于开发人员进行识别、分析及调试。
- 简单,清晰,性能高。
上周实现的解决方案似乎基本符合这些条件,有机会我再来分享吧。
来源:oschina
链接:https://my.oschina.net/u/59889/blog/42010