webpack源码解析六(webpack-chain)
前言
前面我们写了几篇文章用来介绍webpack源码,跟着官网结合demo把整个webpack配置撸了一遍:
- webpack源码解析一
- webpack源码解析二(html-webpack-plugin插件)
- webpack源码解析三
- webpack源码解析四
- webpack源码解析五
今天我们说一下一个关于webpack配置的第三方库webpack-chain,为什么要讲它呢? 可以借助IDE可以做到智能提示,让我们配置起来不容易出错,而且完全链式语法,用起来比较爽!
开始
我们还是接着我们之前章节的webpack-demo继续,webpack-chain的api用法大家可以简单的先过一下官网,然后看一下我们之前webpack-demo的配置文件,
webpack.config.js:
const path = require("path");
module.exports = {mode: "development",context: path.resolve(__dirname, "./src"),// entry: ["babel-polyfill","./index.js"],entry: {app: ["./index.js"]},output: {path: path.join(process.cwd(), "lib"), //默认为path.join(process.cwd(), "dist")pathinfo: true,filename: "[name].[contenthash:16].[fullhash:16].[id].js",chunkFilename: "[id].js",// library: "demoSay",// libraryExport: "default",// libraryTarget: "jsonp",},experiments: {// outputModule: true},module: {noParse: /babel-polyfill/,rules: [{test: /.vue$/,use: 'vue-loader',},{test: /\.(sass|scss)$/,use: ["style-loader","css-loader",{loader: "postcss-loader",options: {config: {path: path.resolve(__dirname, "./postcss.config.js")}}},"sass-loader"],},{test: /\.png$/,oneOf: [{resourceQuery: /inline/,loader: "url-loader",options: {limit: 1024 * 1024 * 10}},{resourceQuery: /external/,loader: "file-loader",}]}]},resolve: {alias: {DemoVue: path.resolve(__dirname, "./src/demo-vue.vue")},extensions: ['.wasm', '.mjs', '.js', '.json', '.vue'],modules: [path.resolve(__dirname, "src"), "node_modules"],unsafeCache: /demo-publicpath/,},plugins: [new (require("vue-loader-plugin"))(),// new (require("uglifyjs-webpack-plugin"))({// test: /\.js($|\?)/i// })],devServer: {before(app, server, compiler) {app.get("/login",(req,res,next)=>{req.query.name="hello "+req.query.name;next();});},after(app, server, compiler) {app.get("/login",(req,res,next)=>{res.json({msg: req.query.name});});},clientLogLevel: "info",allowedHosts: ["localhost"],contentBase: path.join(process.cwd(), "lib"),// contentBasePublicPath: "/assets",filename: /app\.js/,headers: {'X-Custom-Foo': 'bar'},historyApiFallback: true,host: "0.0.0.0",port: "8090",hot: true,liveReload: true,open: true,useLocalIp: true,overlay: true,},// performance: {// hints: "error",// maxEntrypointSize: 1024// }
};
👌,这个配置文件我就不分析太多了,因为我是直接用的之前demo的配置文件,不懂的小伙伴可以看看之前的章节。
安装
首先在demo根目录安装webpack-chain:
npm install -D webpack-chain || yarn add -D webpack-chain
配置
我们在demo根目录创建一个webpack-chain.js的文件,先导出一个config对象:
webpack-chain.js
const Config = require('webpack-chain');
const config = new Config();module.exports = config.toConfig();
我们new了一个webpack-chain的config对象,然后利用toConfig方法是把webpack-chain的config对象转换成webpack的配置对象的方法。
mode

可以看到,当我们用mode方法的时候,IDE直接提示我们value的值有哪些了,是不是很方便呢?
ok! 我们这里用的是“development”,所以我们直接设置mode的值为“development”(小伙伴可以像我一样,用ide分两屏一左一右对照的来写一遍)。
const Config = require('webpack-chain');
const config = new Config();config.mode("development")module.exports = config.toConfig();
context

