preface

I am currently learning Webpack. First of all, I will make a simple summary of the knowledge points of Webpack. Please go to the official website to learn more comprehensively. Secondly, a Loader and plugin are implemented in combination with Teacher Wang Lei’s “WebPack Principle and Practice” to sort out the whole operation process. Please point out any errors.

First, webpack basic knowledge

1, webpack

What is Webpack?

  • webpackIt is a module packer, which treats our development project as a whole and packages all the resources used in the development process together for output.

What does WebPack solve?

  • We may use code for new features during development, and WebPack can convert this code into code compatible with most environments.
  • Webpack can package the rest of a project’s resources together for output, thus eliminating frequent browser requests for files.
  • Webpack supports the packaging of different resources, including fonts, styles, images, etc. So we have a unified modular solution, and the loading of resources can be controlled by code.

2, entry

Entry: Mainly defines the file from which webpack is packaged. It is written as follows:

//webpack.config.js
module.exports={
    entry:'./path/main.js'
}
Copy the code

You can also write objects this way

//webpack.config.js
module.exports={
    entry: {main:'./src/main.js'}}Copy the code

Webpack starts from the entry interface file composition to build the dependency map. After entering the file, it will find all the modules or resources dependent on the entry and exit file and package them.

3, the loader

Loader: in fact, Webpack can only deal with JS code, but we said before webpack can deal with the development of other resources, is the use of loader can carry out the conversion of corresponding resources. Let’s say we load the CSS code and install it first

npm install --save-dev css-loader
Copy the code

Then configure it in webpack.config.js

module.exports = {
  module: {
    rules: [{test: /\.css$/.use: 'css-loader'}}}]Copy the code

Or you could write it this way

