Why build?

The front end is growing fast.

More and more ideas and frameworks are coming out.

Is to improve the efficiency of development.

For example, ES6 needs to be converted from Babel to ES5 to run on the browser.

For example, SCSS needs to be converted to CSS to run in the browser

.

These ideas and frameworks are built upon.

Why choose WebPack for the build?

Webpack treats everything like a module!

Webpack can be extended by plugin!

The Webpack community is very large and active, keeping up with The Times! Ecological chain integrity, maintenance is also very high!

To understand why we want to use WebPack, let’s take a look back at how we used JavaScript on the Web before packaging tools came along. There are two ways to run JavaScript in a browser. The first way is to reference some script for each function. This solution is difficult to scale because loading too many scripts can cause network bottlenecks. The second way is to use a large.js file that contains all the project code, but this can cause problems with scope, file size, readability, and maintainability.

Why did you choose webpack: webpack.docschina.org/concepts/wh…

Webpack is first packaged

Webpack is a module packaging tool, but it can only package JS, so if you want to package other modules such as CSS and pictures, you also need to use Loader. For problems that Loader cannot solve, you also need to use plug-in plugin to expand functions.

The installation

yarn add webpack webpack-cli

configuration

  1. createwebpack.config.jsfile
  2. configurationwebpack.config.js
    const path = require('path')
    
    module.exports={
        entry: {
            main:'./src/index.js'
        },
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: "[name].js"}}Copy the code
  3. configurationpackge.json
    "script": {"start":"webpack"
    }
    Copy the code

Explain the configuration

In fact, webpack already has a lot of very rich stuff configured by default, like output, and if we don’t configure output, by default we output a dist folder with a file called bundle.js.

We can also configure ourselves:

  1. entry

    To configure the entry file, if there is only one entry file, we can enter :’./ SRC /index.js’.

    If you have multiple entry files, or you want to give your entry file a name: main is the name of the entry file.

    The nice thing about the name of this entry file is that it can be used in many later configurations: instead of manually typing main, we can simply use ‘[name]’ to lock onto the previously configured main.

    This can be realized, change a, can change all.

  2. output

    When you output path, it generates a folder containing a file named filename with the name ‘[name].js’. As mentioned above, this [name] corresponds to the entry file name!

loader

Webpack can only package and parse JS code, so we need to use loader to parse non-JS modules!

css

The installation

yarn add style-loader css-loader

configuration

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

Explain the configuration

Test :/\.css$/: When we encounter a.css file, we go to the following loader.

Cs-loader assembs CSS files together, and then style-loader converts the CSS code into a string and inserts it into our output file main.js.

scss

CSS is not enough for us, we need to use more powerful SCSS!

The installation

yarn add sass-loader style-loader css-loader node-sass

configuration

module.exports={
    module: {
        rules: [
            {
                test:/\.scss$/,
                loaders:['style-loader'.'css-loader'.'sass-loader']},]}}Copy the code

Explain the configuration

Sass-loader first compiles SCSS code into CSS code, csS-loader sets CSS files together, and then style-loader converts CSS code into strings and inserts them into our output file main.js.

image

The installation

yarn add file-loader url-loader

configuration

