在 webpack < 4 的版本中,通常将 vendor 作为一个单独的入口起点添加到 entry 选项中,以将其编译为一个单独的文件(与 CommonsChunkPlugin 结合使用)。而在 webpack 4 中不鼓励这样做。而是使用 optimization.splitChunks 选项,将 vendor 和 app(应用程序) 模块分开,并为其创建一个单独的文件。不要 为 vendor 或其他不是执行起点创建 entry。
loader 从右到左(或从下到上)地取值(evaluate)/执行(execute)。
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true,
},
},
{ loader: 'sass-loader' },
],
},
],
},
};
在Webpack中,sideEffects
是一个用于标记模块是否有副作用(side effect)的配置选项。副作用是指模块在导入时对全局状态进行修改或对环境造成其他影响,例如修改文件系统、发起网络请求等。对于没有副作用的模块,Webpack可以利用这个信息进行Tree Shaking(摇树优化),将未被使用的代码从最终的打包文件中删除,以减小打包文件的体积。
sideEffects
的值可以是一个布尔值或一个数组。如果值为false
,表示该模块没有副作用,可以进行Tree Shaking优化;如果值为true
或者数组中包含"*"
,表示该模块具有副作用,不可进行Tree Shaking优化;如果值为一个数组,数组中包含的字符串表示该模块具有副作用的文件路径或通配符。
例如,如果你的项目中有一个模块app.js
,它没有副作用,那么可以在它的代码中添加如下注释:
/*#__PURE__*/ // 声明该模块没有副作用
export function add(x, y) {
return x + y;
}
然后在Webpack的配置文件中添加以下配置:
module.exports = {
// ...
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
optimization: {
usedExports: true
},
// ...
// 声明哪些模块没有副作用
sideEffects: [
"./src/add.js"
]
}
这样,在打包时,Webpack会根据sideEffects
的配置信息,将没有副作用的模块进行Tree Shaking优化,提高打包性能和减小打包体积。
在Webpack中,对于纯CSS或Less文件(即没有包含JavaScript代码),无法进行Tree Shaking优化,因为它们不会产生导出的JavaScript代码。Tree Shaking优化是通过检测导出的JavaScript代码的使用情况来删除未使用的代码,因此只有包含导出的JavaScript代码的模块才能进行Tree Shaking优化。
但是,如果你在Less或CSS中使用了类似于PostCSS、Autoprefixer等预处理器,它们可能会将部分JavaScript代码添加到生成的CSS文件中,例如添加浏览器前缀或者自动转换代码。在这种情况下,生成的CSS文件包含了导出的JavaScript代码,因此可以进行Tree Shaking优化。
要使Webpack可以对包含JavaScript代码的CSS或Less文件进行Tree Shaking优化,可以使用mini-css-extract-plugin
插件,将CSS或Less文件单独打包成一个JavaScript模块,然后再进行Tree Shaking优化。
例如,在Webpack配置文件中添加以下配置:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// ...
module: {
rules: [
{
test: /.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"less-loader",
{
loader: "postcss-loader",
options: {
plugins: [
require("autoprefixer")()
]
}
}
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
optimization: {
usedExports: true
}
}
这样,Webpack会将Less文件单独打包成一个JavaScript模块,然后在Tree Shaking优化时检测它的导出代码是否被使用,以删除未使用的代码。
未被引用的CSS或Less文件并不会自动被Webpack删除,因为它们虽然未被引用,但是它们仍然被包含在Webpack的打包文件中,因此仍然会增加打包文件的体积。
如果需要将未使用的CSS或Less文件从打包文件中删除,可以使用插件或者手动处理。
例如,可以使用purgecss-webpack-plugin
插件,在打包后对未使用的CSS文件进行删除。这个插件会分析打包后的HTML和JavaScript代码,检测哪些CSS样式类未被使用,然后将其从CSS文件中删除。
安装purgecss-webpack-plugin
:
npm install -D purgecss-webpack-plugin
在Webpack配置文件中添加以下配置:
const PurgeCssWebpackPlugin = require('purgecss-webpack-plugin');
const glob = require('glob');
module.exports = {
// ...
plugins: [
new PurgeCssWebpackPlugin({
paths: glob.sync(`${path.join(__dirname, 'src')}/**/*`, { nodir: true }),
// whitelist patterns
whitelistPatterns: [/^a-/, /^is-/, /^has-/, /^router-link-active$/]
})
]
}
这样,purgecss-webpack-plugin
就会在打包后检测未使用的CSS样式类,并将其从CSS文件中删除。
需要注意的是,purgecss-webpack-plugin
只能处理在HTML和JavaScript代码中使用的CSS样式类,无法处理在CSS文件中定义但未使用的样式类。因此,在使用该插件前需要仔细检查CSS文件,确保所有的样式类都被使用。
1.性能优化:Webpack 4具有更好的性能和更快的构建速度。新的版本使用了更好的算法和数据结构,以减少构建时间。此外,Webpack 4还通过将模块ID生成算法从数字更改为基于字符串的算法,实现了更好的缓存和更小的包大小。
2.模块优化:Webpack 4优化了模块系统,允许开发者使用ES6模块,并且默认启用Tree Shaking,可以消除未使用的代码,减少包的大小。
3.Mode选项:Webpack 4新增了mode选项,可以配置为“development”、“production”和“none”三种模式,每种模式都有不同的默认配置和插件,便于开发者进行不同环境的构建和优化。
4.新的插件:Webpack 4提供了许多新的插件和特性,如OptimizeCSSAssetsPlugin、SplitChunksPlugin、NoEmitOnErrorsPlugin等。
5.更好的文档:Webpack 4的文档进行了改进,更加清晰明了,方便开发者使用。
SplitChunksPlugin是Webpack 4中用于代码分割的新插件,与Webpack 3中的CommonsChunkPlugin相比,SplitChunksPlugin有以下几个优点:
更好的默认配置:SplitChunksPlugin提供了更好的默认配置,可以更好地适应不同的项目需求,而CommonsChunkPlugin需要手动配置选项。
更灵活的配置选项:SplitChunksPlugin提供了更灵活的配置选项,可以根据实际需求进行定制化设置,例如可以基于模块的大小、被引用的次数等进行分割。
更好的性能:SplitChunksPlugin相比CommonsChunkPlugin具有更好的性能,可以更有效地将代码分割为更小的块,并实现更好的缓存机制。
更好的Tree Shaking特性:SplitChunksPlugin可以更好地支持Tree Shaking特性,可以更有效地消除未使用的代码,从而减少打包的体积。
总的来说,SplitChunksPlugin在功能和性能上都比CommonsChunkPlugin更优秀,可以更好地满足现代前端项目的需求。但是,如果您正在使用Webpack 3,CommonsChunkPlugin仍然是可用的,并且可以实现代码分割的功能。
CommonsChunkPlugin是Webpack中用于提取公共代码的插件,它的作用是将多个入口文件中相同的代码提取到一个单独的文件中,以便在多个页面之间共享使用,避免代码重复和增加页面加载时间。以下是CommonsChunkPlugin的常用配置选项:
name
:表示提取出的公共模块的名称,可以使用字符串或者函数来指定名称。
filename
:表示生成的文件名,可以使用[hash]等占位符。
minChunks
:表示在多少个模块中使用的模块才会被提取为公共模块,默认值是2,也可以使用函数来指定。
chunks
:表示在哪些入口chunk中使用的模块才会被提取为公共模块,可以是一个字符串或字符串数组,也可以是一个函数。
children
:表示是否在所有的chunk中查找公共模块,默认值是false。
async
:表示将异步加载的模块中公共部分提取到单独的文件中。
Webpack 5 相比于 Webpack 4 做了很多升级,以下是一些主要的升级点:
更快的构建速度:Webpack 5 使用了一些新的技术,如持久化缓存和更好的多线程处理,从而提高了构建速度和性能。
更好的 Tree Shaking:Webpack 5 在 Tree Shaking 算法上进行了优化,可以更准确地识别和剔除没有使用的代码。
更好的模块解析:Webpack 5 对模块解析进行了升级,支持更多的模块类型和模块路径解析方式,例如支持 type: module
,从而使得 ES Modules 能够更好地工作。
更好的代码块拆分:Webpack 5 对代码块拆分进行了升级,支持动态导入的模块和代码块,可以更好地控制代码块的大小和数量。
更好的输出管理:Webpack 5 对输出管理进行了升级,支持更多的输出选项和格式,例如支持输出 ESM 格式的代码。
更好的错误处理:Webpack 5 对错误处理进行了升级,可以提供更详细和有用的错误信息和提示。
更好的插件系统:Webpack 5 对插件系统进行了升级,支持更多的插件选项和钩子,例如支持 beforeRun
和 afterEmit
两个新的钩子。
更好的缓存管理:Webpack 5 对缓存管理进行了升级,支持更多的缓存选项和控制方式,例如支持自定义缓存目录和缓存方式。
除了以上几点之外,Webpack 5 还有很多其他的升级和改进,可以去官方文档查看详细信息。总的来说,Webpack 5 对 Webpack 4 进行了全面的升级和改进,提高了构建速度、代码质量和开发体验。
Webpack对import()
的模块单独创建异步块的目的是实现代码分割(code splitting)和按需加载(on-demand loading)的优化策略。
代码分割是一种将代码拆分成较小块的技术,它可以使应用程序在加载时只请求所需的代码块,而不是一次性加载所有代码。这样可以减少初始加载时间,并提高应用程序的性能。通过将import()
的模块单独创建异步块,Webpack能够将这些模块的代码与主应用程序的代码分离,使得在初始加载时只加载必要的代码,延迟加载其他模块。
异步块的创建还使得按需加载成为可能。当应用程序需要某个模块时,可以通过动态导入(import()
)该模块来触发异步请求,从而按需加载该模块的代码。这种按需加载的方式可以根据用户操作、路由变化或其他条件来决定何时加载特定模块,以优化应用程序的性能和资源利用。
另外,通过将import()
的模块单独创建异步块,Webpack还能够利用浏览器的并行加载能力。当浏览器发起异步请求时,它可以同时请求多个文件,从而加快加载速度。
总之,通过将import()
的模块单独创建异步块,Webpack实现了代码分割和按需加载的优化策略,以提高应用程序的性能和加载速度。它允许开发人员根据需要延迟加载模块,减少初始加载的大小,并充分利用浏览器的并行加载能力。
如果一个模块没有显示的声明sideEffects,也没用添加/#PURE/这样的声明。那么就要依赖Terser来进行推断副作用了。但是在现实场景下,这是一件很困难的事情。贴一下webpack官方文档里的一个demo:
import { Button } from '@shopify/polaris';
import hoistStatics from 'hoist-non-react-statics';
function Button(_ref) {
// ...
}
function merge() {
var _final = {};
for (
var _len = arguments.length, objs = new Array(_len), _key = 0;
_key < _len;
_key++
) {
objs[_key] = arguments[_key];
}
for (var _i = 0, _objs = objs; _i < _objs.length; _i++) {
var obj = _objs[_i];
mergeRecursively(_final, obj);
}
return _final;
}
function withAppProvider() {
return function addProvider(WrappedComponent) {
var WithProvider =
/*#__PURE__*/
(function (_React$Component) {
// ...
return WithProvider;
})(Component);
WithProvider.contextTypes = WrappedComponent.contextTypes
? merge(WrappedComponent.contextTypes, polarisAppProviderContextTypes)
: polarisAppProviderContextTypes;
var FinalComponent = hoistStatics(WithProvider, WrappedComponent);
return FinalComponent;
};
}
var Button$1 = withAppProvider()(Button);
export {
// ...,
Button$1,
};
When
Button
is unused you can effectively remove theexport { Button$1 };
which leaves all the remaining code. So the question is “Does this code have any side effects or can it be safely removed?”. Difficult to say, especially because of this linewithAppProvider()(Button)
.withAppProvider
is called and the return value is also called. Are there any side effects when callingmerge
orhoistStatics
? Are there side effects when assigningWithProvider.contextTypes
(Setter?) or when readingWrappedComponent.contextTypes
(Getter?).Terser actually tries to figure it out, but it doesn’t know for sure in many cases. This doesn’t mean that terser is not doing its job well because it can’t figure it out. It’s too difficult to determine it reliably in a dynamic language like JavaScript.
reference: https://webpack.js.org/guides/tree-shaking/#clarifying-tree-shaking-and-sideeffects