webpack 是一个 JavaScript 应用程序的 静态模块打包工具。
这份文档最重要的是:你的 webpack 配置可以有许多不同的格式和风格。关键在于,为了便于你和你的团队易于理解和维护这些配置,需要保证一致性。
前言:本文档旨在给出这些概念的高度概述,同时提供具体概念的详尽相关用例。
目录
(2)、如何根据 webpack.config.js 中的 mode 变量更改打包模式?
(1)、(推荐)在 webpack.config.js 文件中指定 loader
一、核心概念
1、入口(entry)
入口指的是:webpack 应该使用哪个模块,来作为构建其内部 依赖图 的开始。
默认值是: ./src/index.js。
你可以通过在 webpack configuration 中配置 entry 属性,来指定一个(或多个)不同的入口起点。
每个 HTML 文档只使用一个入口起点。
webpack.config.js
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js'
}
};
2、输出(output)
输出指的是: 控制 webpack 如何向硬盘写入编译文件(bundle),以及如何命名这些文件。
输出文件的默认值是: ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。
你可以通过在配置中指定一个 output 字段,来配置这些处理过程。
即使可以存在多个 entry 起点,也只能指定一个 output 配置。
(1)、低配
在 webpack 中配置 output
属性的最低要求是,将它的值设置为一个对象,包括以下属性:
filename
用于输出文件的文件名。
webpack.config.js
module.exports = {
output: {
filename: 'bundle.js',
}
};
此配置将一个单独的 bundle.js
文件输出到 dist
目录中。
(2)、中配
多个入口起点
如果配置创建了多个单独的 "chunk"(例如,使用多个入口起点或使用像 CommonsChunkPlugin 这样的插件),则应该使用 占位符(substitutions) 来确保每个文件具有唯一的名称。
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};
// 写入到硬盘:./dist/app.js, ./dist/search.js
(3)、高配
高级进阶
以下是对资源使用 CDN 和 hash 的复杂示例:
config.js
module.exports = {
//...
output: {
path: '/home/proj/cdn/assets/[hash]',
publicPath: 'http://cdn.example.com/assets/[hash]/'
}
};
如果在编译时,不知道最终输出文件的 publicPath
是什么地址,则可以将其留空,并且在运行时通过入口起点文件中的 __webpack_public_path__
动态设置。
__webpack_public_path__ = myRuntimePublicPath;
// 应用程序入口的其余部分
3、模式(mode)
提供 mode
配置选项,告知 webpack 使用相应环境的内置优化。
值包括:none, development 或 production(默认)。
(1)、用法
只需在配置对象中提供 mode
选项:
module.exports = {
mode: 'production'
};
或者从 CLI 参数中传递:
webpack --mode=production
支持以下字符串值:
选项 | 描述 |
---|---|
Development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。 启用 NamedChunksPlugin 和 NamedModulesPlugin。 |
Production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。 启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。 |
None | 退出任何默认优化选项 |
(2)、如何根据 webpack.config.js 中的 mode 变量更改打包模式?
如果要根据 webpack.config.js 中的 mode 变量更改打包行为,则必须将配置导出为一个函数,而不是导出为一个对象:
var config = {
entry: './app.js'
//...
};
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
//...
}
return config;
};
4、loader
webpack 只能理解 JavaScript 和 JSON 文件。
loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
loader 运行在 Node.js 中,支持任何 Node.js 能做到的操作。
在你的应用程序中,有 三种 使用 loader 的方式:
- 配置(推荐):在 webpack.config.js 文件中指定 loader。
- 内联:在每个
import
语句中显式指定 loader。 - CLI:在 shell 命令中指定它们。
(1)、(推荐)在 webpack.config.js 文件中指定 loader
这样展示 loader ,让你对各个 loader 有个全局概览,易于维护。
在 module.rules 中配置,允许指定多个 loader。
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
},
{ loader: 'sass-loader' }
]
}
]
}
};
在use数组中,loader 从右到左地 “取值-->执行” 。在上面的示例中,从 sass-loader 开始执行,然后继续执行 css-loader,最后以 style-loader 为结束。查看 loader 功能 了解有关 loader 顺序的更多信息。
(2)、在每个 import
语句中显式指定 loader
可以在 import
语句或任何 等同于 "import" 的方法 中指定 loader。使用 !
将资源中的 loader 分开。每个部分都会相对于当前目录解析。
import Styles from 'style-loader!css-loader?modules!./styles.css';
- 使用
!
为整个规则添加前缀,可以覆盖配置中的所有 loader 定义。 - 选项可以传递查询参数,例如
?key=value&foo=bar
,或者一个 JSON 对象,例如?{"key":"value","foo":"bar"}
。
(3)、在 shell 命令中指定loader
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
这会对 .jade
文件使用 jade-loader
,以及对 .css
文件使用 style-loader
和 css-loader
。
5、插件(plugin)
插件是 webpack 的支柱功能,插件目的在于解决 loader 无法实现的其他事,包括:打包优化,资源管理,注入环境变量。
想要使用一个插件,你只需要 require()
它,然后把它添加到 plugins
数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new
操作符来创建它的一个实例。
(1)、机制原理
webpack 插件是一个具有 apply
方法的 JavaScript 对象。apply
方法会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。
ConsoleLogOnBuildWebpackPlugin.js
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tap(pluginName, compilation => {
console.log('webpack 构建过程开始!');
});
}
}
compiler hook 的 tap 方法的第一个参数,应该是驼峰式命名的插件名称。建议为此使用一个常量,以便它可以在所有 hook 中复用。
(2)、用法
由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins
属性传入 new
实例。
根据你使用 webpack 的需要,这里有多种方式使用插件。
①、配置 webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); //通过 npm 安装
const webpack = require('webpack'); //访问内置的插件
const path = require('path');
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'my-first-webpack.bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: 'babel-loader'
}
]
},
plugins: [
new webpack.ProgressPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
②、Node API
在使用 Node API 时,还可以通过配置中的 plugins
属性传入插件。
some-node-script.js
const webpack = require('webpack'); //访问 webpack 运行时(runtime)
const configuration = require('./webpack.config.js');
let compiler = webpack(configuration);
new webpack.ProgressPlugin().apply(compiler);
compiler.run(function(err, stats) {
// ...
});
6、浏览器兼容性
webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import()
和 require.ensure()
需要 Promise
。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill。
7、部署目标
因为服务器和浏览器代码都可以用 JavaScript 编写,所以 webpack 提供了多种部署 target(目标),你可以在你的 webpack 配置对象 中进行设置。
(1)、基础用法
webpack.config.js
module.exports = {
target: 'node'
};
每个 target 都有各种部署(deployment)/环境(environment)特定的附加项,以支持满足其需求。查看 target 可用值。
(2)、多个 target
虽然 webpack 不支持向 target
传入多个字符串,还是可以通过打包两个单独配置,来创建出一个同构的 library:
webpack.config.js
const path = require('path');
const serverConfig = {
target: 'node',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.node.js'
}
//…
};
const clientConfig = {
target: 'web', // <=== 默认是 'web',可省略
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.js'
}
//…
};
module.exports = [ serverConfig, clientConfig ];
上面的例子将在 dist
文件夹下创建 lib.js
和 lib.node.js
文件。
(3)、资源
从上面的选项能够看出,可以选择多种不同的部署 target。下面是一个示例列表,以及可以参考的资源。
- compare-webpack-target-bundles:有关「测试和查看」不同的 webpack target 的大量资源。也有大量 bug 报告。
- Boilerplate of Electron-React Application:一个 electron 主进程和渲染进程构建过程的很好的例子。
8、模块热替换
模块热替换(HMR)功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
- 保留在完全重新加载页面期间丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 在源代码中对 CSS/JS 进行修改,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
这一切是如何运行的(原理)?请戳此链接:https://webpack.docschina.org/concepts/hot-module-replacement/
二、了解运作机制
为了更好地理解模块打包工具背后的理念,以及在底层它们是如何运作的,请参考以下资源:
三、配置webpack的注意事项
- 在使用 webpack 命令行接口(CLI)时,访问命令行接口(CLI)参数(应该编写自己的命令行接口(CLI),或 使用 --env)
- 导出不确定的值(调用 webpack 两次应该产生同样的输出文件)
- 编写很长的配置(应该将配置拆分为多个文件)
后记:
1、待回头研究的技能点:使用webpack可预测的长效缓存:https://webpack.docschina.org/concepts/manifest/