The last article recorded the use of webpack4 some basic use of small skills, really did not expect to harvest so big response, or very thank you for your wrong love, did not read about Webpack4 14 knowledge points, fair trade

This article combines React with WebPack 4 to build a powerful React development environment from scratch

React-webpack4-cook: react-webpack4-cook: react-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook If it is helpful to you, you might as well give a star

preface

An article does not write the preface to the total feeling is not too formal, about how I complete a general knowledge point of the summary. Actually a lot of people to have a look at will, one can do the characteristics of waste (of course, including me), and this time, you need to develop a detailed plan slightly, like I this article will first list knowledge, list the direction of the big, to develop a mind map, and then, according to a mind map coding plan clear, will get twice the result with half the effort, therefore, I hope you can follow this step by step with the code to go over, whether it is real development, or interview, some pull. All right, let’s just take a look at the catalog. now

1. Basic configuration

1. Init project

mkdir react-webpack4-cook
cd react-webpack4-cook
mkdir src
mkdir dist
npm init -y
Copy the code

2. Install WebPack

yarn add webpack webpack-cli webpack-dev-server -D // WebPack4 breaks webpack
touch webpack.config.js
// webpack.config.js initializes the content
module.exports = {
    mode: "development".entry: ["./src/index.js"].output: {
        // Output directory
        path: path.join(__dirname, "dist"),
        // File name
        filename: "bundle.js"
    },
    module: {},plugins: [].devServer:{}
}
Copy the code

Install React and write the code

This part of the code is too long, just some simple react and react-router code writing, you can go to Github to check, here only describes the basic functions

cd src 
cnpm i react react-router-dom -S
// Install react and react-router with react code| - SRC │ index. Js master file ├ ─ ─ ─ pages │ Count. JSX - realized the function of a counter, click on the button, can let a number increase, │ home.jsx -- a simple text display of the ├ ─router index.js -- route configuration file, 2 pages for count and HomeCopy the code

Babel compiles ES6,JSX, etc

// @babel/core-babel @babel/preset-env- Build ES6 etc @babel/preset-react- convert JSX
cnpm i babel-loader @babel/core @babel/preset-env  @babel/plugin-transform-runtime   @babel/preset-react -D
// @babel/plugin-transform-runtime: avoid polyfill contaminating global variables, reduce packing volume
// @babel/polyfill: ES6 built-in method and function conversion gasket
cnpm i @babel/polyfill @babel/runtime
 {
        test: /\.jsx? $/.exclude: /node_modules/.use: [{loader: "babel-loader"}}]Copy the code

Create a.babelrc file

{
  "presets": ["@babel/preset-env"."@babel/preset-react"]."plugins": ["@babel/plugin-transform-runtime"]}Copy the code

5. Introduce Polyfill as needed

SRC/index.js to globally introduce @babel/polyfill and write ES6 syntax, but this has a disadvantage:

  1. Introduced the global@babel/polyfillThis approach may import unwanted polyfills in the code, resulting in a larger packaging volume

Change. Babelrc to only translate what we use

npm install core-js@2 @babel/runtime-corejs2 -S
 
{
  "presets": ["@babel/preset-env",
              { "useBuiltIns": "usage" },
              "@babel/preset-react"]."plugins": ["@babel/plugin-transform-runtime"]} will comment out the code introducing the global// import '@babel/polyfill'
Copy the code

This is configured to import on demand. When you configure to import polyfills on demand, Babel automatically imports the relevant polyfills for functions above ES6, which greatly reduces the volume of packaging and compilation

5. Plugin CleanWebpackPlugin

After multiple packages, you will find that each package generates a bunch of files in the dist directory, but the last package is still there. We need to clean up the old files in the dist directory each time

cnpm install  clean-webpack-plugin -D
// Note this imported pit, the latest version needs to be imported this way instead of directly const CleanWebpackPlugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
 
plugins: [
    new CleanWebpackPlugin() 
]
Copy the code

6、使用插件 HtmlWebpackPlugin

After the previous step, the index.html is also cleared. Therefore, we will use the HtmlWebpackPlugin to generate HTML and automatically insert every js package into your index.html, and it can also create the final index.html based on one of your HTML templates, that is, you can specify the template

cnpm install html-webpack-plugin -D
/ / create the template. HTML
cd src
touch template.html
 
