This article introduces webPack configuration information in the form of the React plug-in package. Rollup is still recommended for writing simple plug-ins, but you can learn a few things about WebPack by writing plug-ins. (Suitable for junior and intermediate Webpack scholars)

1. Install Node and NPM, create a folder, run the NPM init command in the folder, press Enter to generate a package.json file as follows:

{" name ":" cobrandcard ", "version" : "1.0.0", "description" : ""," main ":" index. Js ", "scripts" : {" test ", "echo \" Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }Copy the code

The package.json file has the following functions: 1) Whenever NPM is used in a project, there is a packgae.json file in the project root directory, which can be created manually or generated by executing the NPM init command. 2) This file usually records the configuration information of the project, version, project name, license, author, etc. It also records the dependencies of the various modules required, including development and execution dependencies, as well as the scripts field (explained later) 3) when executing NPM install. NPM downloads project dependencies based on the modules in Dependencies and devDependencies in this file.

Question: What is the difference between dependencies and devDependencies? For example, you need to install react to run your program. When you use your React plug-in, you need to download the package of Dependencies in the production environment.

2. Create a SRC folder in the root directory to save your code snippets.

index.html

<html>

<head>
  <meta charset='UTF-8'>
</head>

<body>
  <div id='app'></div>
  <script src='.src/index.js'></script>
</body>

</html>
Copy the code

src/index.js

window.document.getElementById('app').innerText = 'hello, world! '
Copy the code

Open the index.html file and see hello World displayed in your browser.

3. Package your code with WebPack

NPM install webpack webpack-cli –save-dev webpack-cli

Use NPX webpack in the root directory. By default, package the index.js files in the SRC directory and generate a dist folder in the root directory to store the packaged code.

2) Manually configure the webpack package. The default webpack configuration file is webpack.config.js or webpackfile.js.

const path = require('path');

module.exports = {
  mode:'development'.entry:'./src/index.js'.output: {filename:'index.js'.path:path.resolve(__dirname,'dist')}Copy the code

NPX webpack: root folder dist (packaged file)

Use the package.json file scripts script command to quickly execute the packaging command

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"."build":"webpack --config webpack.config.js"
  },
Copy the code

NPM run build executes the same packaging command.

4. The local server webpack-dev-server

Purpose: Debug code locally without manually opening the index.html file or manually refreshing the page. What can Webpack-dev-server do for us? For example, before using Webpack-dev-server, I need to refresh the page every time I modify the code. If I want to see whether the packaged code is normal, I need to repackage and refresh the page again to see, and we need to manually run the HTML file. After using Webpack-dev-server, it helps us set up a simple server locally and can monitor code updates in real time, and after configuring hot updates, it can also make local updates without refreshing the page. NPM install webpack-dev-server –save-dev

devServer: {
    // Dist is the base directory
    contentBase: path.join(__dirname, "dist"),
    // Automatically compress code
    compress: true.// The service port is 1208
    port: 1208.// Automatically open the browser
    open: true.host: "dev.jd.com".// publicPath: "/assets/",
    hot: true,},Copy the code

Configuration Script information:

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"."build": "webpack --config webpack.config.js"."start":"webpack-dev-server"
  },
Copy the code

After repackaging, we run NPM start, and instead of seeing hello World printed on the page, we see the packaged folder, so we need to create an HTML file in the dist folder. The HTML-webpack-plugin plugin needs to be installed to generate HTML files.

const htmlWebpackPlugin = require('html-webpack-plugin')
plugins: [// Array for all webPack plugins
    new htmlWebpackPlugin({
      template:'./index.html'.filename:'index.html'})]Copy the code

Execute the package NPM run build operation and find that the HTML file is generated in the dist folder

<html>

<head>
  <meta charset='UTF-8'>
</head>

<body>
  <div id='app'></div>
<script src="main.js"></script></body>

</html>
Copy the code

NPM start: The local server is started, and the browser displays Hello World.

5. Hot Update (HMR)