module.exports={
    module: {
        rules: [
           {
                test:/\.(jpg|png|jpeg)$/,
                use: {
                    loader: "url-loader",
                    options: {
                        outputPath:'images',
                        name:'[name].[ext]'.limit: 2048}}]}}Copy the code

Explain the configuration

When we encounter JPG, PNG, jpeg, we go to the following configuration!

If my image size is greater than limit: For 2048 2KB, I create an images folder in the dist directory, which contains the images I packed with file-loader, named ‘[name].[ext]’,[name] is the entry file NAME I configured, and.[ext] is our image suffix.

If my image size is less than 2KB, I use url-loader, which will convert the image to base64 and insert it into main.js.

Base64 conversion of small images does not make sense because the size of small images becomes larger after being base64 converted.

plugin

Plugin is used to extend the functionality of Webpack!

html-webpack-plugin

The installation

yarn add html-webpack-plugin

configuration

const HtmlPlugin = require('html-webpack-plugin')

plugins:[
  new HtmlPlugin({
         template: "index.html"})]Copy the code

Explain the configuration

Template: “index.html” uses the index.html file in our current directory as the template

Create an index.html file with the same template in the generated dist folder after packaging.

clean-webpack-plugin

The installation

yarn add clean-webpack-plugin

configuration

const {CleanWebpackPlugin} = require('clean-webpack-plugin')

plugins:[
 new CleanWebpackPlugin({})
]
Copy the code

Explain the configuration

Delete the dist folder before each packing!

watch

During development, every change to the code should be repackaged and previewed.

This is really troublesome! 😓

We expect that if I can change the code, it will rebuild itself and the page will refresh immediately!

Improve work efficiency and optimize development experience

The installation

There is no

configuration

"script": {"start":"webpack --watch"
}
Copy the code

Explain the configuration

Run NPM start, run index.html, and our file is listening!

After we have modified the code, in the browser, manually refresh the page again, we can see the latest changes!

devServer

Having to manually refresh the browser after every code change is too much trouble!

Also, the file system cannot make Ajax requests! This is a head-scratching question!

The installation

yarn add webpack-dev-server

configuration

devServer: {
        contentBase:'./dist',
        hot:true,
        hotOnly:true, 
        open:true.historyApiFallback: true,
        overlay:true,
        progress: true
    }
Copy the code
"script": {"start":"webpack-dev-server"
}
Copy the code

Explain the configuration

Webpack-dev-server can only be used in development environments!

The configuration in devServer can also be written in a different way :” start”:”webpack-dev-server –hot –open”

Of course, much of the configuration in devServer is built-in by default:

  1. contentBase:'./dist'In:distStart the server
  2. hot:trueOpen hot update mode! When you change the code, you no longer have to refresh the page manually, the browser will refresh it for you!
  3. hotOnly:true: even ifHMRDoes not take effect and the browser does not refresh automatically
  4. historyApiFallback: true: If our page is 404, it will goindex.htmlInstead of throwing an error page
  5. open:true: When we finish packing, the browser automatically opens and automatically loads ourindex.htmlpage
  6. overlay:trueIf something goes wrong in your code, display the error directly on the browser page!
  7. progress: true: Displays the process you packaged

Attention! Hot loading of CSS code does not work if it has been separated from main.js as a CSS file!

HMR

Although we have a particularly good webpack-dev-server –hot

But the hot function, every time the browser automatically refresh, is to load all resources! The equivalent of a page refresh!

However, we want, if: if we only modify the CSS file, then reload the CSS file!

Replace only the modules we update!

Hot Module Replacement = HMR

The installation

There is no

configuration

const webpack = require('webpack')

plugins: [
   new webpack.HotModuleReplacementPlugin()
]
Copy the code

Insert another one into our index.js entry file:

if (module.hot) {
    module.hot.accept();
}
Copy the code

Explain the configuration

  1. HotModuleReplacementPluginHotmodule updates can be implemented. When we update the code, the browser Network loads the JS and JSON files we generated for hot.update. Instead of reloading all the previous resources again!
  2. We have to accept in some filemodule.hot.accept()If no file is accepted, the hot module replacement file will not be generated.
  3. Why doesn’t our CSS need to be writtenmodule.hot.accept()Because,css-loaderThis operation has been done for us
  4. We can do it atmodule.hotListen for which file has been modified and then do what you want:
    if (module.hot) {
        console.log('Modified... ')
        module.hot.accept('@components/child', () => {
            ReactDom.render( <App/>,document.getElementById('root'))}); }Copy the code

    Like I’m listening in'@components/child'The file has been modified, so I’ll just re-render the page!

jsx

The installation

yarn add babel-loader

configuration

 {
    test:/\.(js|jsx)$/,
    exclude:/node_modules/,
    loader: 'babel-loader'
}
Copy the code

Explain the configuration

Excluding files in node_modules that end in JS and JSX, we’ll use babel-loader to convert ES6 code to ES5 code!

tsx

The installation

yarn add awesome-typescript-loader

configuration

{
    test:/\.(ts)x$/,
    exclude:/node_modules/,
    loader: "awesome-typescript-loader"
}
Copy the code

Explain the configuration

Excluding ts and TSX files in the node_modules folder, we’ll use awesome-typescript-loader to convert ts code into compilable JS code

react

The installation

yarn add react react-dom @babel/preset-env @babel/preset-react

configuration

Create the.babelrc file

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

Explain the configuration

React….

Resolve facilitates the introduction of code

The installation

There is no

configuration

resolve: {
        extensions: ['.js'.'.jsx'.'.tsx'],
        mainFiles: ['index'].alias: {
            '@components':path.resolve(__dirname, 'src/components'),
            '@pages': path.resolve(__dirname, 'src/pages'),
            '@assets': path.resolve(__dirname, 'src/assets')}}Copy the code

Explain the configuration

  1. extensions: ['.js','.jsx','.tsx']: js, JSX, TSX file end, we do not need to write suffix when we import!
  2. mainFiles: ['index']: If the file is calledindex“, then we can not write filename, directly import the folder name of the previous level can be used
  3. alias: When we do import, if we change the path of the file, then the imported path also needs to be changed. Changing the path is very troublesome, so we usealias. If the introduction path issrc/componentsWe can use it directly@componentsInstead of!

Dynamic chunk

The installation

configuration

 output: {
        path: path.resolve(__dirname, 'dist'),
        filename: "[name].js",
        chunkFilename: "[name].chunk.js"
}
Copy the code

Explain the configuration

ChunkFilename: “[name].chunk.js” This chunkFilename will take effect when you encounter dynamically imported modules!

How to introduce dynamically?

There are two kinds of dynamic import methods, one is self-written, and the other is built-in.

  1. His writing
    const getAsyncComponent =(load)=>{
        return class AsyncComponent extends React.Component{
            componentDidMount() {
                load().then(({default: Component})=>{
                    this.setState({
                        Component
                    })
                })
            }
            render() {
                const {Component} = this.state || {}
                returnComponent ? <Component {... this.props}/> : <div>loading... </div> } } } const asyncUser = getAsyncComponent(()=>import(/* webpackChunkName:'page-user'* /'@pages/user'))
    Copy the code
  2. Suspense Lazy comes with React
    lazy(()=>import(/* webpackChunkName:'page-user'* /'@pages/user'))
    Copy the code
  3. All the code
    import React, { Suspense, Component, lazy } from 'react'
    import  ReactDom from 'react-dom'
    import './index.scss'
    import { Route, BrowserRouter, Switch } from 'react-router-dom'
    import Home from '@pages/home';
    
    // import {User} from "@pages/user";
    // import {About} from "@pages/about"; // If you do not unregister the synchronized import, chunk cannot be dynamically generated... // const asyncUserComponent = ()=>import(/* webpackChunkName:'page-user'* /'@pages/user').then(({default: component})=> component())
    
    const getAsyncComponent =(load)=>{
        return class AsyncComponent extends React.Component{
            componentDidMount() {
                load().then(({default: Component})=>{
                    this.setState({
                        Component
                    })
                })
            }
            render() {
                const {Component} = this.state || {}
                returnComponent ? <Component {... this.props}/> : <div>loading... </div> } } } const asyncUser = getAsyncComponent(()=>import(/* webpackChunkName:'page-user'* /'@pages/user'))
    const asyncAbout = getAsyncComponent(()=>import(/* webpackChunkName:'page-about'* /'@pages/about'))
    
    class App extends React.Component{
        render() {return( <Suspense fallback={<div>loading... </div>}> <BrowserRouter> <Switch> <Route exact path='/' component={Home}/>
                                <Route path='/user' component={lazy(()=>import(/* webpackChunkName:'page-user'* /'@pages/user'))}/>
                                <Route path='/about' component={asyncAbout}/>
                        </Switch>
                    </BrowserRouter>
                </Suspense>
    
            )
        }
    }
    
    ReactDom.render(<App/>,document.getElementById('root'))
    
    Copy the code
  4. explain
    ()=>import(/* webpackChunkName:'page-user'* /'@pages/user')
    Copy the code

    webpackChunkNameThat’s the name of chunk, and at the end of that chunk is a file calledpage-user.chunk.js

Static the chunk

Static chunks are chunks that are imported in the traditional import way

For example: import React from ‘React’

The installation

There is no

configuration

  optimization: {
        usedExports: true,
        splitChunks: {
            chunks: "all",
            cacheGroups: {
                vendors:{
                    test:/node_modules/,
                    priority:-10,
                },
                ui:{
                    test:/src\/components/,
                    minSize:0,
                    reuseExistingChunk: true,
                    priority:-20
                }
            }
        }
    }
Copy the code

Explain the configuration

If the import module belongs to the node_modules directory, it is then tucked under the vendors module, and the vendors ~ main.chunk.js is the packaged file name

If the import module belongs to the SRC /components directory, it is inserted into the UI module, and the packaged file name is called: UI ~ main.chunk.js

Compression js

This feature is usually used in production mode

The installation

yarn add terser-webpack-plugin

configuration

const TerserJSPlugin = require("terser-webpack-plugin");

 optimization:{
        minimizer: [
            new TerserJSPlugin({})
        ]
    },
Copy the code

Explain the configuration

Compressed JS code

CSS separate files

The installation

yarn add mini-css-extract-plugin

configuration

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

   module: {
        rules: [
            {
                test:/\.scss$/,
                loaders:[MiniCssExtractPlugin.loader,'css-loader'.'sass-loader'] {},test:/\.css$/,
                loaders:[MiniCssExtractPlugin.loader,'css-loader']
            },
        ]
    }
    
    plugins: [
        new MiniCssExtractPlugin({
            filename: "[name].css",
            chunkFilename: "[id].css"})].Copy the code

Explain the configuration

Separate the CSS code from the main.js file into a separate CSS file.

Compress CSS

Generally used in production mode.

The installation

yarn add optimize-css-assets-webpack-plugin

configuration

  optimization:{
        minimizer: [
            new OptimizeCSSAssetsPlugin({})
        ]
    }
Copy the code

Explain the configuration

CSS code is compressed

DllPlugin

We only hope that the third party modules will be analyzed when they are first packaged and will not be analyzed in the future.

Speed up packing!

The installation

yarm add add-asset-html-webpack-plugin

configuration

  1. createwebpack.dll.js
const {DllPlugin} = require('webpack')
const path = require('path')

module.exports={
    mode:'production',
    entry:{
        react:['react'.'react-dom'],
        time:['timeago.js']
    },
    output:{
        filename: "[name].dll.js",
        path: path.resolve(__dirname, 'dll'),
        library: '[name]'
    },
    plugins:[
        new DllPlugin({
            name:'[name]',
            path: path.resolve(__dirname, './dll/[name].manifest.json')]}})Copy the code
  1. "dll": "webpack --config webpack.dll.js"

  2. Configuration webpack. Config. Js:

const fs = require('fs')
const {DllReferencePlugin} = require('webpack')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')

const dllPlugins = ()=>{
    const plugins = []
    const files = fs.readdirSync(path.resolve(__dirname, './dll'))
    files.forEach(file => {
        if (/.*\.dll.js/.test(file)){
            plugins.push(new AddAssetHtmlPlugin({
                filepath: path.resolve(__dirname, './dll',file)
            }))
        }
        if (/.*\.manifest.json/.test(file)){
            plugins.push(new DllReferencePlugin({
                manifest:path.resolve(__dirname, './dll', file)
            }))
        }
    })
    return plugins;
}

 plugins: [
    ...dllPlugins()
 ]
Copy the code

Explain the configuration

1. Pay attention to

Run yarn Run DLL first. In this way, webpack.dl. js is parsed and DLL folder and DLL files are generated.

Resolve (path.resolve(__dirname, ‘./ DLL ‘))) if you can’t find the folder file, then run yarn start.

2.DllReferencePlugin :

This means that when we were packing, we found a third party module that we would have looked for again and again from node_modules

Now, we will start with the DLL/vendors manifest. Json inside looking for mapping relationship

If the third party module is in the mapping, and we know that the third party module is in vendor.dll. Js,

Then it will be taken from the global variable, because the third party module generated the global variable when it was first packaged

You don’t have to analyze node_modules bit by bit, which speeds up packing

3.AddAssetHtmlPlugin:

Finally, we package the generated *.dll. Js file and insert it into our index. HTML as a static file

The separation of the environment

The development environment is different from the production environment. Some things work in the development environment, but not necessarily in the production environment, such as devServer, but some code is common, such as CSS-Loader.

Development environments also focus on different things than production environments. The development environment pays more attention to the efficiency and convenience of writing code. Production environments are more focused on bag size and portability.

So, do different configurations for different environments!

The installation

yarn add webpack-merge

configuration

Production environment :(similar to development environment)

const merge = require('webpack-merge')
const baseConfig = require('./webpack.base') const prodConfig = {... } module.exports=merge(baseConfig, prodConfig)Copy the code

Explain the configuration

In different environment, according to different emphasis, do different things!

The last

Continue to learn