// The content is as follows
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta http-equiv=" x-UA-compatible "content="ie=edge"> <title>react-webpack4-cook</title> </head> <body> <div id="root"></div> </body> </ HTML > // webpack.config.js make changes const HtmlWebpackPlugin = require('html-webpack-plugin'); Plugins: [new CleanWebpackPlugin(), new HtmlWebpackPlugin({filename: 'index.html', // Finally create a filename template: Path.join (__dirname, 'SRC /template.html') // Specify template path})]Copy the code

7. Optimize DevTool using source-map

The devtool option in Webpack controls whether and how the Source map is generated. In short, the Source map is the file that helps us locate the error message. Correctly configuring source Map can improve development efficiency and locate errors faster.

Add the following sentence under the mode option in webpack.config.js:

devtool:"cheap-module-eval-source-map".// Development environment configuration
devtool:"cheap-module-source-map".// Generate configuration online
Copy the code

8. Use WebpackDevServer

Webpack-dev-server is a small static file server built locally, with real-time reloading capabilities, and provides web services for packaging generated resources

  devServer: {
    hot: true.contentBase: path.join(__dirname, "./dist"),
    host: "0.0.0.0".// Can be accessed using mobile phone
    port: 8080.historyApiFallback: true.// All 404's are linked to index.html
    proxy: {
      // Proxy to the backend service address, intercepts all request addresses starting with API
      "/api": "http://localhost:3000"}}Copy the code

9. Use HotModuleReplacement for HMR

After setting up the local server in the development environment, the page will refresh synchronously when the content is modified, and we will now enter the toCOunt page

  1. Click the button to add the number to a non-zero number, such as 6

  2. And then you can change the text of the button in your code, change anything, and you’ll see that when the page refreshes, the number goes back to zero

This is obviously not what we want, want, can keep the state of the page, or change the code, page or save the state of Numbers is 6, which is to realize local changes, first of all need to use: HotModuleReplacementPlugin plug-in

devServer: {
    hot: true
},
 
plugins: [
    new webpack.HotModuleReplacementPlugin()
],
Copy the code

Click the button to increase the number, then change the content, and find that there is still no saved state… What? What do I do

The @! That’s not all. Moving on, we also need the react-hot-loader plugin

10. React-hot-loader Records the react page retention state

Let’s continue with the above operation, divided into four steps

cnpm i react-hot-loader -D
 
// Write this in the main file
 
import React from "react";
import ReactDOM from "react-dom";
import { AppContainer } from "react-hot-loader"; ------------------- 1First introduce AppContainreimport { BrowserRouter } from "react-router-dom";
import Router from "./router";
 
/* Initialize */renderWithHotReload(Router); ------------------2 -Initialization,/* Hot update */
if (module.hot) {------------------- 3, hot update operationmodule.hot.accept("./router/index.js", () = > {const Router = require("./router/index.js").default;
    renderWithHotReload(Router);
  });
}
 
 
function renderWithHotReload(Router) {-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --4 -Reactdom.render (<AppContainer>
      <BrowserRouter>
        <Router />
      </BrowserRouter>
    </AppContainer>.document.getElementById("app")); }Copy the code

All right, now you try again

Compile CSS and SCSS

cnpm install css-loader style-loader sass-loader node-sass -D
 
{
  test: /\.scss$/.use: [
      "style-loader".// Create a style tag and add CSS to it
      "css-loader"./ / compile CSS
      "sass-loader" / / compile SCSS]}Copy the code

12. Integrate postCSS

Most of all, what is the use of this? Automatically adding prefixes, PostCSS-CssNext allows you to use future CSS features and do some compatibility processing

cnpm install  postcss-loader postcss-cssnext -D
 
{
    test: /\.scss$/.use: [
            "style-loader".// Create a style tag and add CSS to it
            "css-loader"./ / compile CSS
            "postcss-loader"."sass-loader" / / compile SCSS]}// 在刚才的home.scss 加上 transform: scale(2);If you look at the console, the prefix is automatically addedCopy the code

13. Manipulating images

CNPM I file-loader url-loader -d file-loader Resolves the problem of importing image paths in files such as CSS url-loader encodes images in BASE64 when they are small. If the value is greater than limit, use file-loader.test: /\.(png|jpg|jpeg|gif|svg)/.use: {
      loader: 'url-loader'.options: {
        outputPath: 'images/'.// Image output path
        limit: 10 * 1024}}}Copy the code