The previous configuration of Webpack-dev-server only realized the functions of monitoring file changes, automatic packaging, and real-time page refresh. However, if we want to achieve hot update, we need to add a new configuration item. The hot configuration item indicates that hot update is enabled. Open hot update configuration needed again hotModuleReplacementPlugin plug-in.After the hot update configuration, modify the CSS style and save to find that the page is not refreshed (it is observed through the refresh button in the upper left corner of the browser), and directly update the view partially, which has been hot updated. If you want to implement hot update after modification of js file, you need to add a business code:Ensure that after implementing hot updates, you need to notify the business code to rerender to implement the page view changes.

6. Introduction of style files and image files

When we write UI plug-ins, we will also use CSS sASS style files, image files, etc., but because webpack is written by Node, it can only recognize js files, other types of files Webpack can not recognize. At this point, we need to use loaders to convert these files so that WebPack can recognize them.

Create a new index.css file under SRC folder, add a font style to the element with id as app, NPM start to check the effect, the page reported an error, can not identify the CSS file. NPM install style-loader css-loader –save-dev webpack.config.js

module: {rules:[
      {
        test:/\.css$/,
        use:['style-loader'.'css-loader'] // Execute from right to left}}]Copy the code

Repackage and run discovery style introduced successfully. CSS -loader is used to parse import style files, style-loader is used to add styles to head tags. How it works: Webpack uses regular expressions to find files that end in CSS and gives them to style-loader and CSS-loader. Thus, CSS files imported by import are converted to style tags and inserted into HTML files at run time.

NPM install file-loader –save-dev

module: {rules:[
      {
        test:/\.css$/,
        use:['style-loader'.'css-loader'] {},test:/\.(png|svg|gif|jpg)$/,
        use:'file-loader'}}]Copy the code

Q: What is the difference between url-loader and file-loader?

{
  test: /\.(png|jpg|gif)$/i,
  loader: 'url-loader'.options: {
    limit: 8192.mimetype: 'image/png'}}Copy the code

When the size of the file is smaller than a certain size, we will choose to use urL-loader, which will not package the image file separately, but will convert the image file into base64 and insert it into CSS file, which will reduce the number of HTTP requests and reduce the loss. Url-loader is compatible with file-loader. It uses url-loader when the file size is smaller than 8192 and file-loader when the file size is larger than 8192. NPM i-d postcss-loader autoprefixer: If we need to write some cSS3 attributes, such as transform, we hope webpack can automatically add the vendor prefix for us, so that it is compatible with different browser versions. In the root directory, create postcss.config,js and set it as follows:

module.exports = {
    plugins: [
        require('autoprefixer') ({overrideBrowserslist: [
                "Android 4.1"."iOS 7.1"."Chrome > 31"."ff > 31"."ie >= 8"]]}});Copy the code

We want to add style prefixes that cover Android 4.1, ios 7.1, etc. Webpack.config.js configuration:After packaging, we found that the CSS3 properties were added with vendor properties.

7. Webpack plug-in (plugins)

Plugins: help you do something when WebPack is running at a certain point. 1) HtmlWebpackPlugin generates an HTML file, And will file into the HTML 2) after the package CleanWebapckPlugin packaging before delete all last packaged good file 3) BundleAnalyzerPlugin used to package the plug-in 4) HotModuleReplacementPlugin performance analysis Plug-ins needed for hot updates

8. Grammar conversion (Babel)

Requirements: At this point, we have some other requirements. When we write the plugin, we may need to use the ES6 syntax and API. In this case, we need to use Babel to escape webpack. Babel is what? Babel is a toolchain that converts ECMAScript 2015+ version code into backwardly compatible JavaScript syntax, enabling code to run in current and older browsers.

The changes in Es6 are mainly divided into two parts: 1) Syntax, such as arrow functions and destructions, which are handled with @babel/preset-env; 2)API, such as Map and Promise, which are handled with @babel/polyfill

To deal with the ES6 syntax: After installing the above plug-ins, create a new babel.config.json file in the root directory and add the following rules:

