What is a webpack – chain?
Webpack-chain attempts to create and modify webPack configurations by providing chained or downstream apis. The Key part of the API can be referenced by a user-specified name, which helps standardize how configurations are modified across projects. Apply a chained API to generate and simplify configuration changes to WebPack versions 2-4.
Why webpack-chain?
The core configuration of WebPack is created and modified based on a potentially difficult JavaScript object. While this is fine for configuring a single project, it can get messy when you try to share these objects across projects and make subsequent changes.
What can we do with Webpack-chain?
Build pluggable development environment and production environment, flexibly configure projects, perfect customized solutions.
Webpack – chain started
Once you have webpack-chain installed, you can start creating a webPack configuration. The following code sample is from the official example. Basic configuration webpack.config.js
// Imports the Webpack-chain module, which exports a single constructor for creating a WebPack configuration API. const Config = require('webpack-chain'); // Create a new configuration instance of the single constructor const config = new config (); // Each call to the API tracks changes to the storage configuration. Config // Modify entry config.entry ('index')
.add('src/index.js'.output. Path ().output.'dist')
.filename('[name].bundle.js'); // Create a named rule that can be used later to modify the rule config.module. rule('lint')
.test(/\.js$/)
.pre()
.include
.add('src').end() // You can also create named 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}}]]); // You can also create a named plug-in! config .plugin('clean')
.use(CleanPlugin, [['dist'], { root: '/dir'}]); Module.exports = config.toconfig (); // Export the configuration object module.exports = config.toconfig () to be used by Webpack;Copy the code
The shared configuration is also simple. Just export the configuration and call the.toconfig () method to export the configuration to WebPack for use before passing it to webpack.
If you’ve learned how to use jquery, you’ll be able to get used to the above chain calls, but we need to take a look at the core API in Webpack-Chain.
- ChainedMap
- ChainedSet
** ChainedMap and ChainedSet operate like JavaScript Maps, providing some convenience for chained and generated configurations **
For a detailed example, please check out the official document, Webpack-chain. Next, let’s see how to set up our production environment configuration step by step.
directory
│─ build │ ├ ─ config.js// Public configuration│ │ ─ ─ build. Js │ └ ─ ─ dev. Js │ ─ ─ the config │ │ ─ ─ base. Js// Basic configuration│ │ ─ ─ CSS, js/ / CSS configuration│ │ ─ ─ HtmlWebpackPlugin. Js/ / HTML configuration│ └ ─ ─ MiniCssExtractPlugin. Js// Extract CSS styles│ ─ ─ the public// Public resources│ └ ─ ─ index. HTML/ / HTML template└ ─ ─ the SRC// Develop the directory│ │ ─ ─ index. The CSS// Test the style└ ─ ─ the main js/ / the main entry
Copy the code
build/base.js
const { findSync } = require('.. /lib');
const Config = require('webpack-chain');
const config = new Config();
const files = findSync('config');
const path = require('path');
const resolve = p= > {
return path.join(process.cwd(), p);
};
module.exports = (a)= > {
const map = new Map(a); files.map(_= > {
const name = _.split('/')
.pop()
.replace('.js'.' ');
return map.set(name, require(_)(config, resolve));
});
map.forEach(v= > v());
return config;
};
Copy the code
Build the production environment
build/build.js
const rimraf = require('rimraf');
const ora = require('ora');
const chalk = require('chalk');
const path = require('path');
rimraf.sync(path.join(process.cwd(), 'dist'));// Delete the dist directory
const config = require('./config') ();const webpack = require('webpack');
const spinner = ora('Start building the project... ');
spinner.start();
webpack(config.toConfig(), function(err, stats) {
spinner.stop();
if (err) throw err;
process.stdout.write(
stats.toString({
colors: true.modules: false.children: false.chunks: false.chunkModules: false
}) + '\n\n'
);
if (stats.hasErrors()) {
console.log(chalk.red('Build failed \n'));
process.exit(1);
}
console.log(chalk.cyan('build complete \ n'));
});
Copy the code
Building a development environment
build/dev.js
const config = require('./config') ();const webpack = require('webpack');
const chalk = require('chalk');
const WebpackDevServer = require('webpack-dev-server');
const port = 8080;
const publicPath = '/common/';
config.devServer
.quiet(true)
.hot(true)
.https(false)
.disableHostCheck(true)
.publicPath(publicPath)
.clientLogLevel('none');
const compiler = webpack(config.toConfig());
const chainDevServer = compiler.options.devServer;
const server = new WebpackDevServer(
compiler,
Object.assign(chainDevServer, {})
);
['SIGINT'.'SIGTERM'].forEach(signal= > {
process.on(signal, () => {
server.close((a)= > {
process.exit(0);
});
});
});
server.listen(port);// Listen on the port
new Promise((a)= > {
compiler.hooks.done.tap('dev', stats => {
const empty = ' ';
const common = 'App running at: - Local: http://127.0.0.1:${port}${publicPath}\n`;
console.log(chalk.cyan('\n' + empty + common));
});
});
Copy the code
The CSS extracts loader configuration
config/css.js
module.exports = (config, resolve) = > {
return (lang, test) = > {
const baseRule = config.module.rule(lang).test(test);
const normalRule = baseRule.oneOf('normal');
applyLoaders(normalRule);
function applyLoaders(rule) {
rule
.use('extract-css-loader')
.loader(require('mini-css-extract-plugin').loader)
.options({
publicPath: '/'
});
rule
.use('css-loader')
.loader('css-loader') .options({}); }}; };Copy the code
CSS extraction plugin MiniCssExtractPlugin
config/MiniCssExtractPlugin.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (config, resolve) = > {
return (a)= > {
config
.oneOf('normal')
.plugin('mini-css-extract')
.use(MiniCssExtractPlugin);
};
};
Copy the code
Automatic HTML generation
config/HtmlWebpackPlugin.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = (config, resolve) = > {
return (a)= > {
config.plugin('html').use(HtmlWebpackPlugin, [
{
template: 'public/index.html'}]); }; };Copy the code
This is the basic configuration based on webpack-chain. Let’s try to write a basic loader and plugin by ourselves. Before doing so, let’s review the basic concepts of loader and Plugin.
- Loader Webpack natively supports only JS and JSON file types, Loaders supports other file types and converts them into valid modules. The execution sequence of the Loader is from right to left. The execution result on the right is sent to the left as a parameter.
- Plugins are used to optimize bundle files, manage resources, and inject environment variables. Anything that loaders cannot handle can be done through plugins, which can be used throughout the build process.
Custom loader
options-chain-loader.js
module.exports = function(content) {
// Re matches. Content is the file content loaded by the loader
return content.replace(new RegExp(/([\$_\w\.]+\? \.) /.'g'),function(res) {
let str = res.replace(/ \? / \..' ');
let arrs = str.split('. ');
let strArr = [];
for(let i = 1; i <= arrs.length; i++) {
strArr.push(arrs.slice(0,i).join('. '));
}
let compile = strArr.join('&');
const done = compile + '&' + str + '. '
return done;
});
};
Copy the code
Used in the wepakc-chain configuration
module.exports = (config, resolve) = > {
const baseRule = config.module.rule('js').test(/.js|.tsx? $/);
const normalRule = baseRule.oneOf('normal');
return (a)= > {
normalRule
.use('options-chain')
.loader(resolve('options-chain-loader'))}}Copy the code
Custom plugins
fileListPlugins.js
class FileListPlugin {
apply(compiler) {
// console.log(compiler)
// Emit is an asynchronous hook, you can touch it using tapAsync, and you can also use tapPromise/tap(synchronous)
compiler.hooks.emit.tapAsync('FileListPlugin', (compilation, callback) => {
// In the makefile, create a header string:
var filelist = 'In this build:\n\n';
// Go through all compiled resource files,
// For each file name, add a line of content.
for (var filename in compilation.assets) {
// console.log(filename)
filelist += The '-' + filename + '\n';
}
// Insert this list as a new file resource into the WebPack build:
compilation.assets['filelist.md'] = {
source: function() {
return filelist;
},
size: function() {
returnfilelist.length; }}; callback(null.11);
});
}
// compiler.plugin('done', function() {
// console.log('Hello World! ');
// });
// }
// compiler.plugin("compilation", function(compilation) {
// // Now, set up callbacks to access steps in Compilation:
// compilation.plugin("optimize", function() {
// console.log("Assets are being optimized.");
/ /});
// });
}
module.exports = FileListPlugin;
Copy the code
Used in the wepakc-chain configuration
const FileListPlugin = require(".. /fileListPlugins");
module.exports = (config, resolve) = > {
return (a)= > {
config
.plugin('file-list-plugin')
.use(FileListPlugin)
}
}
Copy the code