This article uses webpack-heiry-starter to Quickly build a Webpack4 local learning environment. It is recommended to read the section “Writing a Plugin” in the Webpack documentation to learn how to develop simple plug-ins.
This article will take you from Webpack configuration engineer to Webpack development engineer by developing your first Webpack plug-in! Be your own wheel, and let others use it.
The complete code is at: github.com/pingan8787/…
I. Background introduction
This article is inspired by the summary of experience in the business. I am not afraid of the god-like products, but the single-minded development.
There is a problem in project packaging: “When the project is hosted to the CDN platform, it is hoped that the index.js in the project will not be cached”. Because we need to modify the contents of index.js, we don’t want the user to be cached.
Think about it for a while, there are several ideas:
- Filter the cache Settings of this file in the CDN platform;
- Find the DOM element and modify it
script
Of the labelsrc
Value and add time stamp; - Dynamic creation at packaging time
script
The tag introduces the file and adds a time stamp.
(There are other ways for you to be smart. Welcome to discuss.)
Analysis of ideas:
- Obviously, if the CDN setting is modified, it will cure the symptoms rather than the root cause;
- In the template file, add
script
Tag, perform get Webpack automatically addedscript
Tag and for itsrc
Value adds a timestamp. But the fact is that before you finish modifying, js files have been loaded, so give up - Need to be in
index.html
Before generating, change the path of the JS file and add the timestamp.
So I’m going to use the third method, inindex.html
Complete the following changes before generating:
Simple question, actually want to try to develop the Webpack Plugin.
Two, basic knowledge
Webpack uses staged build callbacks so developers can introduce their own behavior into the Webpack build process. Before developing, you need to understand the following Webpack concepts:
2.1 Webpack plug-in composition
Before we customize our plug-in, we need to understand what a Webpack plug-in consists of. Here is an excerpt from the documentation:
- A named JavaScript function;
- Define the apply method on its prototype;
- Specify an event hook that touches Webpack itself;
- Manipulate instance specific data within Webpack;
- The callback provided by Webpack is called after the functionality is implemented.
2.2 Basic architecture of Webpack plug-in
The plug-in is instantiated by a constructor. The constructor defines the apply method, which is called once by the Webpack Compiler when the plug-in is installed. The Apply method can receive a reference to the Webpack Compiler object, which can be accessed in the callback function.
The official documentation provides a simple plug-in structure:
class HelloWorldPlugin {
apply(compiler) {
compiler.hooks.done.tap('Hello World Plugin'.(
stats /* Stats is passed in as an argument when a hook is touched. * /
) = > {
console.log('Hello World! '); }); }}module.exports = HelloWorldPlugin;
Copy the code
Using plug-ins:
// webpack.config.js
var HelloWorldPlugin = require('hello-world');
module.exports = {
/ /... Here are the other configurations...
plugins: [new HelloWorldPlugin({ options: true}})];Copy the code
2.3 introduce HtmlWebpackPlugin
The HtmlWebpackPlugin simplifies the creation of HTML files to serve your Webpack. This is especially useful for Webpack bundles that include a hash in their file name that changes each time they compile.
The basic function of the plug-in is summarized: generate HTML files.
The htML-webapck-plugin has two main functions:
- Bring in external resources for HTML files (e.g
script
/link
) dynamically add hash after each compilation to prevent cache problems of reference files; - Create HTML entry files dynamically, as in a single page application
index.html
File.
HTML – Webapck-plugin plugin principle introduction:
- Read the Webpack
entry
Configuration related entrychunk
å’Œextract-text-webpack-plugin
CSS styles extracted by plug-ins; - Insert the style into the plug-in provided
template
或templateContent
Configure the specified template file; - The insertion mode is through
link
The tag introduces styles throughscript
The tag introduces the script file.
Iii. Development process
The principle of SetScriptTimestampPlugin developed in this paper is as follows: Before the HTML file is generated by HtmlWebpackPlugin, the reserved space of the template file is replaced with a script, in which time stamps are automatically added to reference the script file.
3.1 Operation mechanism of plug-ins
3.2 Initializing the plug-in file
New SetScriptTimestampPlugin js file, and refer to the basic structure of the plug-in in official document, initialize the plug-in code:
// SetScriptTimestampPlugin.js
class SetScriptTimestampPlugin {
apply(compiler) {
compiler.hooks.done.tap('SetScriptTimestampPlugin'.(compilation, callback) = > {
console.log('SetScriptTimestampPlugin! '); }); }}module.exports = SetScriptTimestampPlugin;
Copy the code
The Apply method is the plug-in prototype method and accepts compiler as a parameter.
3.3 Selecting plug-in trigger timing
Choosing when to trigger the plug-in is actually a matter of choosing which compiler hooks the plug-in triggers. Compiler Hooks Webpack provides many Hooks. Here are a few.
entryOption
: in the Webpack optionentry
After the configuration items are processed, execute the plug-in.afterPlugins
: After setting up the initial plug-in, execute the plug-in.compilation
Execute the plug-in after compiling and creating, but before generating the file.emit
: Generates resources tooutput
Directory before.done
: The compilation is complete.
Our plugin is supposed to dynamically add script tags before the HTML output, so we hook into the compilation stage and modify the code:
// SetScriptTimestampPlugin.js
class SetScriptTimestampPlugin {
apply(compiler) {
- compiler.hooks.done.tap('SetScriptTimestampPlugin',
+ compiler.hooks.compilation.tap('SetScriptTimestampPlugin',(compilation, callback) => { console.log('SetScriptTimestampPlugin! '); }); } } module.exports = SetScriptTimestampPlugin;Copy the code
Specifying the event hook function under Compiler.hooks executes the callback function when the hook is triggered. Webpack provides three ways to trigger hooks:
tap
To:synchronouslyTrigger hook;tapAsync
To:asynchronousTrigger hook;tapPromise
To:asynchronousTriggers the hook, which returns a Promise;
The compilation is also a different set of hook methods. Because the compilation is a SyncHook synchronized hook, the tap operation is used. The TAP method takes two parameters: the plug-in name and the callback function.
3.4 Adding a plug-in replacement entry
We basically take the template file, specify the replacement entry, and replace it with the script that needs to be executed.
So we add
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Introduction to Webpack plug-in development</title>
</head>
<body>
<! -- other code -->
<! --SetScriptTimestampPlugin inset script-->
</body>
</html>
Copy the code
3.5 Writing plug-in logic
At this point, start writing the logic for the plug-in. From the previous step, we know that the second parameter to tap is a callback function, and that the callback function has two parameters: compilation and callback.
Compilation inherits from Compiler, contains all the compiler content (as well as Webpack options), and also has plugin functions to access task points.
// SetScriptTimestampPlugin.js
class SetScriptTimestampPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('SetScriptTimestampPlugin'.(compilation, callback) = > {
// The plugin logic calls the compilation plugin method
compilation.plugin(
"html-webpack-plugin-before-html-processing".function(htmlPluginData, callback) {
// Read and modify the SRC list on script
let jsScr = htmlPluginData.assets.js[0];
htmlPluginData.assets.js = [];
let result = `
<script>
let scriptDOM = document.createElement("script");
let jsScr = "./${jsScr}";
scriptDOM.src = jsScr + "?" + new Date().getTime();
document.body.appendChild(scriptDOM)
</script>
`;
let resultHTML = htmlPluginData.html.replace(
"<! --SetScriptTimestampPlugin inset script-->", result
);
// Returns the modified resulthtmlPluginData.html = resultHTML; }); }); }}module.exports = SetScriptTimestampPlugin;
Copy the code
In the plugin logic above, we do these things:
- perform
compilation.plugin
Method with two parameters: plug-in event and callback method.
The “plug-in events” are the events provided by the plug-in, which are used to monitor the status of the plug-in. Here are a few events provided by the HTmL-webpack-plugin (see htML-webpack-plugin for full details) : Async:
html-webpack-plugin-before-html-generation
html-webpack-plugin-before-html-processing
html-webpack-plugin-alter-asset-tags
Sync:
html-webpack-plugin-alter-chunks
- Get and clear the list of script file names.
In the callback method, use htmlplugindata.assets. js to get the list of script file names that need to be imported through script, copy it, and delete the original list.
- Write the replacement logic.
The replacement logic is to dynamically create a script tag, set its SRC value to the filename of the script read in the previous step, and concatenate the timestamp as a parameter.
- Insert the replacement logic.
Htmlplugindata.html can be used to get the string output of the template file. We just need to replace the entry
Replace with the replacement logic we wrote in the previous step.
- Returns the HTML file.
Finally, the modified HTML string is assigned to the original htmlplugindata.html to achieve the modification effect.
3.5 Using Plug-ins
Custom plugins to use, same as other plugins, instantiated in the plugins array:
// webpack.config.js
const SetScriptTimestampPlugin = require("./SetScriptTimestampPlugin.js");
module.exports = {
/ /... Omit other configurations
plugins: [
/ /... Omit other plug-ins
new SetScriptTimestampPlugin()
]
}
Copy the code
At this point, we have implemented the requirement “when the project is hosted on the CDN platform, we want to implement that the index.js in the project is not cached”.
Iv. Case expansion
Here we use the SetScriptTimestampPlugin plugin as an example to further expand.
4.1 Reading plug-in configuration parameters
Each plug-in is essentially a class, like an instantiation of a class, which can be instantiated by passing in configuration parameters that operate in the constructor:
// SetScriptTimestampPlugin.js
class SetScriptTimestampPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
console.log(this.options.filename); // "index.js"
/ /... Omit other code}}module.exports = SetScriptTimestampPlugin;
Copy the code
When using:
// webpack.config.js
const SetScriptTimestampPlugin = require("./SetScriptTimestampPlugin.js");
module.exports = {
/ /... Omit other configurations
plugins: [
/ /... Omit other plug-ins
new SetScriptTimestampPlugin({
filename: "index.js"}})]Copy the code
4.2 Adding time stamps for Multiple script files
If we need to change the timestamp of multiple script files at the same time, we only need to adjust the parameter type and execution script. To modify the script, here is not the specific expansion, space is limited, you can think about the implementation of the ~ here is the use of plug-in parameters:
// webpack.config.js
const SetScriptTimestampPlugin = require("./SetScriptTimestampPlugin.js");
module.exports = {
/ /... Omit other configurations
plugins: [
/ /... Omit other plug-ins
new SetScriptTimestampPlugin({
filename: ["index.js"."boundle.js"."pingan.js"]]}})Copy the code
Generate results:
<script src="./index.js? 1582425467655"></script>
<script src="./boundle.js? 1582425467655"></script>
<script src="./pingan.js? 1582425467655"></script>
Copy the code
Five, the summary
This article uses custom Webpack plug-ins to implement some of the trickier requirements of everyday life. This paper mainly introduces the basic composition and simple structure of Webpack plug-in, and also introduces HtmlWebpackPlugin plug-in. Based on these basic knowledge, an HTML text replacement plug-in is completed. Finally, two scenarios are used to expand the scope of the plug-in.
Finally, there is more knowledge to learn about Webpack plug-in development. It is recommended to read the official document “Writing a Plugin” to learn more.
This article is purely a summary of personal experience, if you have any objections, welcome to give advice.
Reference documentation
- “Writing a Plugin”
- HtmlWebpackPlugin – Webpack
- “Extending HtmlwebpackPlugin to Insert custom Scripts”