在前面两篇博客中,主要讲了webpack的使用和webpack的核心概念,他们都是非常重要的,在这篇博客中,讲主要讨论webpack配置相关问题。
参考文章:https://webpack.js.org/configuration/
对于配置文件中不懂的地方都可以在上面的文章中找到最精确的文档来查看学习。
第一部分
可能你已经注意到了几乎没有两个configuration是相同的,这是因为webpack的配置文件就是一个导出一个对象的js文件。这个对象将会基于自身的属性被处理。
因为这是nodejs中的Commonjs标准,所以我们可以这样做:
- 通过require()引入其他的文件。
- 通过require()使用npm中的一些功能。
- 使用js的控制流表达式如 ?: 操作符。
- 对于经常使用的值使用常量或者变量来表示。
另外,下面的这些事不推荐的:
- 当使用webpack CLI时,使用CLI参数。
- 导出不确定的值。
- 写了很长的配置(而不是将配置文件分割为多份不同功能的配置文件)。 比如我们使用vue-cli时就会发现webpack的配置文件有4个之多,这就是为了防止一个配置文件过大而冗杂。
下面就是一个最简单的配置文件:
var path = require('path'); module.exports = { entry: './foo.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'foo.bundle.js' } };
下面也是一个配置文件,它是包含多对象的:
var path = require('path'); var webpack = require('webpack'); var webpackMerge = require('webpack-merge'); var baseConfig = { target: 'async-node', entry: { entry: './entry.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, plugins: [ new webpack.optimize.CommonsChunkPlugin({ name: 'inline', filename: 'inline.js', minChunks: Infinity }), new webpack.optimize.AggressiveSplittingPlugin({ minSize: 5000, maxSize: 10000 }), ] }; let targets = ['web', 'webworker', 'node', 'async-node', 'node-webkit', 'electron-main'].map((target) => { let base = webpackMerge(baseConfig, { target: target, output: { path: path.resolve(__dirname, 'dist/' + target), filename: '[name].' + target + '.js' } }); return base; }); module.exports = targets;
对于这个配置文件,有下面几点需要说明:
- 对于配置文件,我们有很多种组织方式来配置, 最重要的是坚持一致性并且保证你的团队都可以理解并维护他们。
- 对于多个对象,我们常用的就是webpackMerge函数,这个函数可以将多个对象merge为一个对象。
- 这里最后导出的是一个targets数组,其中的每一个元素都是合并之后的对象。
第二部分
不难发现,之前我们在写配置文件时往往会用到node的内置模块---path模块。通过传入全局变量__dirname来使用。 这样可以防止出现文件的路径错误问题,因为不同的操作系统和对于相对路径/绝对路径的支持问题。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
const path = require('path'); module.exports = { // click on the name of the option to get to the detailed documentation // click on the items with arrows to show more examples / advanced options entry: "./app/entry", // string | object | array // Here the application starts executing // and webpack starts bundling output: { // options related to how webpack emits results path: path.resolve(__dirname, "dist"), // string // the target directory for all output files // must be an absolute path (use the Node.js path module) filename: "bundle.js", // string // the filename template for entry chunks publicPath: "/assets/", // string // the url to the output directory resolved relative to the HTML page library: "MyLibrary", // string, // the name of the exported library libraryTarget: "umd", // universal module definition // the type of the exported library /* Advanced output configuration (click to show) */ }, module: { // configuration regarding modules rules: [ // rules for modules (configure loaders, parser options, etc.) { test: /\.jsx?$/, include: [ path.resolve(__dirname, "app") ], exclude: [ path.resolve(__dirname, "app/demo-files") ], // these are matching conditions, each accepting a regular expression or string // test and include have the same behavior, both must be matched // exclude must not be matched (takes preferrence over test and include) // Best practices: // - Use RegExp only in test and for filename matching // - Use arrays of absolute paths in include and exclude // - Try to avoid exclude and prefer include issuer: { test, include, exclude }, // conditions for the issuer (the origin of the import) enforce: "pre", enforce: "post", // flags to apply these rules, even if they are overridden (advanced option) loader: "babel-loader", // the loader which should be applied, it'll be resolved relative to the context // -loader suffix is no longer optional in webpack2 for clarity reasons // see webpack 1 upgrade guide options: { presets: ["es2015"] }, // options for the loader }, { test: "\.html$", use: [ // apply multiple loaders and options "htmllint-loader", { loader: "html-loader", options: { /* ... */ } } ] }, { oneOf: [ /* rules */ ] }, // only use one of these nested rules { rules: [ /* rules */ ] }, // use all of these nested rules (combine with conditions to be useful) { resource: { and: [ /* conditions */ ] } }, // matches only if all conditions are matched { resource: { or: [ /* conditions */ ] } }, { resource: [ /* conditions */ ] }, // matches if any condition is matched (default for arrays) { resource: { not: /* condition */ } } // matches if the condition is not matched ], /* Advanced module configuration (click to show) */ }, resolve: { // options for resolving module requests // (does not apply to resolving to loaders) modules: [ "node_modules", path.resolve(__dirname, "app") ], // directories where to look for modules extensions: [".js", ".json", ".jsx", ".css"], // extensions that are used alias: { // a list of module name aliases "module": "new-module", // alias "module" -> "new-module" and "module/path/file" -> "new-module/path/file" "only-module$": "new-module", // alias "only-module" -> "new-module", but not "module/path/file" -> "new-module/path/file" "module": path.resolve(__dirname, "app/third/module.js"), // alias "module" -> "./app/third/module.js" and "module/file" results in error // modules aliases are imported relative to the current context }, /* alternative alias syntax (click to show) */ /* Advanced resolve configuration (click to show) */ }, performance: { hints: "warning", // enum maxAssetSize: 200000, // int (in bytes), maxEntrypointSize: 400000, // int (in bytes) assetFilter: function(assetFilename) { // Function predicate that provides asset filenames return assetFilename.endsWith('.css') || assetFilename.endsWith('.js'); } }, devtool: "source-map", // enum // enhance debugging by adding meta info for the browser devtools // source-map most detailed at the expense of build speed. context: __dirname, // string (absolute path!) // the home directory for webpack // the entry and module.rules.loader option // is resolved relative to this directory target: "web", // enum // the environment in which the bundle should run // changes chunk loading behavior and available modules externals: ["react", /^@angular\//], // Don't follow/bundle these modules, but request them at runtime from the environment stats: "errors-only", // lets you precisely control what bundle information gets displayed 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: [ // ... ], // list of additional plugins /* Advanced configuration (click to show) */ }
在上面的配置文件中我们应该可以找到大部分遇到的问题了。
配置语言
webpack允许你使用任何语言来写配置文件。配置文件支持的语言我们可以在node-interpret中看到。也就是说,webpack在node-interpret的支持下将会根据你的选择语言来运行你的配置文件。
比如,你可以使用coffeescript,如下所示:
HtmlWebpackPlugin = require('html-webpack-plugin') webpack = require('webpack') path = require('path') config = entry: './path/to/my/entry/file.js' output: path: path.resolve(__dirname, 'dist') filename: 'my-first-webpack.bundle.js' module: rules: [ { test: /\.(js|jsx)$/ use: 'babel-loader' } ] plugins: [ new (webpack.optimize.UglifyJsPlugin) new HtmlWebpackPlugin(template: './src/index.html') ] module.exports = config
这样的配置文件也是可以正常执行的。
配置类型
对于一个配置文件,我们除了可以导出一个配置对象,还有很多其他的方式来满足满足我们的需求。
为使用 --env 导出一个函数
最终你将会发现这样一个需求:在你的webpack.config.js中消除development和production builds之间的歧义。你至少有两个选择:
不是导出一个配置对象,而是导出一个返回的函数,这个函数可以接受环境参数作为变量。当你运行webpack时,你可以指定构建环境的关键词通过 --env, 比如使用 --env.production 或者是 --env.platform=web。 如下所示;
-module.exports = { +module.exports = function(env) { + return { plugins: [ new webpack.optimize.UglifyJsPlugin({ + compress: env.production // compress only in production build }) ] + }; };
其中的+标识新添加的代码,其中的-标识不需要、删除的代码。
导出一个promise
webpack将会运行通过配置文件导出的函数并且等待一个promise被返回,当你需要异步的加载配置变量时这是非常方便的。
module.exports = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ entry: './app.js', /* ... */ }) }, 5000) }) }
导出多个配置文件
不是导出一个配置对象或函数,你可以导出多个配置文件。 当运行webpack时,所有的配置文件都会被建立, 比如,在创建一个库时这是非常有用的
module.exports = [{ output: { filename: './dist-amd.js', libraryTarget: 'amd' }, entry: './app.js', }, { output: { filename: './dist-commonjs.js', libraryTarget: 'commonjs' }, entry: './app.js', }]
//webpack.js.org/guides/production-build/