持续开发和部署 web 项目时,对于引入的 script 脚步文件的缓存控制是一个比较重要的话题,通常使用 webpack 打包用于生产环境 js 文件时都在文件名中包含 chunkhash 值,即要求改动准确代表对应文件名变动,这样用户总是会获得最新的代码以获得正确的功能并也能利用浏览器缓存提高体验。
所有 js 代码,其必然包含两个部分:
通常应该将第一部分单独打包,命名为 venders,第二部分根据实际情况分为一到多个文件。现在我们使用 webpack 提供的 dll 插件来实现这一点。
首先建立一个 webpack.dll.config.js 文件,专门来生成 venders 文件的:
- import path from "path";
- import webpack from "webpack";
- export default {
- entry: {
- vendors: [
- "react",
- "react-dom",
- "react-router",
- "react-router-redux",
- "babel-polyfill",
- "redux",
- "redux-thunk",
- "react-redux",
- "axios",
- "classnames"
- ]
- },
- output: {
- path: path.resolve(__dirname, "./dist"),
- publicPath: "./",
- filename: "[name].[chunkhash:8].js",
- library: "[name]_[chunkhash:8]"
- },
- plugins: [
- new webpack.DllPlugin({
- context: __dirname,
- path: "manifest.json",
- name: "[name]_[chunkhash:8]"
- })
- ]
- };
上述配置文件中,我们声明了这个 vendors 文件包含 react 等第三方包,请注意 output.library 要保持和 dllplugin 中的 name 参数一致。
然后在我们正常的 webpack.config.js 文件中,plugins 部分添加一条记录:
- new webpack.DllReferencePlugin({
- context: __dirname,
- manifest: require("./manifest.json")
- }),
mainfest.json 文件是由 webpack.dll.config.js 配置下运行 webpack 生成的配置文件,包含所有引用的第三方包内引入的模块文件,并为他们赋予了唯一的数字 id。
这时通过修改业务代码,生成的 vendors 文件的 hash 后缀是不变的。
实际上,在我们自己的业务中,也存在一些不同页面复用的模块,如业务相关的一些 utils,如果它们有改动,那将影响所有引用了该模块的 chunk 的 hash 值。那么可以将这些公用的代码继续合并并单独分离为 common 代码,可以在 webpack.config 中 plugins 项添加一条记录:
- new webpack.optimize.CommonsChunkPlugin({
- name: "common"
- })
但是这时会发现不论修改了哪一部分代码,都会影响 common 文件的 chunkhash 值。
这是因为因为 webpack 的 runtime 代码也被作为公共代码引入到 common chunk 中了,而这一部分代码包含了所有 chunk 的 chunkhash 内容在其中。要解决这个问题,我们需要精准控制 common chunk 的内容。现在新添加一条记录:
- new webpack.optimize.CommonsChunkPlugin({
- name: "common",
- minChunks: ({ resource } = {}) => {
- return (
- resource && /utils\/([0-9a-zA-Z_-]+)\.js/i.test(resource)
- );
- }
- })
并将上一次添加的 common 记录修改为:
- new webpack.optimize.CommonsChunkPlugin({
- name: "manifest"
- })
添加 manifest chunk 是用于 webpack runtime 代码存放的,否则它仍然会放入最后一个可用的 bundle 代码里面,这里即是生成的 common 文件。
现在我们在代码中引入一个新模块,继续编译代码:
虽然我只修改了 pageA chunk 的引入,但是 pageB chunk 的 hash 也发生了变化,这个变化不是准确对应 chunk 文件变化的。
打开 pageB.f51b42fd.js 文件,有一部分代码如下:
- var _react = __webpack_require__(0);
- var _react2 = _interopRequireDefault(_react);
- var _modB = __webpack_require__(7);
- var modB = _interopRequireWildcard(_modB);
- var _utils = __webpack_require__(2);
而在之前,它们是
- var _react = __webpack_require__(0);
- var _react2 = _interopRequireDefault(_react);
- var _modB = __webpack_require__(6);
- var modB = _interopRequireWildcard(_modB);
- var _utils = __webpack_require__(2);
明显发现_modB 的引入 id 由 6 变成了 7,这个 id 就是 webpack 为每个模块赋予的数字 id,因为我们引入了新的模块,而 webpack 对模块编号是按照编译时引入顺序的,因此新模块可能占用了老模块的 id。要弥补这一点,我们可以使用另一种模块 id 模式,比如 webpack HashedModuleIdsPlugin 插件,基于模块路径 hash 的 id。现在在 webpack.config.js 的 plugins 项中添加一条记录:
- new webpack.HashedModuleIdsPlugin()
继续重复上面添加新模块前后对比操作:
但是问题依旧,对比了一下 pageB 生成文件前后代码,实际代码变动是没有的,仅有开头的变量名是正好对应文件 hash 的,这个是意料之外的,虽然 HashedModuleIdsPlugin 已经起到作用保证已有模块 id 不变,但是仍然不能阻止 pageB 的 chunkhash 变化,即使其代码内容没有变化。这个问题仍需要待解决。
本文未完待续。
来源: http://www.tuicool.com/articles/jiyY7rN