14. Work with fonts

{
        test: /\.(eot|woff2? |ttf|svg)$/.use: [{loader: 'url-loader'.options: {
              name: '[name]-[hash:5].min.[ext]'.limit: 5000.// fonts file size <= 5KB, use 'base64'; else, output svg file
              publicPath: 'fonts/'.outputPath: 'fonts/'}}}]Copy the code

Webpack optimization

1. Alias optimizes the file path

  1. Extension: You can specify extension without adding file extensions to require or import
  2. Alias: Configuring aliases speeds up WebPack’s search for modules
  resolve: {
    extension: ["".".js".".jsx"].alias: {
      "@": path.join(__dirname, "src"),
      pages: path.join(__dirname, "src/pages"),
      router: path.join(__dirname, "src/router")}},Copy the code

14, Use static resource path publicPath(CDN)

By deploying resources around the world, CDN enables users to access resources nearby and speeds up access. To access the CDN, you need to upload static resources of web pages to the CDN service. When accessing these resources, the URL provided by the CDN service is used.

output:{
 publicPatch: '// [CDN].com'.// Specify the CDN address for storing JS files
}
Copy the code

2. MiniCssExtractPlugin, extract CSS files

Without configuration, our CSS is packaged directly into JS, and we want to generate CSS files separately. Because CSS is generated separately, CSS and JS can be downloaded in parallel, improving page loading efficiency

cnpm install mini-css-extract-plugin -D
 
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 
 {
        test: /\.scss$/.use: [
          // "style-loader", // b no longer needs style-loader to have separate processing
          MiniCssExtractPlugin.loader,
          "css-loader"./ / compile CSS
          "postcss-loader"."sass-loader" / / compile SCSS]},plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css".chunkFilename: "[id].css"})]Copy the code

3. Code segmentation Load and extract common code as required

Why load on demand?

We can now see that, once packaged, all the pages only generate one bundle.js, which will be very slow when we load the first screen. Because he also downloaded the js of other pages, that is to say, before the execution is finished, the page is finished! Full! Empty! White!!! !!!! . If each page packs its own JS, it can load its own JS when entering the page, and the first screen load can be much faster

  optimization: {
    splitChunks: {
      chunks: "all".// All common chunks of the chunks code are separated into a single file}},Copy the code

5, file compression

Webpack4 automatically compresses the code whenever it is in production mode

mode:productioin
Copy the code

6. Expose global variables

You can use the $variable globally directly

 new webpack.ProvidePlugin({
      $: 'jquery'.// npm
      jQuery: 'jQuery' // Local Js file
    })
Copy the code

8. Specify the environment and define the environment variables

plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        VUEP_BASE_URL: JSON.stringify('http://localhost:9000')}}),]Copy the code

9, CSS Tree Shaking

npm i glob-all purify-css purifycss-webpack --save-dev
 
const PurifyCSS = require('purifycss-webpack')
const glob = require('glob-all')
plugins:[
    // Remove useless CSS
    new PurifyCSS({
      paths: glob.sync([
        // Make the path file for CSS Tree Shaking
        path.resolve(__dirname, './src/*.html'), // Note that we also need to tree shaking HTML files
        path.resolve(__dirname, './src/*.js')])})]Copy the code

10, JS Tree Shaking

Delete useless JS code from the code. Only import is supported. Commonjs is not supported

Develpoment tree shaking is invalid as long as mode is production. Developer tree shaking is invalid as webpack is for debugging

  optimization: {
    usedExports:true,}Copy the code

The DllPlugin plugin packages third-party class libraries

Projects introduced many third party libraries, the libraries in a long period of time, the basic will not update, when packaged separate packaging to improve packing speed, and dynamic link library plug-in DllPlugin, its principle is to make web page depend on the basis of abstract module packaging to the DLL file, when you need to import the exists in a DLL module, The module is no longer packaged, but retrieved from a DLL.

Install jquery and import it in the entry file. Create a new webpack.dll.config.js file

/* * @desc Static public resource packaging configuration */


const path = require('path')
const webpack = require('webpack')

const src = path.resolve(process.cwd(), 'src'); // Source directory
const evn = process.env.NODE_ENV == "production" ? "production" : "development";