module: {
    rules: [{test: /\.css$/.use: [{loader: 'style-loader' },
          {
            loader: 'css-loader'.options: {
              modules: true}}]}Copy the code

Of course, not only these two ways of writing, but also in reference to resources when writing, can also be written in the CLI, the specific can go to the official website to learn, in fact, this will report errors, because CSS-loader only CSS code with JS code to wrap, to use also need to install style-loader, about this knowledge point, More on that below.

4, the plugins

Plugins: Plugins are very powerful. They mainly optimize the whole packaging process, such as compression, automatic HTML generation, automatic file cleaning, etc.

const HtmlWebpackPlugin = require('html-webpack-plugin'); 
// Install via NPM
module.exports = {
    plugins: [
        new HtmlWebpackPlugin({template: './src/index.html'}})]Copy the code

5, the output

Output: mainly defines the configuration of packaged file output, including file name, output path, etc. As shown below.

//webpack.config.js
module.exports={
    output: {filename:'bundle.js'
        path:'./dist'}}Copy the code

Or you could write it this way

//webpack.config.js
module.exports={
    entry: {
        app: './src/app.js'.search: './src/search.js'
    },
    output: {
        filename: '[name].js'.path: __dirname + '/dist'}}Copy the code

More detailed writing and introduction can be directly to the official website.

Ii. Running process and principle of Webpack

1. Loader introduction

Webpack wants to realize the modular of the whole front-end project, it can not only manage JS files, the whole project of each resource including CSS, images, etc., should be managed; By default, Webpack can only manage JS files. How does Webpack manage files of other resources?

Let’s start by loading CSS files through WebPack to explore how WebPack loads module resources. Specify our entry file as main.css as shown below

/* main.css */
div{
    color:red
}
Copy the code

The configuration file is

//webpack.config.js
module.exports={
    entry:"./src/main.css".output: {filename:'bundle.js'}}Copy the code

Package the files

css
webpack
js
js
css
webpack
js
js
css
js
css
js

/* main.css*/ console.log(' CSS file ');Copy the code

css
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
webpack
js
loader
loader
loader
js
js
loader
loader
webpack

Next we will install the CSS-Loader that handles CSS files

npm install css-loader --save-dev
Copy the code

Do the WebPack configuration

//webpack.config.js
const path=require('path');
module.exports={
	entry:'./src/main.css'.output: {filename:'bundle.js'.path:path.resolve(__dirname,'dist')},mode:none,
	module: {rules: [{test:/$\.css/.use:'css-loader'}}}]Copy the code
/*main.css*/
div{
    color:red
}
Copy the code

After repackaging, there is no error, but the HTML file does not turn red as we expected. Why is this? Let’s take a look at how the CSS section of the bundle.

css
js
css
push
css-loader
css
js
style-loader
js
style
style-loader

npm install style-loader --save-dev
Copy the code

Do the WebPack configuration

//webpack.config.js
const path=require('path');
module.exports={
	entry:'./src/main.css'.output: {filename:'bundle.js'.path:path.resolve(__dirname,'dist')},mode:none,
	module: {rules: [{test:/$\.css/.use: ['style-loader'.'css-loader'}]}}Copy the code

Again to pack, found that the normal display of red. Here is a new module that handles the modules returned by CSS-Loader. There is also a module 1, the space is too big, I will not attach the graph, interested in you can have a try. To sum up :style-loader is used to load all style modules in CSS-Loader and add them to the page by creating style tags

use
css
webpack
loader
css
css-loader
css-loader
css-loader
style-loader
sttyle-loader
style

2. Implement a simple loader

In order to better understand loader, let’s implement a Loader. The implementation principle of Loader is actually very simple. Loader is essentially a function. The parameters of the function are the files we need to load. The returned content can be used as the parameters of the next loader, and finally output a JS module or add it to bundle.js in other forms.

We have developed a loader that can load markdown files. Markdown files need to be converted into HTML before they can be displayed on the page, so we need to import markdown files and convert them into HTML strings that can be displayed.

We can create a markdown-loader.js file in the root directory instead of the NPM installed module. When loading the markdown file, we can directly load the file we created. The directory is as follows:

//about.md
#About
this is a markdown file
Copy the code
//main.js
import about from './about.md'
console.log(about)
Copy the code

Loader is essentially an export function, which processes the loaded content. The function needs to receive a parameter, which is the loaded file, and output the result after loading.

//markdown-loader.js
module.exports=function(source){
    console.log(source)
    return 'hello loader'
}
Copy the code
//webpack.config.js
const path=require('path');
module.exports={
	entry:'./src/main.js'.output: {filename:'bundle.js'.path:path.resolve(__dirname,'dist')},mode:'none'.module: {rules:[
			{
				test:/\.md$/.use:'./markdown-loader'}}}]Copy the code

After packaging, it will look like the picture below:

markdown
hello loader
loader
loader
js
bundle.js
markdown-loader.js

//markdown.loader.js
module.exports=function(source){
    console.log(source)
    return 'console.log("hello loader")'
}
Copy the code

This will work just fine, and you can look at our packaging results

js
bundle.js

Next, we will continue to implement our loader logic. If we want the Markdown file to be properly displayed in the web page, we need to display the corresponding resources as HTML files. Here, it is complicated, so we use the corresponding marked module for conversion, and need to install and reference the marked module.

npm install marked --save-dev
Copy the code
//mrkdown-loader.js
const marked=require('marked')
module.exports=source= >{
    cosnt html=marked(source);
    const code=`module.exports=The ${JSON.stringify(html)}`;
    return code
}
Copy the code

Return the HTML file as a JS module, which we can print out from the browser.

//mian.js
import about from  './about.md';
function component(about){
	var element=document.createElement('div');
	element.innerHTML=about;
	return element
}
document.body.appendChild(component(about))
Copy the code

The result is shown below

bundle.js

about
__webpack_requir__(1)

bundle.js
index.html
bundle.js
http

The principle of loader design is that each loader has a single function. In order to better understand the loader mechanism, the loader written above can be further subdivided.

//mrkdown-loader.js
const marked=require('marked')
module.exports=source= >{
    cosnt html=marked(source);
    return html
}
Copy the code
//html-loader.js
module.exports=source= >{
    const code=`module.exports=The ${JSON.stringify(source)}`;
    return code;
}
Copy the code

Of course we need to make some changes to the configuration file

//webpack.config.js
const path=require('path');
module.exports={
	entry:'./src/main.js'.output: {filename:'bundle.js'.path:path.resolve(__dirname,'dist')},mode:'none'.module: {rules:[
			{
				test:/\.md$/.use: ['html-loader'.'./markdown-loader'}]}}Copy the code

The effect is the same. To summarize, WebPack has a built-in loader that handles JS files. When we load files of other resources, webPack cannot handle the related resources, so we need to use the corresponding loader for loading. The essence of loader is a function that can receive parameters and process the loaded resources. The processing results can be returned to the next loader as parameters to continue processing, and finally return a JS module or add methods to the bundle.js. Each module we load is stored in the bundle.js file as an item in the array, and other modules can retrieve the reference directly from the array if they need it.

3. Plugin introduction

Plugin is primarily designed to enhance WebPack’s ability to automate the build of projects. Auto-generate HTML documents, auto-clean dist directories before packaging, inject global variables, and so on. Plug-ins allow us to implement almost all of the functions used in front-end engineering.

Let’s try out a plugin. Before each package, we need to automatically clean up the Dist directory. This can be done using the clean-webpack-plugin. So let’s just install it

npm install clean-webpack-plugin
Copy the code

Configuration file Reference

//webpack-config.js
const {CleanWebpackPlugin}=require('clean-webpack-plugin')
module.exports={
    entry:'./src/main.js'
    output:{
        filename:'bundle.js'
    },
    plugins: [new CleanWebpackPlugin()
    ]
}
Copy the code

This will clean up the dist directory. There are many commonly used plugins, you can go to the official website to be familiar with how to use.

4. Develop a Plugin

The plugin mechanism for WebPack is essentially the hook mechanism we often see. There are many links in the whole working process of WebapCK, and almost every link has corresponding hook functions. The development of Plugin is to add different tasks based on these hook functions. When The implementation of Webpack reaches this link, the corresponding hook functions will be triggered, and the corresponding plugin will be triggered. The plugin can then process the file in this state. For example, compress the entire resource at the end of the generation, and so on. Let’s develop a plugin.

Our requirement is to develop a plug-in that removes comments in bundle.js, as shown on the far left, to make our articles more readable.

remove-comments-plugin.js
plugin
apply
apply

//remove-comments-plugin.js
class RemoveCommentsPlugin{
    apply(compiler){
        console.log('RecomveCommentsPlugin start'); }}Copy the code

When WebPack is started, this type generates an object, and the object’s Apply method receives a Compiler parameter. This parameter is the core object of WebPack work, which contains all configuration information during webPack construction. It is through this object that the corresponding hook function is registered. The emit hook is called when the output directory is generated, so this is the best stage for the emit hook function.

compiler
hooks
emit
tap
compilation
compiler
compiler
webpack
compilation
compilation
assets

//webpack-config.js
const {CleanWebpackPlugin}=require('clean-webpack-plugin')
const RemoveCommentsPlugin=require('remove-comments-plugin')
module.exports={
    entry:'./src/main.js'
    output:{
        filename:'bundle.js'
    },
    plugins: [new CleanWebpackPlugin(),
        new RemoveCommentsPlugin()
    ]
}
Copy the code
//remove-comments-plugin.js
class RemoveCommentsPlugin{
    apply(compiler){
        compiler.hooks.emit.tap('RemoveCommentsPlugin'.function(compilation){
            for(const name of compilation.assets){
                console.log(name); }}}})module.exports=RemoveCommentsPlugin;
Copy the code

//remove-comments-plugin.js
class RemoveCommentsPlugin{
    apply(compiler){
        compiler.hooks.emit.tap('RemoveCommentsPlugin'.function(compilation){
            for(const name of compilation.assets){
                console.log(compilation.assets[name].source())
            }
        })
    }
}
module.exports=RemoveCommentsPlugin;
Copy the code

compilation.assets

//remove-comments-plugin.js
class RemoveCommentsPlugin{
    apply(compiler){
        compiler.hooks.emit.tap('RemoveCommentsPlugin'.function(compilation){
            for(const name of compilation.assets){
                const content=compilation.assets[name].source();
                const noContent=content.replace(/\/\*{2,}\/\s? /g.' ');
                compilation.assets[name]={
                    source:(a)= >noContent,
                    size:(a)= >noContent.length
                }
            }
        })
    }
}
module.exports=RemoveCommentsPlugin;
Copy the code

After this execution, the generated bundle.js has no previous comments, as shown below:

5. The whole process of Webpack packaging

Here I will introduce the core packaging process of Webpack, this process I listen to the documents and their own understanding, if there is a mistake, please feel free to comment, thank you.

(1) Webpack CLI starts the packaging process

  • Parse webpack CLI command parameters, as shown in the followingmode=productionCheck whether the configuration file specified by the command parameter is loaded (if no configuration file is specified, use the default configuration file), combine the loaded configuration file with the command parameter configuration, and use the command parameter preferentially. The result is a complete configuration object (Options).

(2) Create the core module of Webpack

  • Use what you got in the previous stepoptionsParameter to create a WebPack core objectcompiler. First of all, check the incomingoptionsArgument, if it is an object, to create onecompilerIf it is an array, create oneMultiCompilerThat is to saywebpackSupport multi-way packaging;pluginsIt’s essentially an array that contains these various instances, and the next step is to iterate through each instance of the arraycompilerObject as a parameterpluginThe instanceapplyMethod, the method callcompilerhooksRegisters a hook function that calls the corresponding callback when fired. The followingcompilerThe life cycle begins.

(3) Use compiler to compile the entire project

  • callcompilertherunMethod, this method has internalbeforeRunandrunTwo methods, this phase calls the object’scompileMethod to generate acompilationObject, which is the context of the build process and contains all the resources and information in the build.
  • And then we callmakeHooks, this phase is the most central part of the build process. Our default is single-entry packaging, so it will be implementedSingleEntryPluginThis plug-in, this plug-in is calledCompilationThe object of theaddEntryMethod from the configurationentryFind the import/export file and add the import module to the module dependency list. Next callCompilationThe object’sbuildModuleMethod for module construction,buildModuleMethods are mainly implementedloaderProcessing of special resources, generated after loadingASTSyntax tree, for this syntax tree to analyze whether the module has a dependent module, if so continue the loopbuildModuleEach dependency; All dependencies are resolved,buildModulePhase ends.
  • Merge to produce outputbuild.jsAnd output to the directory. The make phase is to find the entry file based on the configuration fileentryRecurse all dependencies in turn, form a dependency tree, and give each module recursed to a differentloaderProcess. According to theoutputOutput).

### 3, WebPack-related optimizations

Webpack implements simple VUE projects

(1) Install the corresponding installation package:

  • webpack,webpack-cli: Take charge of the packing process
  • vue:vueRuntime dependencies
  • vue-loader: Load loadingvueFile, thisloaderYou have to rely onvue-template-compilerparsingcompleteComponents,css-loaderContinue CSS styling within the component. One more needs to be loaded at this stageVueloaderPlugin
  • html-webpack-plugin: Responsible for generationhtmlfile
  • clean-webpack-plugin: Is responsible for clearing files before generating themdistdirectory

(2) Project related catalog

(3) Specific code

//webpack.config.js
const path=require('path');
const {CleanWebpackPlugin}=require('clean-webpack-plugin')
const HtmlWebpackPlugin=require('html-webpack-plugin')
const VueLoaderPlugin=require('vue-loader/lib/plugin')
module.exports={
	entry:path.resolve(__dirname,'src/index.js'),
	output: {filename:'bundle.js'.path:path.resolve(__dirname,'dist')},mode:'development'.module: {rules:[
			{
				test:/\.vue$/.use:'vue-loader'}, {test:/\.css$/.use: ['style-loader'.'css-loader']]}},resolve: {alias: {'vue$':'vue/dist/vue.js'}},plugins: [new CleanWebpackPlugin(),
		new HtmlWebpackPlugin({
			title:'todo-demo'
		}),
		new VueLoaderPlugin()
	]
}
Copy the code
//src/index.js
import Vue from 'vue'
import App from './app.vue'
const root=document.createElement('div');
document.body.appendChild(root);
new Vue({
	render:(h) = >h(App)
}).$mount(root)
Copy the code
//App.vue <template> <div id="text"> {{text}} </div> </template> <script> export default{ data(){ return{ text:'abc' } }  } </script> <style> #text{ color:red; } </style>Copy the code

(4) Operation effect