Open FrankKai opened 4 years ago
通过阅读这篇文章,可以学习到如何使用webpack-chain插件使得前端项目更加工程化。vue-cli3.0的vue.config.js采用webpack-chain插件的方式进行链式配置,所以这个插件非常值得一学。
使用前的声明式编程:
const path = require('path'); const config = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' }, module: { rules: [ { test: /\.(js)$/, use: 'babel-loader' } ] }, }; module.exports = config;
使用后的声明式编程(函数式编程):
const Config = require('webpack-chain'); const config = new Config(); config. .entry('index') .add('src/index.js') .end() .output .path('dist') filename('my-first-webpack.bundle.js'); config.module .rule('compile') .test(/\.js$/) .use('babel') .loader('babel-loader') module.exports = config;
通过对比我们发现webpack-chain有以下优点:
vue-cli3.0和webpack4.0时代,webpack-chain插件是必会插件,因为在vue.config.js中,configureWebpack和chainWebpack中的cb中注入的config对象,其实都是一个webpack-chain实例,如果想对webpack的一些插件的配置做修改,那么就必须先理解webpack-chain。
webpack.core.js
const Config = require('webpack-chain'); const config = new Config(); // 入口出口文件配置 config. .entry('index') .add('src/index.js') .end() .output .path('dist') filename('[name].bundle.js'); // 创建之后可以修改的命名规则 config.module .rule('lint') .test(/\.js$/) .pre() .include .add('src') end() .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' }]); module.exports = config;
webpack.dev.js
const config = require('./webpack.core'); // ... module.exports = config.toConfig();
webpack.prod.js
ChainedMap的有些key,可以直接作为方法调用,这些缩写方法也同样会返回原始实例,方便后续的链式调用。
devServer.hot(true); devServer.set('hot', true);
.end()
config
.entry()
config.entry(name).add(value)
.entry()缩写方法配置
config.entryPoints.get(name).add(value)
.add()
output
module
plugin
config .plugin(name) .use(WebpackPlugin, args) // 直接引入 config .plugin('hot') .use(webpack.HotModuleReplacementPlugin); // 可以通过requrire('')的方式引入插件。 config .plugin('env') .use(require.resolve('webpack/lib/EnvironmentPlugin'), [{ 'VAR': false }]);
config .plugin(name) .tap(args => newArgs) // 为arguments新增一个'SECRET_KEY' 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')
config .plugin(name) .after(otherName) // 例子 config .plugin('html-template') .use(HtmlWebpackTemplate) .after('script-ext') .end() .plugin('script-ext') .use(ScriptExtWebpackPlugin)
vue.config.js
const apiConfig = require('./config/api'); module.exports = { chainWebpack: config => { config .plugin('define') .tap(args => { args[0].API_CONFIG = JSON.stringify(apiConfig) return args }) } }
需要注意的是,在vue-cli3.0中,我们不能直接SET NODE_ENV=production或者EXPORT NODE_ENV=production。 因为vue-cli-servive有3种模式,serve默认为development,build为production,若想修改vue-cli-service包中的NODE_ENV,需要通过vue-cli-service serve --mode production进行切换。 就像下面这样:
{ "scripts": { "dev": "vue-cli-service serve", // mode默认为development "production": "vue-cli-service serve --mode production", }, }
注意:我们只能在development, production或者test 3个模式下进行切换,不能引入类似preproduction之类的自定义node环境,但是实际上这3个环境已经足以满足大多数的开发情况。
在源码文件base.js中,有下面的代码:
webpackConfig .plugin('define') .use(require('webpack/lib/DefinePlugin'), [ resolveClientEnv(options) ])
这一点很关键!我们在vue.config.js中拿到的config.plugin('define'),实际上时vue-service内部创建的webpack.DefinePlugin实例的引用 ! 明确了这一点,我们在以后增强webpack默认插件配置时,需要先到vue-service的源码中寻找一番,看看有没有对应plugin的引用,若有,必须根据vue-service定义的名字直接引用,否则会修改失败。
通过阅读这篇文章,可以学习到如何使用webpack-chain插件使得前端项目更加工程化。vue-cli3.0的vue.config.js采用webpack-chain插件的方式进行链式配置,所以这个插件非常值得一学。
命令式编程->声明式编程(函数式编程)
使用前的声明式编程:
使用后的声明式编程(函数式编程):
通过对比我们发现webpack-chain有以下优点:
vue-cli3.0和webpack4.0时代,webpack-chain插件是必会插件,因为在vue.config.js中,configureWebpack和chainWebpack中的cb中注入的config对象,其实都是一个webpack-chain实例,如果想对webpack的一些插件的配置做修改,那么就必须先理解webpack-chain。
为什么要使用webpack-chain插件?
webpack-plugin使用示例
webpack.core.js
webpack.dev.js
webpack.prod.js
一些常用的webpack-chain 缩写方法
ChainedMap的有些key,可以直接作为方法调用,这些缩写方法也同样会返回原始实例,方便后续的链式调用。
.end()
通过.end()
可以返回到更高层级的上下文,但是仅向上一个层级,并且返回一个mutate后的实例。或者是直接通过config
是获取的顶级上下文。.entry()
config.entryPoints.get()的缩写 。可以通过config.entry(name).add(value)
的.entry()缩写方法配置
,也可以通过config.entryPoints.get(name).add(value)
配置。.add()
这是一个ChainedSet方法,它可以将值添加在Set的尾部。output
这是一个ChainedMap对象,有很多方法,例如path(),filename(),publicPath()等常用的方法。module
也是一个ChinedMap,主要方法为rules(),配置loader的规则,config.module.rule(name).use(name).loader(loader).options(options),或者config.module.rule(name).use(name).tap(options => newOptions)plugin
也是ChinedMap,主要是对plugin配置,config.plugin(name).use(WebpackPlugin, args)。重点对plugin做深入学习。引入webpack-chain后如何配置plugin?
新增插件
修改参数
修改实例
删除插件
某个插件前调用插件odering before
不能在同一个插件上既使用before又使用after。
某个插件后调用插件ordering after
不能在同一个插件上既使用before又使用after。
vue-cli3.0引入webpack-chain后,如何配置最常用的DefinePlugin呢?
vue.config.js
需要注意的是,在vue-cli3.0中,我们不能直接SET NODE_ENV=production或者EXPORT NODE_ENV=production。 因为vue-cli-servive有3种模式,serve默认为development,build为production,若想修改vue-cli-service包中的NODE_ENV,需要通过vue-cli-service serve --mode production进行切换。 就像下面这样:
注意:我们只能在development, production或者test 3个模式下进行切换,不能引入类似preproduction之类的自定义node环境,但是实际上这3个环境已经足以满足大多数的开发情况。
为什么vue-cli 3.0中的DefinePlugin可以用config.plugin('define')修改入参?
在源码文件base.js中,有下面的代码:
这一点很关键!我们在vue.config.js中拿到的config.plugin('define'),实际上时vue-service内部创建的webpack.DefinePlugin实例的引用 ! 明确了这一点,我们在以后增强webpack默认插件配置时,需要先到vue-service的源码中寻找一番,看看有没有对应plugin的引用,若有,必须根据vue-service定义的名字直接引用,否则会修改失败。