{
  "presets": [["@babel/preset-env",
      {
        "targets": {
          "firefox": "60",},"useBuiltIns": "usage",}]]}Copy the code

By default, it will convert all es6+ syntax that the browser is not compatible with, but sometimes we do not need to be compatible with all browser versions, so we can use target to set the lowest compatible browser version. This means converting es6 syntax to be compatible with firefox60 and later browsers, and adding babel-loader to webpack.config.js.Business scenarios process es6 apis The polyfill gasket used to handle the ES6 API was introduced at the beginning of the file to convert the ES6 API, but after packaging the file became several times larger, it imported all the packages, can we implement on-demand import? When the useBuiltIns attribute is set to Usage, it means import on demand. It will import es6 attributes that are not supported by IE8 above in our program through global variables. It is compatible with all APIS and prototype methods. One is to declare a version of Corejs, otherwise errors will be reported when packaging, and the other is that Corejs3 improves on some of the shortcomings of 2 and is compatible with archetypal methods such as includes.The plug-in scenario handles the ES6 APIThe application scenario of the second configuration method is plug-in or framework, which is implemented by plugin-transform-Runtime.Why distinguish between two usage scenarios for dealing with ES6 apis? The first method is to introduce the code package in the form of global variables, which will pollute global variables, which are not feared in business scenarios. However, in the plug-in scenario, we do not want to pollute the environment for others to use our plug-in, so we choose to use the second one (which creates a sandbox environment and is isolated from the global environment).

9. Join the react

Requirements: Last part, since we’re writing a React plugin, but webPack doesn’t have a way to recognize JSX syntax yet, we’ll configure it. The main configuration is to add @babel/preset- React to the Babel configuration file, and use babel-loader in the webpack configuration file to handle JSX syntax, so react can be used

"@babel/preset-react"
Copy the code

This allows you to run the React code in your project. Install React and react-dom.

10. Production environment packaging

We have now written a plug-in and tuned it locally. Next, we’re gonna pack it up and upload it. Create a new file: webpack. Config. Product. Js

const path = require('path');

module.exports = {
  mode: 'production'.entry: './src/index.js'.output: {
    libraryTarget: 'umd'.filename: 'index.js'.path: path.resolve(__dirname, 'build'),
    chunkFilename: '[name].min.js'
  },
  externals: {
    'react': 'react'.'react-dom': 'react-dom'
  },
  module: {
    rules: [{test: /\.css$/,
        use: ['style-loader'.'css-loader'] {},test: /\.(png|svg|gif|jpg)$/,
        use: 'file-loader'
      },
      {
        test: /\.jsx? $/,
        loader: 'babel-loader'.exclude: /node_modules/}}}]Copy the code

LibraryTarget and Library are required output attributes for library development. How do we want others to introduce libraries when we develop them? This can be done in the following ways: Traditional script:

<script src="demo.js"></script>
Copy the code

AMD way:

define(['demo'].function(demo) {
demo();
});
Copy the code

Commonjs way:

const demo = require('demo');
demo();
Copy the code

The ES6 module is imported

import demo from 'demo';
Copy the code

Why does the class library support different introductions? This is what webpack.library and output.libraryTarget provide. The output.libraryTarget property controls how webPack packed content is exposed. The way of exposure is divided into the following three ways: Expose a variable libraryTarget: “var”

output: {
    libraryTarget:'var'.library:'abc'.filename: "index.js".path: path.resolve(__dirname, "build"),},Copy the code

The value packaged by Webpack is assigned to a variable named output.library. Here are some of the webpack packages:Copies the packaged content to a global variable that is used directly when referencing the class library, which is not supported by the NodeJS environment.

<head>
  <meta charset='UTF-8'>
  <script src='./build/index.js'></script>
  <script>
    console.log(abc.mytest())
  </script>
</head>
Copy the code

Expose the specified property assigned to the specified object by the return value of the object property library. Attribute is specified by output.library and object is specified by output.libraryTarget. 1)libraryTarget: “this”