module.exports = {
    mode: 'production'.entry: {
        // Define the entry file vendor.js that packages public files in the application
        jquery: ['jquery']},output: {
        path: path.resolve(__dirname, '.. '.'dll'),
        filename: '[name].dll.js'.library: '[name]_[hash]'.libraryTarget: 'this'
    },

    plugins: [
        new webpack.DllPlugin({
            // Define the entry file vendor.js that packages public files in the application
            context: process.cwd(),

            // Manifest.json file output location
            path: path.resolve(__dirname, '.. '.'dll/[name]-manifest.json'),

            // Define the function name exposed by the packaged public vendor file
            name: '[name]_[hash]'}})]Copy the code

Add it in package.json

        "build:dll": "webpack --config ./build/webpack.dll.config.js".Copy the code

run

npm run build:dll
Copy the code

You will find that there is a DLL folder with DLL. Js file in it, so we have packed our jquery and other files separately. How to use it next?

We need to install another dependency, NPM I add-asset-html-webpack-plugin, which will inject our packed DLL. js file into our generated index.html. Make changes in the webpack.base.config.js file.

 new AddAssetHtmlWebpackPlugin({
 filepath: path.resolve(__dirname, '.. /dll/jquery.dll.js'Corresponding DLL file path) / /}), new webpack. DllReferencePlugin ({manifest: path. Resolve (__dirname,'.. '.'dll/jquery-manifest.json')})Copy the code

. Ok, you can have new webpack DllReferencePlugin this plug-in commented, packaging, test under the open pack to try, I test results, comments 5689 money, annotation, 5302 ms, only sent 300 ms? Note that I only have one jquery package here as a demo, what if you pull out a lot of them? Wouldn’t that be horrible? If you’re a little confused, I recommend you go online and take a look at my code

12. Use happypack to execute tasks concurrently

Webpack running on Node. Has a single-threaded model, meaning that Webpack needs to process tasks one by one, not multiple tasks at a time. Happy Pack allows Webpack to do this by splitting the task into multiple sub-processes for concurrent execution, which then send the results to the main process.

cnpm i -D happypack
 
// webpack.config.js
 rules: [
     {
        // cnpm i babel-loader @babel/core @babel/preset-env -D
        test: /\.jsx? $/.exclude: /node_modules/.use: [{// Each loader corresponds to an ID
            loader: "happypack/loader? id=busongBabel"}}]]// Add to plugins
plugins:[
      new HappyPack({
      // Use a unique identifier id to indicate that the current HappyPack is used to process a specific class of files
      id:'busongBabel'.// How to handle.js files, which are used in the same way as in Loader configuration
      loaders:['babel-loader? cacheDirectory'].threadPool: HappyPackThreadPool,
  })
]
 
Copy the code

13. PWA optimization strategy

In short: the first time you visit a website, if successful, make a cache so that when the server dies, you can still access the page. This is PWA. I believe you already know that PWA processing is only needed in the production environment in case of accidents.

 cnpm i workbox-webpack-plugin -D

const WorkboxPlugin = require('workbox-webpack-plugin') // Introduce the PWA plugin
const prodConfig = {
  plugins: [
    / / configuration PWA
    new WorkboxPlugin.GenerateSW({
      clientsClaim: true.skipWaiting: true})]} add to the entry file// Check whether the browser supports serviceWorker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(registration= > {
        console.log('service-worker registed')
      })
      .catch(error= > {
        console.log('service-worker registed error')})})}Copy the code

After you’ve configured it, you can package it into the dist directory, start a static server in the dist directory, visit the home page, and then close the server. You’ll be surprised to find that the site is still accessible.

15. Merge extractionwebpackCommon configuration

To separate the development environment from the production environment and the WebPack configuration file, use webpack-merge, which merges the WebPack configurationCopy the code

16. Final separation of configuration files (finished)

Due to the limitation of time and space, basically end here. Above, whether it is mentioned not mentioned, or there are some details, github source code is basically all included, if there is a need to go to Github reference configuration files, follow a copy out, will get more results with half the effort

May there be no more Webpack configuration engineers in the world

Here, you might as well click a like, give a star, add a follow. React-webpack4-cook: react-webpack4-cook: react-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook: React-webpack4-cook If it is helpful to you, you might as well give a star