注意:这是对原项目readme文件的翻译,为啥翻译这个呢,由于Vue CLI3脚手架生成的项目使用这种方式配置webpack,可是脚手架中对这块的介绍很少,因此把这部分翻译出来,以供团队和你们参考。css
应用一个链式 API 来生成和简化 2-4 版本的webpack的配置的修改。html
此文档对应于webpack-chain的v5版本,对于之前的版本,请参阅:node
注意: 虽然 webpack-chain 被普遍应用在Neutrino中,然而本软件包彻底独立,可供任何项目使用。jquery
webpack 的核心配置的建立和修改基于一个有潜在难于处理的 JavaScript 对象。虽然这对于配置单个项目来讲仍是 OK 的,但当你尝试跨项目共享这些对象并使其进行后续的修改就会变的混乱不堪,由于您须要深刻了解底层对象的结构以进行这些更改。webpack
webpack-chain
尝试经过提供可链式或顺流式的 API 建立和修改webpack 配置。API的 Key 部分能够由用户指定的名称引用,这有助于 跨项目修改配置方式 的标准化。git
经过如下示例能够更容易地解释这一点。github
webpack-chain
须要 Node.js v6.9及更高版本. webpack-chain
也只建立并被设计于使用webpack的2,3,4版本的配置对象。web
你可使用Yarn或者npm来安装此软件包(俩个包管理工具选一个就行):express
yarn add --dev webpack-chain
npm install --save-dev webpack-chain
当你安装了 webpack-chain
, 你就能够开始建立一个webpack的配置。 对于本指南,咱们的示例基本配置 webpack.config.js
将位于咱们项目的根目录。npm
// 导入 webpack-chain 模块,该模块导出了一个用于建立一个webpack配置API的单一构造函数。 const Config = require('webpack-chain'); // 对该单一构造函数建立一个新的配置实例 const config = new Config(); // 用链式API改变配置 // 每一个API的调用都会跟踪对存储配置的更改。 config // 修改 entry 配置 .entry('index') .add('src/index.js') .end() // 修改 output 配置 .output .path('dist') .filename('[name].bundle.js'); // 建立一个具名规则,之后用来修改规则 config.module .rule('lint') .test(/\.js$/) .pre() .include .add('src') .end() // 还能够建立具名use (loaders) .use('eslint') .loader('eslint-loader') .options({ rules: { semi: 'off' } }); config.module .rule('compile') .test(/\.js$/) .include .add('src') .add('test') .end() .use('babel') .loader('babel-loader') .options({ presets: [ ['@babel/preset-env', { modules: false }] ] }); // 也能够建立一个具名的插件! config .plugin('clean') .use(CleanPlugin, [['dist'], { root: '/dir' }]); // 导出这个修改完成的要被webpack使用的配置对象 module.exports = config.toConfig();
共享配置也很简单。仅仅导出配置 和 在传递给webpack以前调用 .toConfig()
方法将配置导出给webpack使用。
// webpack.core.js const Config = require('webpack-chain'); const config = new Config(); // 跨目标共享配置 // Make configuration shared across targets // ... module.exports = config; // webpack.dev.js const config = require('./webpack.core'); // Dev-specific configuration // 开发具体配置 // ... module.exports = config.toConfig(); // webpack.prod.js const config = require('./webpack.core'); // Production-specific configuration // 生产具体配置 // ... module.exports = config.toConfig();
webpack-chain 中的核心API接口之一是 ChainedMap
. 一个 ChainedMap
的操做相似于JavaScript Map, 为链式和生成配置提供了一些便利。 若是一个属性被标记一个 ChainedMap
, 则它将具备以下的API和方法:
除非另有说明,不然这些方法将返回 ChainedMap
, 容许链式调用这些方法。
// 从 Map 移除全部 配置. clear()
// 经过键值从 Map 移除单个配置. // key: * delete(key)
// 获取 Map 中相应键的值 // key: * // returns: value get(key)
// 获取 Map 中相应键的值 // 若是键在Map中不存在,则ChainedMap中该键的值会被配置为fn的返回值. // key: * // fn: Function () -> value // returns: value getOrCompute(key, fn)
// 配置Map中 已存在的键的值 // key: * // value: * set(key, value)
// Map中是否存在一个配置值的特定键,返回 真或假 // key: * // returns: Boolean has(key)
// 返回 Map中已存储的全部值的数组 // returns: Array values()
// 返回Map中所有配置的一个对象, 其中 键是这个对象属性,值是相应键的值, // 若是Map是空,返回 `undefined` // 使用 `.before() 或 .after()` 的ChainedMap, 则将按照属性名进行排序。 // returns: Object, undefined if empty entries()
// 提供一个对象,这个对象的属性和值将 映射进 Map。 // 你也能够提供一个数组做为第二个参数以便忽略合并的属性名称。 // obj: Object // omit: Optional Array merge(obj, omit)
// 对当前配置上下文执行函数。 // handler: Function -> ChainedMap // 一个把ChainedMap实例做为单个参数的函数 batch(handler)
// 条件执行一个函数去继续配置 // condition: Boolean // whenTruthy: Function -> ChainedMap // 当条件为真,调用把ChainedMap实例做为单一参数传入的函数 // whenFalsy: Optional Function -> ChainedMap // 当条件为假,调用把ChainedMap实例做为单一参数传入的函数 when(condition, whenTruthy, whenFalsy)
webpack-chain 中的核心API接口另外一个是 ChainedSet
. 一个 ChainedSet
的操做相似于JavaScript Map, 为链式和生成配置提供了一些便利。 若是一个属性被标记一个 ChainedSet
, 则它将具备以下的API和方法:
除非另有说明,不然这些方法将返回 ChainedSet
, 容许链式调用这些方法。
// 添加/追加 给Set末尾位置一个值. // value: * add(value)
// 添加 给Set开始位置一个值. // value: * prepend(value)
// 移除Set中所有值. clear()
// 移除Set中一个指定的值. // value: * delete(value)
// 检测Set中是否存在一个值. // value: * // returns: Boolean has(value)
// 返回Set中值的数组. // returns: Array values()
// 链接给定的数组到 Set 尾部。 // arr: Array merge(arr)
// 对当前配置上下文执行函数。 // handler: Function -> ChainedSet // 一个把 ChainedSet 实例做为单个参数的函数 batch(handler)
// 条件执行一个函数去继续配置 // condition: Boolean // whenTruthy: Function -> ChainedSet // 当条件为真,调用把 ChainedSet 实例做为单一参数传入的函数 // whenFalsy: Optional Function -> ChainedSet // 当条件为假,调用把 ChainedSet 实例做为单一参数传入的函数 when(condition, whenTruthy, whenFalsy)
存在许多简写方法,用于 使用与简写方法名称相同的键在 ChainedMap 设置一个值
例如, devServer.hot
是一个速记方法, 所以它能够用做:
// 在 ChainedMap 上设置一个值的 速记方法 devServer.hot(true); // 上述方法等效于: devServer.set('hot', true);
一个速记方法是可链式的,所以调用它将返回 原实例,容许你继续链式使用
建立一个新的配置对象
const Config = require('webpack-chain'); const config = new Config();
移动到API的更深层将改变你正在修改的内容的上下文。 你能够经过 config
在此引用顶级配置或者经过调用 .end()
方法向上移动一级 使你移回更高的 上下文环境。
若是你熟悉jQuery, 这里与其 .end()
工做原理相似。除非另有说明,不然所有的API调用都将在当前上下文中返回API实例。 这样,你能够根据须要连续 链式API调用.
有关对全部速记和低级房费有效的特定值的详细信息,请参阅 webpack文档层次结构 中的相应名词。
Config : ChainedMap
config .amd(amd) .bail(bail) .cache(cache) .devtool(devtool) .context(context) .externals(externals) .loader(loader) .mode(mode) .parallelism(parallelism) .profile(profile) .recordsPath(recordsPath) .recordsInputPath(recordsInputPath) .recordsOutputPath(recordsOutputPath) .stats(stats) .target(target) .watch(watch) .watchOptions(watchOptions)
// 回到 config.entryPoints : ChainedMap config.entry(name) : ChainedSet config .entry(name) .add(value) .add(value) config .entry(name) .clear() // 用低级别 config.entryPoints: config.entryPoints .get(name) .add(value) .add(value) config.entryPoints .get(name) .clear()
config.output : ChainedMap config.output .auxiliaryComment(auxiliaryComment) .chunkFilename(chunkFilename) .chunkLoadTimeout(chunkLoadTimeout) .crossOriginLoading(crossOriginLoading) .devtoolFallbackModuleFilenameTemplate(devtoolFallbackModuleFilenameTemplate) .devtoolLineToLine(devtoolLineToLine) .devtoolModuleFilenameTemplate(devtoolModuleFilenameTemplate) .filename(filename) .hashFunction(hashFunction) .hashDigest(hashDigest) .hashDigestLength(hashDigestLength) .hashSalt(hashSalt) .hotUpdateChunkFilename(hotUpdateChunkFilename) .hotUpdateFunction(hotUpdateFunction) .hotUpdateMainFilename(hotUpdateMainFilename) .jsonpFunction(jsonpFunction) .library(library) .libraryExport(libraryExport) .libraryTarget(libraryTarget) .path(path) .pathinfo(pathinfo) .publicPath(publicPath) .sourceMapFilename(sourceMapFilename) .sourcePrefix(sourcePrefix) .strictModuleExceptionHandling(strictModuleExceptionHandling) .umdNamedDefine(umdNamedDefine)
config.resolve : ChainedMap config.resolve .cachePredicate(cachePredicate) .cacheWithContext(cacheWithContext) .enforceExtension(enforceExtension) .enforceModuleExtension(enforceModuleExtension) .unsafeCache(unsafeCache) .symlinks(symlinks)
config.resolve.alias : ChainedMap config.resolve.alias .set(key, value) .set(key, value) .delete(key) .clear()
config.resolve.modules : ChainedSet config.resolve.modules .add(value) .prepend(value) .clear()
config.resolve.aliasFields : ChainedSet config.resolve.aliasFields .add(value) .prepend(value) .clear()
config.resolve.descriptionFields : ChainedSet config.resolve.descriptionFields .add(value) .prepend(value) .clear()
config.resolve.extensions : ChainedSet config.resolve.extensions .add(value) .prepend(value) .clear()
config.resolve.mainFields : ChainedSet config.resolve.mainFields .add(value) .prepend(value) .clear()
config.resolve.mainFiles : ChainedSet config.resolve.mainFiles .add(value) .prepend(value) .clear()
当前API config.resolveLoader
相同于 配置 config.resolve
用下面的配置:
config.resolveLoader.moduleExtensions : ChainedSet config.resolveLoader.moduleExtensions .add(value) .prepend(value) .clear()
config.resolveLoader.packageMains : ChainedSet config.resolveLoader.packageMains .add(value) .prepend(value) .clear()
config.performance : ChainedMap config.performance .hints(hints) .maxEntrypointSize(maxEntrypointSize) .maxAssetSize(maxAssetSize) .assetFilter(assetFilter)
config.optimization : ChainedMap config.optimization .concatenateModules(concatenateModules) .flagIncludedChunks(flagIncludedChunks) .mergeDuplicateChunks(mergeDuplicateChunks) .minimize(minimize) .namedChunks(namedChunks) .namedModules(namedModules) .nodeEnv(nodeEnv) .noEmitOnErrors(noEmitOnErrors) .occurrenceOrder(occurrenceOrder) .portableRecords(portableRecords) .providedExports(providedExports) .removeAvailableModules(removeAvailableModules) .removeEmptyChunks(removeEmptyChunks) .runtimeChunk(runtimeChunk) .sideEffects(sideEffects) .splitChunks(splitChunks) .usedExports(usedExports)
// 回到 config.optimization.minimizers config.optimization .minimizer(name) : ChainedMap
注意: 不要用 new
去建立最小优化器插件,由于已经为你作好了。
config.optimization .minimizer(name) .use(WebpackPlugin, args) // 例如 config.optimization .minimizer('css') .use(OptimizeCSSAssetsPlugin, [{ cssProcessorOptions: { safe: true } }]) // Minimizer 插件也能够由它们的路径指定,从而容许在不使用插件或webpack配置的状况下跳过昂贵的 require s。 config.optimization .minimizer('css') .use(require.resolve('optimize-css-assets-webpack-plugin'), [{ cssProcessorOptions: { safe: true } }])
config.optimization .minimizer(name) .tap(args => newArgs) // 例如 config .minimizer('css') .tap(args => [...args, { cssProcessorOptions: { safe: false } }])
config.optimization .minimizer(name) .init((Plugin, args) => new Plugin(...args));
config.optimization.minimizers.delete(name)
// 回到 config.plugins config.plugin(name) : ChainedMap
注意: 不要用 new
去建立插件,由于已经为你作好了。
config .plugin(name) .use(WebpackPlugin, args) // 例如 config .plugin('hot') .use(webpack.HotModuleReplacementPlugin); // 插件也能够由它们的路径指定,从而容许在不使用插件或webpack配置的状况下跳过昂贵的 require s。 config .plugin('env') .use(require.resolve('webpack/lib/EnvironmentPlugin'), [{ 'VAR': false }]);
config .plugin(name) .tap(args => newArgs) // 例如 config .plugin('env') .tap(args => [...args, 'SECRET_KEY']);
config .plugin(name) .init((Plugin, args) => new Plugin(...args));
config.plugins.delete(name)
指定当前插件上下文应该在另外一个指定插件以前执行,你不能在同一个插件上同时使用 .before()
和 .after()
。
config .plugin(name) .before(otherName) // 例如 config .plugin('html-template') .use(HtmlWebpackTemplate) .end() .plugin('script-ext') .use(ScriptExtWebpackPlugin) .before('html-template');
指定当前插件上下文应该在另外一个指定插件以后执行,你不能在同一个插件上同时使用 .before()
和 .after()
。
config .plugin(name) .after(otherName) // 例如 config .plugin('html-template') .after('script-ext') .use(HtmlWebpackTemplate) .end() .plugin('script-ext') .use(ScriptExtWebpackPlugin);
// 回到 config.resolve.plugins config.resolve.plugin(name) : ChainedMap
注意: 不要用 new
去建立插件,由于已经为你作好了。
config.resolve .plugin(name) .use(WebpackPlugin, args)
config.resolve .plugin(name) .tap(args => newArgs)
config.resolve .plugin(name) .init((Plugin, args) => new Plugin(...args))
config.resolve.plugins.delete(name)
指定当前插件上下文应该在另外一个指定插件以前执行,你不能在同一个插件上同时使用 .before()
和 .after()
。
config.resolve .plugin(name) .before(otherName) // 例如 config.resolve .plugin('beta') .use(BetaWebpackPlugin) .end() .plugin('alpha') .use(AlphaWebpackPlugin) .before('beta');
指定当前插件上下文应该在另外一个指定插件以后执行,你不能在同一个插件上同时使用 .before()
和 .after()
。
config.resolve .plugin(name) .after(otherName) // 例如 config.resolve .plugin('beta') .after('alpha') .use(BetaWebpackTemplate) .end() .plugin('alpha') .use(AlphaWebpackPlugin);
config.node : ChainedMap config.node .set('__dirname', 'mock') .set('__filename', 'mock');
config.devServer : ChainedMap
config.devServer.allowedHosts : ChainedSet config.devServer.allowedHosts .add(value) .prepend(value) .clear()
config.devServer .bonjour(bonjour) .clientLogLevel(clientLogLevel) .color(color) .compress(compress) .contentBase(contentBase) .disableHostCheck(disableHostCheck) .filename(filename) .headers(headers) .historyApiFallback(historyApiFallback) .host(host) .hot(hot) .hotOnly(hotOnly) .https(https) .inline(inline) .info(info) .lazy(lazy) .noInfo(noInfo) .open(open) .openPage(openPage) .overlay(overlay) .pfx(pfx) .pfxPassphrase(pfxPassphrase) .port(port) .progress(progress) .proxy(proxy) .public(public) .publicPath(publicPath) .quiet(quiet) .setup(setup) .socket(socket) .staticOptions(staticOptions) .stats(stats) .stdin(stdin) .useLocalIp(useLocalIp) .watchContentBase(watchContentBase) .watchOptions(watchOptions)
config.module : ChainedMap
config.module : ChainedMap config.module .noParse(noParse)
config.module.rules : ChainedMap config.module .rule(name) .test(test) .pre() .post() .enforce(preOrPost)
config.module.rules{}.uses : ChainedMap config.module .rule(name) .use(name) .loader(loader) .options(options) // Example config.module .rule('compile') .use('babel') .loader('babel-loader') .options({ presets: ['@babel/preset-env'] });
config.module .rule(name) .use(name) .tap(options => newOptions) // 例如 config.module .rule('compile') .use('babel') .tap(options => merge(options, { plugins: ['@babel/plugin-proposal-class-properties'] }));
config.module.rules{}.oneOfs : ChainedMap<Rule> config.module .rule(name) .oneOf(name) // 例如 config.module .rule('css') .oneOf('inline') .resourceQuery(/inline/) .use('url') .loader('url-loader') .end() .end() .oneOf('external') .resourceQuery(/external/) .use('file') .loader('file-loader')
webpack-chain 支持将对象合并到配置实例,改实例相似于 webpack-chain 模式 布局的布局。 请注意,这不是 webpack 配置对象,但您能够再将webpack配置对象提供给webpack-chain 以匹配器布局以前对其进行转换。
config.merge({ devtool: 'source-map' }); config.get('devtool') // "source-map"
config.merge({ [key]: value, amd, bail, cache, context, devtool, externals, loader, mode, parallelism, profile, recordsPath, recordsInputPath, recordsOutputPath, stats, target, watch, watchOptions, entry: { [name]: [...values] }, plugin: { [name]: { plugin: WebpackPlugin, args: [...args], before, after } }, devServer: { [key]: value, clientLogLevel, compress, contentBase, filename, headers, historyApiFallback, host, hot, hotOnly, https, inline, lazy, noInfo, overlay, port, proxy, quiet, setup, stats, watchContentBase }, node: { [key]: value }, optimizations: { concatenateModules, flagIncludedChunks, mergeDuplicateChunks, minimize, minimizer, namedChunks, namedModules, nodeEnv, noEmitOnErrors, occurrenceOrder, portableRecords, providedExports, removeAvailableModules, removeEmptyChunks, runtimeChunk, sideEffects, splitChunks, usedExports, }, performance: { [key]: value, hints, maxEntrypointSize, maxAssetSize, assetFilter }, resolve: { [key]: value, alias: { [key]: value }, aliasFields: [...values], descriptionFields: [...values], extensions: [...values], mainFields: [...values], mainFiles: [...values], modules: [...values], plugin: { [name]: { plugin: WebpackPlugin, args: [...args], before, after } } }, resolveLoader: { [key]: value, alias: { [key]: value }, aliasFields: [...values], descriptionFields: [...values], extensions: [...values], mainFields: [...values], mainFiles: [...values], modules: [...values], moduleExtensions: [...values], packageMains: [...values], plugin: { [name]: { plugin: WebpackPlugin, args: [...args], before, after } } }, module: { [key]: value, rule: { [name]: { [key]: value, enforce, issuer, parser, resource, resourceQuery, test, include: [...paths], exclude: [...paths], oneOf: { [name]: Rule }, use: { [name]: { loader: LoaderString, options: LoaderOptions, before, after } } } } } })
当使用的状况下工做ChainedMap和ChainedSet,则可使用执行条件的配置when。您必须指定一个表达式 when(),以评估其真实性或虚假性。若是表达式是真实的,则将使用当前连接实例的实例调用第一个函数参数。您能够选择提供在条件为假时调用的第二个函数,该函数也是当前连接的实例。
// 示例:仅在生产期间添加minify插件 config .when(process.env.NODE_ENV === 'production', config => { config .plugin('minify') .use(BabiliWebpackPlugin); });
// 例:只有在生产过程当中添加缩小插件,不然设置devtool到源映射 config .when(process.env.NODE_ENV === 'production', config => config.plugin('minify').use(BabiliWebpackPlugin), config => config.devtool('source-map') );
您可使用检查生成的webpack配置config.toString()。这将生成配置的字符串化版本,其中包含命名规则,用法和插件的注释提示:
config .module .rule('compile') .test(/\.js$/) .use('babel') .loader('babel-loader'); config.toString(); { module: { rules: [ /* config.module.rule('compile') */ { test: /\.js$/, use: [ /* config.module.rule('compile').use('babel') */ { loader: 'babel-loader' } ] } ] } }
默认状况下,若是生成的字符串包含须要的函数和插件,则不能直接用做真正的webpack配置。为了生成可用的配置,您能够经过__expression在其上设置特殊属性来自定义函数和插件的字符串化方式:
class MyPlugin {} MyPlugin.__expression = `require('my-plugin')`; function myFunction () {} myFunction.__expression = `require('my-function')`; config .plugin('example') .use(MyPlugin, [{ fn: myFunction }]); config.toString(); /* { plugins: [ new (require('my-plugin'))({ fn: require('my-function') }) ] } */
经过其路径指定的插件将require()自动生成其语句:
config .plugin('env') .use(require.resolve('webpack/lib/ProvidePlugin'), [{ jQuery: 'jquery' }]) config.toString(); { plugins: [ new (require('/foo/bar/src/node_modules/webpack/lib/EnvironmentPlugin.js'))( { jQuery: 'jquery' } ) ] }
您还能够调用toString静态方法Config,以便在字符串化以前修改配置对象。
Config.toString({ ...config.toConfig(), module: { defaultRules: [ { use: [ { loader: 'banner-loader', options: { prefix: 'banner-prefix.txt' }, }, ], }, ], }, }) { plugins: [ /* config.plugin('foo') */ new TestPlugin() ], module: { defaultRules: [ { use: [ { loader: 'banner-loader', options: { prefix: 'banner-prefix.txt' } } ] } ] } }