可以看到,ide提示我们输出一个string字符串:
const Config = require('webpack-chain');
const config = new Config();
const path = require("path");config.mode("development").context(path.resolve(__dirname, "./src"))module.exports = config.toConfig();
Ok! 后面的内容我就不截图了,直接按照提示配置就ok,我就加快节奏了哈~
entry
const Config = require('webpack-chain');
const config = new Config();
const path = require("path");config.mode("development").context(path.resolve(__dirname, "./src")).entry("app").add("./index.js").end().
module.exports = config.toConfig();
ok,可以看到entry方法返回了一个EntryPoint对象,EntryPoint对象是一个TypedChainedSet集合,所以我们往集合中添加一个字符串“./index.js”,因为TypedChainedSet集合的parent是config对象,所以我们调用end方法退回到config对象。
output
const Config = require('webpack-chain');
const config = new Config();
const path = require("path");config.mode("development").context(path.resolve(__dirname, "./src")).entry("app").add("./index.js").end().output.path(path.join(process.cwd(), "lib")).pathinfo(true).filename("[name].[contenthash:16].[fullhash:16].[id].js").chunkFilename("[id].js").end()module.exports = config.toConfig();
同样,output配置完毕后直接调用end退出output回到config。
experiments
这个配置是webpack5才有的,但是我们这里用的webpack-chain还是针对webpack5.0以下版本,那我们怎么办呢?先看一下源文件,
webpack.config.js:
...
experiments: {// outputModule: true},
...
我们在demo中是注释掉了,因为当初是为了结合output.libraryTarget="module"用的,虽然是注释掉了,我们大不了不传值,所以我们可以这样写:
webpack-chain.js
const Config = require('webpack-chain');
const config = new Config();
const path = require("path");config.mode("development").context(path.resolve(__dirname, "./src")).entry("app").add("./index.js").end().output.path(path.join(process.cwd(), "lib")).pathinfo(true).filename("[name].[contenthash:16].[fullhash:16].[id].js").chunkFilename("[id].js").end().set("experiments",{})module.exports = config.toConfig();
我们可以直接调用set方法给config设置一个“experiments”属性。
module
const Config = require('webpack-chain');
const config = new Config();
const path = require("path");config.mode("development").context(path.resolve(__dirname, "./src")).entry("app").add("./index.js").end().output.path(path.join(process.cwd(), "lib")).pathinfo(true).filename("[name].[contenthash:16].[fullhash:16].[id].js").chunkFilename("[id].js").end().set("experiments",{}).module.noParse(/babel-polyfill/).rule("vue").test(/\.vue$/).use("vue-loader").loader("vue-loader").end().end().rule("sass").test( /\.(sass|scss)$/).use("style-loader").loader("style-loader").end().use("css-loader").loader("css-loader").end().use("postcss-loader").loader("postcss-loader").options( {config: {path: path.resolve(__dirname, "./postcss.config.js")}}).end().use("sass-loader").loader("sass-loader").end().end().rule("png").test(/\.png$/).oneOf("png-loader").rule("url-loader").resourceQuery(/inline/).use("url-loader").loader("url-loader").options({limit: 1024 * 1024 * 10}).end().end().rule("file-loader").resourceQuery(/external/).use("file-loader").loader("file-loader").end().end().end().end().end()
module.exports = config.toConfig();
ok,这里有点复杂,因为rule是可以嵌套的,不过通过之前文章我们已经充分了解每个配置的用法了,所以还行!!
rules是一个Array数组,每一个item是一个Rule,每一个Rule都有test、use等属性,照着ide提示来没啥问题,比如代码最后用了5个end方法去退出到config对象:
- 第一个end(当前this是Use,退出到Rule)
- 第二个end(当前this是Rule,退出到oneOf)
- 第三个end(当前this是oneOf, 退出到Rule)
- 第四个end (当前this是Rule,退出到Module)
- 第五个end (当前this是Module,退出到Config)
哈哈~ 我这里是为了一个链式到底,所以才这么干的,小伙伴可以直接结束当前语句,然后利用config去配置接下来的项目。
resolve
alias是一个对象,所以webpack-chain用的是map集合,extensions是一个数组,所以webpack-chain用的是一个set集合,
const Config = require('webpack-chain');
const config = new Config();
const path = require("path");config.mode("development").context(path.resolve(__dirname, "./src")).entry("app").add("./index.js").end().output.path(path.join(process.cwd(), "lib")).pathinfo(true).filename("[name].[contenthash:16].[fullhash:16].[id].js").chunkFilename("[id].js").end().set("experiments",{}).module.noParse(/babel-polyfill/).rule("vue").test(/\.vue$/).use("vue-loader").loader("vue-loader").end().end().rule("sass").test( /\.(sass|scss)$/).use("style-loader").loader("style-loader").end().use("css-loader").loader("css-loader").end().use("postcss-loader").loader("postcss-loader").options( {config: {path: path.resolve(__dirname, "./postcss.config.js")}}).end().use("sass-loader").loader("sass-loader").end().end().rule("png").test(/\.png$/).oneOf("png-loader").rule("url-loader").resourceQuery(/inline/).use("url-loader").loader("url-loader").options({limit: 1024 * 1024 * 10}).end().end().rule("file-loader").resourceQuery(/external/).use("file-loader").loader("file-loader").end().end().end().end().end().resolve.alias.set("DemoVue", path.resolve(__dirname, "./src/demo-vue.vue")).end().extensions.add(".wasm").add(".mjs").add(".js").add(".json").add(".vue").end().modules.add(path.resolve(__dirname, "src")).add("node_modules").end().unsafeCache(/demo-publicpath/).end()
module.exports = config.toConfig();
plugin
config提供了plugin方法,传入标识符会返回一个Plugin对象,里面有use方法,use方法的第一个参数是类,第二个参数是构造函数参数。
const Config = require('webpack-chain');
const config = new Config();
const path = require("path");config.mode("development").context(path.resolve(__dirname, "./src")).entry("app").add("./index.js").end().output.path(path.join(process.cwd(), "lib")).pathinfo(true).filename("[name].[contenthash:16].[fullhash:16].[id].js").chunkFilename("[id].js").end().set("experiments",{}).module.noParse(/babel-polyfill/).rule("vue").test(/\.vue$/).use("vue-loader").loader("vue-loader").end().end().rule("sass").test( /\.(sass|scss)$/).use("style-loader").loader("style-loader").end().use("css-loader").loader("css-loader").end().use("postcss-loader").loader("postcss-loader").options( {config: {path: path.resolve(__dirname, "./postcss.config.js")}}).end().use("sass-loader").loader("sass-loader").end().end().rule("png").test(/\.png$/).oneOf("png-loader").rule("url-loader").resourceQuery(/inline/).use("url-loader").loader("url-loader").options({limit: 1024 * 1024 * 10}).end().end().rule("file-loader").resourceQuery(/external/).use("file-loader").loader("file-loader").end().end().end().end().end().resolve.alias.set("DemoVue", path.resolve(__dirname, "./src/demo-vue.vue")).end().extensions.add(".wasm").add(".mjs").add(".js").add(".json").add(".vue").end().modules.add(path.resolve(__dirname, "src")).add("node_modules").end().unsafeCache(/demo-publicpath/).end().plugin("vue-loader-plugin").use(require("vue-loader-plugin"),[]).end()
module.exports = config.toConfig();
devServer && performance
const Config = require('webpack-chain');
const config = new Config();
const path = require("path");config.mode("development").context(path.resolve(__dirname, "./src")).entry("app").add("./index.js").end().output.path(path.join(process.cwd(), "lib")).pathinfo(true).filename("[name].[contenthash:16].[fullhash:16].[id].js").chunkFilename("[id].js").end().set("experiments",{}).module.noParse(/babel-polyfill/).rule("vue").test(/\.vue$/).use("vue-loader").loader("vue-loader").end().end().rule("sass").test( /\.(sass|scss)$/).use("style-loader").loader("style-loader").end().use("css-loader").loader("css-loader").end().use("postcss-loader").loader("postcss-loader").options( {config: {path: path.resolve(__dirname, "./postcss.config.js")}}).end().use("sass-loader").loader("sass-loader").end().end().rule("png").test(/\.png$/).oneOf("png-loader").rule("url-loader").resourceQuery(/inline/).use("url-loader").loader("url-loader").options({limit: 1024 * 1024 * 10}).end().end().rule("file-loader").resourceQuery(/external/).use("file-loader").loader("file-loader").end().end().end().end().end().resolve.alias.set("DemoVue", path.resolve(__dirname, "./src/demo-vue.vue")).end().extensions.add(".wasm").add(".mjs").add(".js").add(".json").add(".vue").end().modules.add(path.resolve(__dirname, "src")).add("node_modules").end().unsafeCache(/demo-publicpath/).end().plugin("vue-loader-plugin").use(require("vue-loader-plugin"),[]).end().devServer.before((app, server, compiler)=>{app.get("/login",(req,res,next)=>{req.query.name="hello "+req.query.name;next();});}).after((app, server, compiler)=>{app.get("/login",(req,res,next)=>{res.json({msg: req.query.name});});}).clientLogLevel("info").allowedHosts.add("localhost").end().contentBase(path.join(process.cwd(), "lib")).filename(/app\.js/).headers({'X-Custom-Foo': 'bar'}).historyApiFallback(true).host("0.0.0.0").port("8090").hot(true).set("liveReload", true).open(true).useLocalIp(true).overlay(true).end().performance.hints("warning").end();
module.exports = config.toConfig();
Ok! 全部配置完毕了,我们测试一下,我们修改webpack的配置文件:
webpack.config.js
module.exports=require("./webpack-chain");
直接导出一个webpack-chain内容。
运行webpack:
webpack-demo git:(master) ✗ node ./node_modules/webpack/node_modules/.bin/webpack-dev-server
ℹ 「wds」: Project is running at http://10.22.1.32:8090/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from xxx/webpack-demo/lib
ℹ 「wds」: 404s will fallback to /index.html
ℹ 「wdm」: wait until bundle finished: /
⚠ 「wdm」: Hash: 4bf061b1a05b1d0c9db1
Version: webpack 5.0.0-beta.7
Time: 2847ms
Built at: 2020-07-13 16:23:36Asset Size63fe41824cb8236c0896a71b7df7f461.png 59.3 KiB [emitted] [name: (app)]
app.a4b9346c31248d4b.4bf061b1a05b1d0c.app.js 675 KiB [emitted] [immutable] [big] [name: app]
Entrypoint app [big] = app.a4b9346c31248d4b.4bf061b1a05b1d0c.app.js (63fe41824cb8236c0896a71b7df7f461.png)
./index.js 271 bytes [built]
(webpack)/node_modules/webpack-dev-server/client?http://10.22.1.32:8090 4.29 KiB [built]
(webpack)/hot/dev-server.js 1.59 KiB [built]
./demo-vue.vue 1.19 KiB [built]
../node_modules/vue/dist/vue.runtime.esm.js 222 KiB [built]
./demo-publicpath.js 95 bytes [built]
(webpack)/node_modules/webpack-dev-server/node_modules/strip-ansi/index.js 161 bytes [built]
(webpack)/node_modules/webpack-dev-server/client/socket.js 1.53 KiB [built]
(webpack)/node_modules/webpack-dev-server/client/overlay.js 3.51 KiB [built]
(webpack)/node_modules/webpack-dev-server/client/utils/log.js 964 bytes [built]
(webpack)/node_modules/webpack-dev-server/client/utils/sendMessage.js 402 bytes [built]
(webpack)/node_modules/webpack-dev-server/client/utils/reloadApp.js 1.59 KiB [built]
(webpack)/hot/emitter.js 75 bytes [built]
(webpack)/node_modules/webpack-dev-server/client/utils/createSocketUrl.js 2.91 KiB [built]
(webpack)/node_modules/webpack/hot sync nonrecursive ^\.\/log$ 170 bytes [built]+ 43 hidden modulesWARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: app.a4b9346c31248d4b.4bf061b1a05b1d0c.app.js (675 KiB)WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:app (675 KiB)app.a4b9346c31248d4b.4bf061b1a05b1d0c.app.jsWARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/ℹ 「wdm」: Compiled with warnings.
👌~ webpack-chain结合demo我们算是讲完了,我们demo应该算是把webpack-chain内容都覆盖了,说到底关键还是得弄懂webpack的所有配置项目,这样才能把webpack-chain运用自如,我不夸张的说我只看了一遍webpack-chain的api,然后通过ide智能提示完成了整个配置,所以强烈推荐小伙伴看一下之前webpack的文章。
未完待续,敬请期待!!
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
