Webpack4引入了SplitChunksPlugin,这使得commonsChunksPlugin变得过时。如何拆分输出代码以提高应用程序的性能是本文的重点。
webpack中的代码拆分究竟是什么?
它允许您将代码拆分为多个文件。如果使用得当,它可以大大提高应用程序的性能。其中的原因是,浏览器正在缓存您的代码。每次进行更改时,包含它的文件都必须由访问您网站的所有人重新下载。但是,您可能不会更改依赖项。如果将它们拆分为单独的文件,则访问者不必再次下载它。
Entry入口
Entry可以定义一个入口点(单页应用程序会发生这种情况)或多个入口点(使用多页面应用程序)。如果只使用字符串定义一个入口点,那么它将被命名为main。如果您使用一个对象定义更多,它们将以Entry对象的参数命名。下面的例子是等效的:
entry: './src/index.js'
entry: {
main: './src/index.js'
}
Output输出
Output输出对象是Webpack应该如何以及在何处输出我们的包等配置。由于我们想要拆分我们的代码,所以您可以使用[name]为输出文件的文件名:
output: {
filename: '[name].[chunkhash].bundle.js',
path: path.resolve(__dirname, 'dist')
}
这里要注意的一件重要事情是[chunkhash]:它是一个特定于块的哈希,它将根据文件的内容生成。只有当文件本身的内容更改时,它才会更改,否则浏览器会缓存它。如果文件名发生更改,浏览器将知道需要重新加载。chunkhash的一个例子如: 0c553ebfd158e16da428。我们的主块将绑定到名为main.[chunkash].bundle.js的文件中。
SplitChunksPlugin
SplitChunksPlugin可以将应用程序的某些部分移动到单独的文件中。如果一个模块在多个块中使用,那么可以很容易地在它们之间共享。这是Webpack的默认行为。
utilities/users.js
export default [
{ firstName: "Adam", age: 28 },
{ firstName: "Jane", age: 24 },
{ firstName: "Ben", age: 31 },
{ firstName: "Lucy", age: 40 }
]
a.js
import _ from 'lodash';
import users from './users';
const adam = _.find(users, { firstName: 'Adam' });
b.js
import _ from 'lodash';
import users from './users';
const lucy = _.find(users, { firstName: 'Lucy' });
webpack.config.js
module.exports = {
entry: {
a: "./src/a.js",
b: "./src/b.js"
},
output: {
filename: "[name].[chunkhash].bundle.js",
path: __dirname + "/dist"
}
};
运行它,您可以看到webpack创建了两个文件:a.[chunkash].bundle.js和b.[chunkash].bundle.js,每个文件都包含一个lodash库的副本:这不是很好!之前说过,为共享库创建单独的文件是webpack的默认行为,但这与异步块有关,这意味着我们异步导入的文件。在描述懒惰加载时,我们将更详细地讨论这个主题。要涉及所有类型的块,我们需要稍微更改webpack配置:
webpack.config.js
module.exports = {
entry: {
a: "./src/a.js",
b: "./src/b.js"
},
output: {
filename: "[name].[chunkhash].bundle.js",
path: __dirname + "/dist"
},
optimization: {
splitChunks: {
chunks: "all"
}
},
};
现在我们可以看到vendors~a~b.[chunkhash].bundle.js文件已创建并包含Lodash库。即默认情况下我们有一些开箱即用的cacheGroups配置:
splitChunks: {
chunks: "all",
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
首先是包含node_modules文件的vendors。其次是所有其他共享模块的默认缓存组。这里有一个小问题:发生了冗余。a.[chunkash].bundle.js和b.[chunkash].bundle.js都包含users.js内容。这是因为默认情况下,SplitChunksPlugin仅对大于30KB的文件拆分块。我们可以很容易地改变:
webpack.config.js
module.exports = {
entry: {
a: "./src/a.js",
b: "./src/b.js"
},
output: {
filename: "[name].[chunkhash].bundle.js",
path: __dirname + "/dist"
},
optimization: {
splitChunks: {
chunks: "all",
minSize: 0
}
}
};
这导致创建了一个名为a~b.[chunkash].bundle.js的新文件。它是这里的默认缓存组。由于我们的users.js文件占用的空间比30Kb少得多,因此在不更改minSize属性的情况下,它不会捆绑到单独的文件中。实际情况下,这是一件好事,因为这不会给我们任何真正的性能提升,并会迫使浏览器对现在非常小的utilities.js文件做出额外的请求。
我们甚至可以更进一步,只是对utilities目录中的文件进行异常处理:
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
a: "./src/a.js",
b: "./src/b.js"
},
output: {
filename: "[name].[chunkhash].bundle.js",
path: __dirname + "/dist"
},
optimization: {
splitChunks: {
chunks: "all",
cacheGroups: {
utilities: {
test: /[\\/]src[\\/]utilities[\\/]/,
minSize: 0
}
}
}
}
};
现在我们的bundle包含4个文件:a.[chunkash].bundle.js,b.[chunkash].bundle.js,vendors~a~b.[chunkash].bundle.js和utilities~a~b.[chunkash].bundle.js。即使我们现在将minSize:0设置为全局设置(在splitchunks对象中),也不会创建默认缓存组。这是因为我们刚刚创建的实用程序组涵盖了可能包含的所有文件。它的默认优先级为0,高于默认缓存组。正如您可能已经注意到的,默认缓存组的优先级设置为-20。
您还可以设置其他默认参数,您可以在SplitChunksPlugin文档中查看。
提示:即使只有一个入口点(在大多数单页应用程序中都会发生这种情况),也最好将依赖项保存在单独的文件中。这实际上是非常简单的,因为使用splitchunksplugin是Webpack4的默认行为,并且它可能足够您在splitchunks配置中设置块:“all”。