在多页网站中合并 CSS 是很常见的优化手法。一般来说,CSS 体积不会太大,使用同一份 CSS,改进用户点击链接后的加载速度,大部分收益都大于多加载几 K CSS 带来的损耗。
对于 UI 库来说也是如此,用统一的样式库,减少 import
时的心智负担,也是性价比很高的做法。
最开始我在 UI 库的入口文件里 export
所有组件,然后在其它仓库里用 import {component} from 'my-components'
使用组件。后来通过分析得知,这样做无法 tree-shaking,而且会导致组件库的重复引用,即 A import
B,B import
C,那么无论 A 里有没有用到 C(比如用到 B 的一个小功能 b1,它不依赖 C),都会把 C 的代码打包进去。
通过研读 Webpack 的 tree-shaking 文档,我得知也没有什么好办法可以规避这个问题,毕竟 JS 很灵活,你也不知道哪个开发者会搞个 eval('const myRequire = require')
,如果要识别解析分辨所有情况太复杂,所以选择最保守的策略:认不出来具体功能的代码都给你带上。(早年我写 NerveNet 的时候,就是想不通这里怎么搞,最后坑掉了。早知道大家都选择绕开,说不定我的 NerveNet 也能如愿写出来了……)
总之,目前 lodash 的做法比较常见且能解决问题,即增加多入口,为每个可能用到的函数都打包独立的函数。这样需要引用那个就引用哪个,不用担心把整个 lodash 都打包进去。
于是我就立项开始重构我厂的几个前端库。然后很快就卡在 UI 库上面:无法生成合并过的 CSS 文件。Google 许久没有结果,吃饭前我灵机一动:vue-loader 必须搭配 VueLoaderPlugin 才能正确打包,会不会这个过程有 bug,导致如果我的入口都是 Vue 单文件组件,就会没法正确合并 CSS 文件。
然后我就测试了一下,代码大约如下:
// a.js
import './a.styl'
// b.js
import './b.styl'
可以生成合并过的 CSS 文件。与之相较,这样的 Vue 单文件组件就不行:
// a.vue
<style lang="stylus">
body
font-size 16px
</style>
// b.vue
<style lang="stylus">
body
color red
</style>
但是,同样是 vue 单文件组件,样式使用 import
的方式导入,就没问题:
// a.vue
<script>
import './a.styl';
</script>
// b.vue
<script>
import './b.styl';
</script>
现在可以确定是 vue-loader 或者 VueLoaderPlugin 有问题。不过最近身体欠佳,每天要花很多时间锻炼,另外还欠下不少坑要填,所以暂时没空去翻 issue 或者提 issue。哪位同学看见了,愿意帮忙的,可以搞一下,也算参与 开源社区建设了,功德无量。
欢迎吐槽,共同进步