大型网站通常会由多个页面组成,每个页面都是一个独立的单页应用。 但由于所有页面都采用同样的技术栈,以及使用同一套样式代码,这导致这些页面之间有很多相同的代码。
如果每个页面的代码都把这些公共的部分包含进去,会造成以下问题:
如果把多个页面公共的代码抽离成单独的文件,就能优化以上问题。 原因是假如用户访问了网站的其中一个网页,那么访问这个网站下的其它网页的概率将非常大。 在用户第一次访问后,这些页面公共代码的文件已经被浏览器缓存起来,在用户切换到其它页面时,存放公共代码的文件就不会再重新加载,而是直接从缓存中获取。 这样做后有如下好处:
你已经知道了提取公共代码会有什么好处,但是在实战中具体要怎么做,以达到效果最优呢? 通常你可以采用以下原则去为你的网站提取公共代码:
base.js
,因为它包含所有网页的基础运行环境;base.js
包含的部分代码外,再找出所有页面都依赖的公共部分的代码提取出来放到 common.js
中去。base.js
和 common.js
中包含的部分,而只包含各个页面单独需要的部分代码。文件之间的结构图如下:
读到这里你可以会有疑问:既然能找出所有页面都依赖的公共代码,并提取出来放到 common.js
中去,为什么还需要再把网站所有页面都需要用到的基础库提取到 base.js
去呢?
原因是为了长期的缓存 base.js
这个文件。
发布到线上的文件都会采用在4-9CDN加速中介绍过的方法,对静态文件的文件名都附加根据文件内容计算出 Hash 值,也就是最终 base.js
的文件名会变成 base_3b1682ac.js
,以长期缓存文件。
网站通常会不断的更新发布,每次发布都会导致 common.js
和各个网页的 JavaScript 文件都会因为文件内容发生变化而导致其 Hash 值被更新,也就是缓存被更新。
把所有页面都需要用到的基础库提取到 base.js
的好处在于只要不升级基础库的版本,base.js
的文件内容就不会变化,Hash 值不会被更新,缓存就不会被更新。
每次发布浏览器都会使用被缓存的 base.js
文件,而不用去重新下载 base.js
文件。
由于 base.js
通常会很大,这对提升网页加速速度能起到很大的效果。
你已经知道如何提取公共代码,接下来教你如何用 Webpack 实现。
Webpack 内置了专门用于提取多个 Chunk 中公共部分的插件 CommonsChunkPlugin,CommonsChunkPlugin 大致使用方法如下:
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
new CommonsChunkPlugin({
// 从哪些 Chunk 中提取
chunks: ['a', 'b'],
// 提取出的公共部分形成一个新的 Chunk,这个新 Chunk 的名称
name: 'common'
})
以上配置就能从网页 A 和网页 B 中抽离出公共部分,放到 common 中。
每个 CommonsChunkPlugin 实例都会生成一个新的 Chunk,这个新 Chunk 中包含了被提取出的代码,在使用过程中必须指定 name
属性,以告诉插件新生成的 Chunk 的名称。
其中 chunks
属性指明从哪些已有的 Chunk 中提取,如果不填该属性,则默认会从所有已知的 Chunk 中提取。
Chunk 是一系列文件的集合,一个 Chunk 中会包含这个 Chunk 的入口文件和入口文件依赖的文件。
通过以上配置输出的 common Chunk 中会包含所有页面都依赖的基础运行库 react、react-dom,为了把基础运行库从 common 中抽离到 base 中去,还需要做一些处理。
首先需要先配置一个 Chunk,这个 Chunk 中只依赖所有页面都依赖的基础库以及所有页面都使用的样式,为此需要在项目中写一个文件 base.js
来描述 base Chunk 所依赖的模块,文件内容如下:
// 所有页面都依赖的基础库
import 'react';
import 'react-dom';
// 所有页面都使用的样式
import './base.css';
接着再修改 Webpack 配置,在 entry 中加入 base,相关修改如下:
module.exports = {
entry: {
base: './base.js'
},
};
以上就完成了对新 Chunk base 的配置。
为了从 common 中提取出 base 也包含的部分,还需要配置一个 CommonsChunkPlugin,相关代码如下:
new CommonsChunkPlugin({
// 从 common 和 base 两个现成的 Chunk 中提取公共的部分
chunks: ['common', 'base'],
// 把公共的部分放到 base 中
name: 'base'
})
由于 common 和 base 公共的部分就是 base 目前已经包含的部分,所以这样配置后 common 将会变小,而 base 将保持不变。
以上都配置好后重新执行构建,你将会得到四个文件,它们分别是:
base.js
:所有网页都依赖的基础库组成的代码;common.js
:除了 base.js
中包含的部分外,网页 A、B 都需要的代码;a.js
:网页 A 单独需要的代码;b.js
:网页 B 单独需要的代码。为了让网页正常运行,以网页 A 为例,你需要在其 HTML 中按照以下顺序引入以下文件才能让网页正常运行:
<script src="base.js"></script>
<script src="common.js"></script>
<script src="a.js"></script>
以上就完成了提取公共代码需要的所有步骤。
针对 CSS 资源,以上理论和方法同样有效,也就是说你也可以对 CSS 文件做同样的优化。
以上方法可能会出现 common.js
中没有代码的情况,原因是除去基础运行库外很难再找到所有页面都会用上的模块。
在出现这种情况时,你可以采取以下做法之一:
minChunks
,表示文件要被提取出来时需要在指定的 Chunks 中最小出现最小次数。
假如 minChunks=2
、chunks=['a','b','c','d']
,任何一个文件只要在 ['a','b','c','d']
中任意两个以上的 Chunk 中都出现过,这个文件就会被提取出来。
你可以根据自己的需求去调整 minChunks 的值,minChunks 越小越多的文件会被提取到 common.js
中去,但这也会导致部分页面加载的不相关的资源越多;
minChunks 越大越少的文件会被提取到 common.js
中去,但这会导致 common.js
变小、效果变弱。本实例提供项目完整代码