配置
一、配置概览
const path = require('path');
module.exports = {
mode: "production", // "production" | "development" | "none"
mode: "production", // enable many optimizations for production builds
mode: "development", // enabled useful tools for development
mode: "none", // no defaults
// Chosen mode tells webpack to use its built-in optimizations accordingly.
entry: "./app/entry", // string | object | array
entry: ["./app/entry1", "./app/entry2"],
entry: {
a: "./app/entry-a",
b: ["./app/entry-b1", "./app/entry-b2"]
},
// 默认为 './src'
// 这里应用程序开始执行
// webpack 开始打包
output: {
// webpack 如何输出结果的相关选项
path: path.resolve(__dirname, "dist"), // string
// 所有输出文件的目标路径
// 必须是绝对路径(使用 Node.js 的 path 模块)
filename: "bundle.js", // string
filename: "[name].js", // 用于多个入口点(entry point)(出口点?)
filename: "[chunkhash].js", // 用于长效缓存
// 「入口分块(entry chunk)」的文件名模板
publicPath: "/assets/", // string
publicPath: "",
publicPath: "https://cdn.example.com/",
// 输出解析文件的目录,url 相对于 HTML 页面
library: "MyLibrary", // string,
// 导出库(exported library)的名称
libraryTarget: "umd", // 通用模块定义
libraryTarget: "umd2", // 通用模块定义
libraryTarget: "commonjs2", // exported with module.exports
libraryTarget: "commonjs", // 作为 exports 的属性导出
libraryTarget: "amd", // 使用 AMD 定义方法来定义
libraryTarget: "this", // 在 this 上设置属性
libraryTarget: "var", // 变量定义于根作用域下
libraryTarget: "assign", // 盲分配(blind assignment)
libraryTarget: "window", // 在 window 对象上设置属性
libraryTarget: "global", // property set to global object
libraryTarget: "jsonp", // jsonp wrapper
// 导出库(exported library)的类型
/* 高级输出配置(点击显示) */
pathinfo: true, // boolean
// 在生成代码时,引入相关的模块、导出、请求等有帮助的路径信息。
chunkFilename: "[id].js",
chunkFilename: "[chunkhash].js", // 长效缓存(/guides/caching)
// 「附加分块(additional chunk)」的文件名模板
jsonpFunction: "myWebpackJsonp", // string
// 用于加载分块的 JSONP 函数名
sourceMapFilename: "[file].map", // string
sourceMapFilename: "sourcemaps/[file].map", // string
// 「source map 位置」的文件名模板
devtoolModuleFilenameTemplate: "webpack:///[resource-path]", // string
// 「devtool 中模块」的文件名模板
devtoolFallbackModuleFilenameTemplate: "webpack:///[resource-path]?[hash]", // string
// 「devtool 中模块」的文件名模板(用于冲突)
umdNamedDefine: true, // boolean
// 在 UMD 库中使用命名的 AMD 模块
crossOriginLoading: "use-credentials", // 枚举
crossOriginLoading: "anonymous",
crossOriginLoading: false,
// 指定运行时如何发出跨域请求问题
/* 专家级输出配置(自行承担风险) */
devtoolLineToLine: {
test: /\.jsx$/
},
// 为这些模块使用 1:1 映射 SourceMaps(快速)
hotUpdateMainFilename: "[hash].hot-update.json", // string
// 「HMR 清单」的文件名模板
hotUpdateChunkFilename: "[id].[hash].hot-update.js", // string
// 「HMR 分块」的文件名模板
sourcePrefix: "\t", // string
// 包内前置式模块资源具有更好可读性
},
module: {
// 关于模块配置
rules: [
// 模块规则(配置 loader、解析器等选项)
{
test: /\.jsx?$/,
include: [
path.resolve(__dirname, "app")
],
exclude: [
path.resolve(__dirname, "app/demo-files")
],
// 这里是匹配条件,每个选项都接收一个正则表达式或字符串
// test 和 include 具有相同的作用,都是必须匹配选项
// exclude 是必不匹配选项(优先于 test 和 include)
// 最佳实践:
// - 只在 test 和 文件名匹配 中使用正则表达式
// - 在 include 和 exclude 中使用绝对路径数组
// - 尽量避免 exclude,更倾向于使用 include
issuer: { test, include, exclude },
// issuer 条件(导入源)
enforce: "pre",
enforce: "post",
// 标识应用这些规则,即使规则覆盖(高级选项)
loader: "babel-loader",
// 应该应用的 loader,它相对上下文解析
// 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
// 查看 webpack 1 升级指南。
options: {
presets: ["es2015"]
},
// loader 的可选项
},
{
test: /\.html$/,
use: [
// 应用多个 loader 和选项
"htmllint-loader",
{
loader: "html-loader",
options: {
/* ... */
}
}
]
},
{ oneOf: [ /* rules */ ] },
// 只使用这些嵌套规则之一
{ rules: [ /* rules */ ] },
// 使用所有这些嵌套规则(合并可用条件)
{ resource: { and: [ /* 条件 */ ] } },
// 仅当所有条件都匹配时才匹配
{ resource: { or: [ /* 条件 */ ] } },
{ resource: [ /* 条件 */ ] },
// 任意条件匹配时匹配(默认为数组)
{ resource: { not: /* 条件 */ } }
// 条件不匹配时匹配
],
/* 高级模块配置(点击展示) */
noParse: [
/special-library\.js$/
],
// 不解析这里的模块
unknownContextRequest: ".",
unknownContextRecursive: true,
unknownContextRegExp: /^\.\/.*$/,
unknownContextCritical: true,
exprContextRequest: ".",
exprContextRegExp: /^\.\/.*$/,
exprContextRecursive: true,
exprContextCritical: true,
wrappedContextRegExp: /.*/,
wrappedContextRecursive: true,
wrappedContextCritical: false,
// specifies default behavior for dynamic requests
},
resolve: {
// 解析模块请求的选项
// (不适用于对 loader 解析)
modules: [
"node_modules",
path.resolve(__dirname, "app")
],
// 用于查找模块的目录
extensions: [".js", ".json", ".jsx", ".css"],
// 使用的扩展名
alias: {
// 模块别名列表
"module": "new-module",
// 起别名:"module" -> "new-module" 和 "module/path/file" -> "new-module/path/file"
"only-module$": "new-module",
// 起别名 "only-module" -> "new-module",但不匹配 "only-module/path/file" -> "new-module/path/file"
"module": path.resolve(__dirname, "app/third/module.js"),
// 起别名 "module" -> "./app/third/module.js" 和 "module/file" 会导致错误
// 模块别名相对于当前上下文导入
},
/* 可供选择的别名语法(点击展示) */
alias: [
{
name: "module",
// 旧的请求
alias: "new-module",
// 新的请求
onlyModule: true
// 如果为 true,只有 "module" 是别名
// 如果为 false,"module/inner/path" 也是别名
}
],
/* 高级解析选项(点击展示) */
symlinks: true,
// 遵循符号链接(symlinks)到新位置
descriptionFiles: ["package.json"],
// 从 package 描述中读取的文件
mainFields: ["main"],
// 从描述文件中读取的属性
// 当请求文件夹时
aliasFields: ["browser"],
// 从描述文件中读取的属性
// 以对此 package 的请求起别名
enforceExtension: false,
// 如果为 true,请求必不包括扩展名
// 如果为 false,请求可以包括扩展名
moduleExtensions: ["-module"],
enforceModuleExtension: false,
// 类似 extensions/enforceExtension,但是用模块名替换文件
unsafeCache: true,
unsafeCache: {},
// 为解析的请求启用缓存
// 这是不安全,因为文件夹结构可能会改动
// 但是性能改善是很大的
cachePredicate: (path, request) => true,
// predicate function which selects requests for caching
plugins: [
// ...
]
// 应用于解析器的附加插件
},
performance: {
hints: "warning", // 枚举
hints: "error", // 性能提示中抛出错误
hints: false, // 关闭性能提示
maxAssetSize: 200000, // 整数类型(以字节为单位)
maxEntrypointSize: 400000, // 整数类型(以字节为单位)
assetFilter: function(assetFilename) {
// 提供资源文件名的断言函数
return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
}
},
devtool: "source-map", // enum
devtool: "inline-source-map", // 嵌入到源文件中
devtool: "eval-source-map", // 将 SourceMap 嵌入到每个模块中
devtool: "hidden-source-map", // SourceMap 不在源文件中引用
devtool: "cheap-source-map", // 没有模块映射(module mappings)的 SourceMap 低级变体(cheap-variant)
devtool: "cheap-module-source-map", // 有模块映射(module mappings)的 SourceMap 低级变体
devtool: "eval", // 没有模块映射,而是命名模块。以牺牲细节达到最快。
// 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
// 牺牲了构建速度的 `source-map' 是最详细的。
context: __dirname, // string(绝对路径!)
// webpack 的主目录
// entry 和 module.rules.loader 选项
// 相对于此目录解析
target: "web", // 枚举
target: "webworker", // WebWorker
target: "node", // node.js 通过 require
target: "async-node", // Node.js 通过 fs 和 vm
target: "node-webkit", // nw.js
target: "electron-main", // electron,主进程(main process)
target: "electron-renderer", // electron,渲染进程(renderer process)
target: (compiler) => { /* ... */ }, // 自定义
// bundle 应该运行的环境
// 更改 块加载行为(chunk loading behavior) 和 可用模块(available module)
externals: ["react", /^@angular\//],
externals: "react", // string(精确匹配)
externals: /^[a-z\-]+($|\/)/, // 正则
externals: { // 对象
angular: "this angular", // this["angular"]
react: { // UMD
commonjs: "react",
commonjs2: "react",
amd: "react",
root: "React"
}
},
externals: (request) => { /* ... */ return "commonjs " + request }
// 不要遵循/打包这些模块,而是在运行时从环境中请求他们
serve: { //object
port: 1337,
content: './dist',
// ...
},
// 为 webpack-serve 提供选项
stats: "errors-only",
stats: { //object
assets: true,
colors: true,
errors: true,
errorDetails: true,
hash: true,
// ...
},
// 精确控制要显示的 bundle 信息
devServer: {
proxy: { // proxy URLs to backend development server
'/api': 'http://localhost:3000'
},
contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
compress: true, // enable gzip compression
historyApiFallback: true, // true for index.html upon 404, object for multiple paths
hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
https: false, // true for self-signed, object for cert authority
noInfo: true, // only errors & warns on hot reload
// ...
},
plugins: [
// ...
],
// 附加插件列表
/* 高级配置(点击展示) */
resolveLoader: { /* 等同于 resolve */ }
// 独立解析选项的 loader
parallelism: 1, // number
// 限制并行处理模块的数量
profile: true, // boolean
// 捕获时机信息
bail: true, //boolean
// 在第一个错误出错时抛出,而不是无视错误。
cache: false, // boolean
// 禁用/启用缓存
watch: true, // boolean
// 启用观察
watchOptions: {
aggregateTimeout: 1000, // in ms
// 将多个更改聚合到单个重构建(rebuild)
poll: true,
poll: 500, // 间隔单位 ms
// 启用轮询观察模式
// 必须用在不通知更改的文件系统中
// 即 nfs shares(译者注:Network FileSystem,最大的功能就是可以透過網路,讓不同的機器、不同的作業系統、可以彼此分享個別的檔案 ( share file ))
},
node: {
// Polyfills and mocks to run Node.js-
// environment code in non-Node environments.
console: false, // boolean | "mock"
global: true, // boolean | "mock"
process: true, // boolean
__filename: "mock", // boolean | "mock"
__dirname: "mock", // boolean | "mock"
Buffer: true, // boolean | "mock"
setImmediate: true // boolean | "mock" | "empty"
},
recordsPath: path.resolve(__dirname, "build/records.json"),
recordsInputPath: path.resolve(__dirname, "build/records.json"),
recordsOutputPath: path.resolve(__dirname, "build/records.json"),
// TODO
}
二、入口entry
1、context
string
,基础目录,绝对路径,用于从配置中解析入口起点和loader
module.exports = {
// 默认当前路径
context: path.resolve(__dirname, 'app')
};
2、entry
string | [string] | object { <key>: string | [string] } | (function: () => string | [string] | object { <key>: string | [string] })
3、命名
如果传入一个字符串或字符串数组,chunk 会被命名为 main
。如果传入一个对象,则每个键(key)会是 chunk 的名称,该值描述了 chunk 的入口起点。
三、输出output
output.filename
string | function
此选项决定了每个输出bundle的名称,将这些bundle写入到output.path
选项指定的目录下。
// 单入口起点
module.exports = {
//...
output: {
filename: 'bundle.js'
}
};
// 多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin)创建多个 bundle
// 使用入口名称
module.exports = {
//...
output: {
filename: '[name].bundle.js'
}
};
// 使用内部chunk id
module.exports = {
//...
output: {
filename: '[id].bundle.js'
}
};
// 使用每次构建过程中,唯一的 hash 生成
module.exports = {
//...
output: {
filename: '[name].[hash].bundle.js'
}
};
// 使用基于每个chunk内容的hash
module.exports = {
//...
output: {
filename: '[chunkhash].bundle.js'
}
}
//使用为提取内容生成的hash
module.exports = {
//...
output: {
filename: '[contenthash].bundle.css'
}
};
module.exports = {
//...
output: {
filename: (chunkData) => {
return chunkData.chunk.name === 'main' ? '[name].js': '[name]/[name].js';
},
}
};
模板 | 描述 |
---|---|
[hash] | 模块标识符(module identifier)的 hash |
[chunkhash] | chunk 内容的 hash |
[name] | 模块名称 |
[id] | 模块标识符(module identifier) |
[query] | 模块的 query,例如,文件名 ? 后面的字符串 |
[function] | 返回字符串类型的filename |
[hash]
和 [chunkhash]
的长度可以使用 [hash:16]
(默认为20)来指定。或者,通过指定output.hashDigestLength
在全局配置长度。
output.chunkFilename
string
,此选项决定了非入口chunk文件的名称,默认使用[id].js
output.hashDigest
生成hash的时候使用编码方式,默认是hex
output.hashDigestLength
散列摘要的前缀长度,默认是20
output.libraryTarget
string: 'var'
配置如何暴露 library。可以使用下面的选项中的任意一个。
module.exports = {
//...
output: {
library: 'MyLibrary',
libraryTarget: 'var'
}
};
// libraryTarget: 'var' --暴露为一个变量
var MyLibrary = _entry_return_;
// 在一个单独的 script……
MyLibrary.doSomething();
// libraryTarget: 'assign' --暴露为一个变量
MyLibrary = _entry_return_;
// libraryTarget: "this" - 入口起点的返回值将分配给 this 的一个属性
this['MyLibrary'] = _entry_return_;
// 在一个单独的 script……
this.MyLibrary.doSomething();
MyLibrary.doSomething(); // 如果 this 是 window
// libraryTarget: 'window' --入口起点的返回值将分配给 window 的一个属性
window['MyLibrary'] = _entry_return_;
window.MyLibrary.doSomething();
// libraryTarget: 'global' --入口起点的返回值将分配给 global 的一个属性
global['MyLibrary'] = _entry_return_;
global.MyLibrary.doSomething();
// libraryTarget: 'commonjs' --入口起点的返回值将分配给 exports 对象
exports['MyLibrary'] = _entry_return_;
require('MyLibrary').doSomething();
// libraryTarget: 'commonjs2' --入口起点的返回值将分配给 exports 对象
exports['MyLibrary'] = _entry_return_;
require('MyLibrary').doSomething();
。。。
output.library
module.exports = {
//...
output: {
library: 'MyLibrary'
}
};
output.path
string
output 目录对应的一个绝对路径
module.exports = {
//...
output: {
path: path.resolve(__dirname, 'dist')
}
};
output.publicPath
string: '' | function
此选项指定在浏览器中所引用的「此输出目录对应的公开 URL」。此选项的值都会以 /
结束。
module.exports = {
//...
output: {
// One of the below
publicPath: 'https://cdn.example.com/assets/', // CDN(总是 HTTPS 协议)
publicPath: '//cdn.example.com/assets/', // CDN(协议相同)
publicPath: '/assets/', // 相对于服务(server-relative)
publicPath: 'assets/', // 相对于 HTML 页面
publicPath: '../assets/', // 相对于 HTML 页面
publicPath: '', // 相对于 HTML 页面(目录相同)
}
};
四、模块module
module.rules
module.exports = {
module: {
rules: [{
test: /\.css/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}]
}
}
test | exclude | include | and | or | not
{ test: Condition }
:匹配特定条件。一般是提供一个正则表达式或正则表达式的数组,但这不是强制的。{ include: Condition }
:匹配特定条件。一般是提供一个字符串或者字符串数组,但这不是强制的。{ exclude: Condition }
:排除特定条件。一般是提供一个字符串或字符串数组,但这不是强制的。{ and: [Condition] }
:必须匹配数组中的所有条件{ or: [Condition] }
:匹配数组中任何一个条件{ not: [Condition] }
:必须排除这个条件jsmodule.exports = { //... module: { rules: [ { test: /\.css$/, include: [ path.resolve(__dirname, 'app/styles'), path.resolve(__dirname, 'vendor/styles') ] } ] } };
loader
jsmodule.exports = { //... module: { rules: [ { loader: 'css-loader', options: { modules: true } } ] } };
use
jsmodule.exports = { //... module: { rules: [ { //... use: [ 'style-loader', { loader: 'css-loader', options: { importLoaders: 1 } }, { loader: 'less-loader', options: { noIeCompat: true } } ] } ] } };
五、解析resolve
module.exports = {
//...
resolve: {
// configuration options
}
};
resolve.alias
object
创建import
或require
的别名,来确保模块引入变得更简单。
module.exports = {
//...
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
}
}
};
// 替换前
import Utility from '../../utilities/utility';
// 替换后
import Utility from 'Utilities/utility';
resolve.enforceExtension
boolean: false
module.exports = {
//...
resolve: {
enforceExtension: false // 如果是 true,将不允许无扩展名(extension-less)文件。默认如果 ./foo 有 .js 扩展,require('./foo') 可以正常运行。
}
};
resolve.extensions
[string]: ['.wasm', '.mjs', '.js', '.json']
module.exports = {
//...
resolve: {
extensions: ['.wasm', '.mjs', '.js', '.json']
}
};
resolve.mainFields
[string]
当从 npm 包中导入模块时(例如,import * as D3 from 'd3'
),此选项将决定在 package.json
中使用哪个字段导入模块。根据 webpack 配置中指定的 target
不同,默认值也会有所不同。默认值如下:
module.exports = {
//...
resolve: {
mainFields: ['browser', 'module', 'main']
}
};
resolve.mainFiles
[string]: ['index']
解析目录时要使用的文件名。例如:import * as dd from './util'
对应的是 import * as dd from './util/index'
resolve.modules
[string]: ['node_modules']
告诉webpack解析模块的时候应该搜索的目录
resolve.unsafeCache
regex
array
boolean: true
启用,会主动缓存模块,但并不安全。传递 true
将缓存一切。默认:
六、优化optimization
optimization.minimize
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimize: false,
},
};
optimization.minimizer
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
sourceMap: true, // Must be set to true if using source-maps in production
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
}
}),
],
}
};
optimization.splitChunks
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
// 第三方组件
libs: {
// 指定chunks名称
name: 'chunk-libs',
//符合组的要求就给构建venders
test: /[\\/]node_modules[\\/]/,
//priority:优先级:数字越大优先级越高,因为默认值为0,所以自定义的一般是负数形式,决定cacheGroups中相同条件下每个组执行的优先顺序。
priority: 10,
// 仅限于最初依赖的第三方
chunks: 'all'
},
elementUI: {
// 将elementUI拆分为单个包
name: 'chunk-elementUI',
// 重量需要大于libs和app,否则将打包到libs或app中
priority: 20,
// 为了适应cnpm
test: /[\\/]node_modules[\\/]_?element-ui(.*)/
},
//公共组件
// commons: {
// name: 'chunk-commons',
// // can customize your rules
// test: resolve('src/components'),
// minChunks: 3,
// priority: 30,
// //这个的作用是当前的chunk如果包含了从main里面分离出来的模块,则重用这个模块,这样的问题是会影响chunk的名称。
// reuseExistingChunk: true,
// //最大初始化加载次数,一个入口文件可以并行加载的最大文件数量,默认3
// maxInitialRequests: 3,
// //表示在分离前的最小模块大小,默认为0,最小为30000
// minSize: 0
// },
}
}
}
};
optimization.removeAvailableModules
bool: true
如果模块已经包含在所有父级模块中,告知 webpack 从 chunk 中检测出这些模块,或移除这些模块。将 optimization.removeAvailableModules
设置为 false
以禁用这项优化。
optimization.removeEmptyChunks
bool: true
如果 chunk 为空,告知 webpack 检测或移除这些 chunk。将 optimization.removeEmptyChunks
设置为 false
以禁用这项优化。
optimization.mergeDuplicateChunks
bool: true
告知 webpack 合并含有相同模块的 chunk。将 optimization.mergeDuplicateChunks
设置为 false
以禁用这项优化。
optimization.usedExports
标记未使用的导出
optimization.sideEffects
告诉 webpack 识别 package.json 中的 sideEffect 标志,或者跳过标记为在不使用导出时不包含副作用的模块。
七、开发中devServer
devServer.after
function (app, server)
在服务内部的所有其他中间件之后, 提供执行自定义中间件的功能。
module.exports = {
//...
devServer: {
after: function(app, server) {
app.get('/custom-route', (req, res) => {
console.log('Custom route was hit!');
res.json({ message: 'This is a custom route!' });
});
// 你也可以在这里直接使用 console.log 来输出信息,
// 这些信息会在开发服务器启动后立即显示。
console.log('The dev server has started and the after hook has been called.');
}
}
};
devServer.before
function (app, server)
在服务内部的所有其他中间件之前, 提供执行自定义中间件的功能。 这可以用来配置自定义处理程序
devServer.compress
// 启用gzip压缩
module.exports = {
//...
devServer: {
compress: true
}
};
注意
注意,必须有 webpack.HotModuleReplacementPlugin
才能完全启用 HMR。如果 webpack
或 webpack-dev-server
是通过 --hot
选项启动的,那么这个插件会被自动添加,所以你可能不需要把它添加到 webpack.config.js
中
devServer.open
告诉 dev-server 在 server 启动后打开浏览器。默认禁用。
module.exports = {
//...
devServer: {
open: true
}
};
devServer.openPage
指定打开浏览器时的导航页面。
module.exports = {
//...
devServer: {
openPage: '/different/page'
}
};
devServer.port
指定要监听请求的端口号
module.exports = {
//...
devServer: {
port: 8080
}
};
devServer.proxy
如果你有单独的后端开发服务器 API,并且希望在同域名下发送 API 请求 ,那么代理某些 URL 会很有用。
module.exports = {
//...
devServer: {
proxy: {
'/sode/api': {
target: process.env.VUE_APP_SODE_BACK_PATH,
ws: true,
changeOrigin: true,
pathRewrite: {
'/sode/api': process.env.VUE_APP_LOGINMETHOD === 'default' ? '' : ''
}
},
}
}
};
devServer.progress
将运行进度输出到控制台。只适用于命令行
webpack-dev-server --progress
devServer.publicPath
此路径下的打包文件可在浏览器中访问。
module.exports = {
//...
devServer: {
publicPath: '/notes/'
}
};
devServer.quiet
boolean
启用 devServer.quiet
后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。