this["myDemo"] = _entry_return_;

this.myDemo();
myDemo(); 
Copy the code

2)libraryTarget: "window"

window["myDemo"] = _entry_return_;

window.myDemo.doSomething();
Copy the code

3)libraryTarget: “global” global[“myDemo”] = entry_return; The nodeJS environment can be supported in this case.

mode: "production".entry: "./src/index.js".target:'node'.output: {
    libraryTarget:'global'.library:'abc'.filename: "index.js".path: path.resolve(__dirname, "build"),},Copy the code

The above three methods export your method functions on public objects. Advantages: Reduced variable conflicts Disadvantages: Not supported in the NodeJS environment

Three. Exposure through modules

  1. libraryTarget: "commonjs"
exports["myDemo"] = _entry_return_;

require("myDemo").doSomething();
Copy the code

Exports directly from exports objects — variables defined in the Library that node supports but not browsers

<head>
  <meta charset='UTF-8'>
  <script src='./build/index.js'></script>
  <script>
    console.log(require('abc').mytest())
  </script>
</head>
Copy the code

This option can be used in the CommonJS environment. 2)libraryTarget: "commonjs2"

module.exports = _entry_return_;

const myDemo = require("myDemo");
myDemo();
Copy the code

Exports directly from module.exports, ignoring library variables. Node supports this option, browsers do not support this option. Why doesn’t CommonJS need to introduce RequireJS separately? Commonjs is a server-side modularization language specification that uses requireJS in Node. 3) libraryTarget: “amd”

define("myDemo"[],function() {
return _entry_return_;
});
Copy the code
require(['myDemo'].function(myDemo) {
// Do something with the library...
myDemo();
});
Copy the code
<head>
  <meta charset='UTF-8'>
  <script src='./build/index.js'></script>
  <script>
    require(['abc'].function (mytest) {
        // Do something with the library...
        console.log(mytest());
      });
    
  </script>
</head>
Copy the code

Amd is a specification of the client module language and requires users to introduce RequireJS to use it. Nodejs is not supported. Browser is supported. 4)libraryTarget: "umd"The scheme supports CommonJS, CommonJS2, AMD, and can be used in browsers and Nodes. It determines which environment it belongs to based on the context in which the plug-in is referenced, making it compatible with CommonJS, AMD, or exposed as a global variable.

(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports= = ='object' && typeof module= = ='object')
  module.exports = factory();
else if(typeof define === 'function' && define.amd)
  define([], factory);
else if(typeof exports= = ='object')
  exports["MyLibrary"] = factory();
else
  root["MyLibrary"] = factory(); }) (typeofself ! = ='undefined' ? self : this.function() {
return _entry_return_;
});
Copy the code
output: {
library: {
  root: "myDemo".amd: "my-demo".commonjs: "my-common-demo"
},
libraryTarget: "umd"
}
Copy the code

Finally, I suggest that if the goal is clear and I am only compatible with NodeJS, then choose CommonJS/Commonjs2, if only compatible with browsers, then choose the exposed variable method, if you want to be universal, then choose UMD method. It is a wise choice to do multiple processing methods for different situations.

11. Perform hot updates while saving the React status

After using hot updates, we found that if we changed something in a file, the file would be rerendered once, and some states in the file, such as the React classic counter, would be reset. After the backtop file is modified, the entire component is rendered and the state is reset.How can we do hot updates while keeping the React state? NPM install react-hot-loader @hot-loader/react-dom –save-dev babel.config.json

"plugins": ["react-hot-loader/babel"]
Copy the code

Backtop.js uses react-hot-loader to package a layer:

import { hot } from "react-hot-loader/root";
 
/ /...
 
export default hot(App);
Copy the code

Hot update can be implemented on the premise of saving the React state.

Above: We implemented the basic WebPack to implement the React plugin configuration, if you want to implement other functions, can be stacked. Webpack has a long way to go. I wish you all to be a strong ‘senior Webpack configurator’.