Learning notes, introductory level, understand the basic configuration of Webpack, single-page CSS \js\ HTML \ image resources involved in loader, plugins configuration. This section practices code. There will be continuous updates in the future. The goal is to systematically learn Webpack and achieve independent handwritten configuration of small and medium-sized projects.

Webpack installation

Environmental preparation:

  1. Install Node. The new version of Node comes with NPM
  2. Create a new project folder (e.g. Demo)
  3. performnpm initNPM init generates a pakeage.json file that records project information.

Install webpack

  1. npm install webpack –save-dev
  2. npm install webpack-cli –save-dev
  3. In the package. The json
  "scripts": {
    "webpack": "webpack"
  },
Copy the code
  1. NPM run webpack -v, see the version number, that is, success

In WebPack 3, WebPack itself and its CLI used to be in the same package, but in version 4, they have separated the two to better manage them, so both packages need to be installed at the same time.

In this example, there is no global installation (–save-dev is replaced with -g), so webpack -v cannot be executed. Instead, an “NPM script” is used, which can also be implemented by configuring the computer’s path.

Webpack it based on the entry file, find the relevant dependencies, packaged into a file, packaged JS files can be directly run in the browser

Webpack can be packaged with 0 configuration

  1. Create folder SRC and create index.js
  2. npm run webpackSRC /index.js in the same directory is packaged by default

Webpack zero configuration has low flexibility, so let’s go to manual configuration

Manual configuration, webpack.config.js

The default configuration that webpack reads is webpack.config.js.

  1. npm run webpack –config webpack.config.my.js

  2. Package. json Configuration script scripts

{
    "scripts": {
        "dev": "webpack --config webpack.config.js"},... }Copy the code

Basic configuration – Package JS

The packaged file is parsed roughly: an object with the module path as the key and the file contents as the value is passed to a self-executing __webpack_require__ function

/ * * * * * * /function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		returnmodule.exports; / * * * * * *}Copy the code
// Webpack is written by nodelet path = require('path'Module. Exports = {mode: node exports = {mode: node exports = {mode: node exports'production'// Production mode development mode entry:'./src/index.js'Output: {filename:'bundle.js'// filename:'bundle.[hash:8].js', / /hash:8 displays only 8 bits. If the file does not change,hashThe same path: the path. The resolve (__dirname,'dist'// __dirname uses the current path as the primary directory}}Copy the code

When mode=production, the js package is compressed

Local service

Currently we can only manually run files to the browser,webpack-dev-server can automatically access our project in the browser by loaclhost

Lead to

  1. Create a new index.html file under SRC
  
      
  <html lang="en">
  <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
      <title>Document</title>
  </head>
  <body>
  </body>
  </html>
Copy the code

Configuration, added to webpack-config.js

DevServer: {port: 3000, // Default port: 8080 progress:true// You can see the service progress contentBase:'./dist'// Default access to the root directory},Copy the code

Webpack – dev – server installation

npm install webpack-dev-server --save-dev
Copy the code

The service

npx webpack-dev-server
Copy the code

Dist: : localhost:3000 is the default access to our project dist, but we do not put SRC index.html nor dist, so we need another plugin html-webpack-plugin

If the installation fails and a JSON error is reported, you can use NPM cache clean –force

HTML pluginhtml-webpack-plugin

What we want is to be able to automatically create HTML in dist and package it into memory, i.e. insert the packed JS file into SRC /index.html and generate the result in dist/index.html

HTML – webpack – the plugin installation

npm install html-webpack-plugin --save-dev
Copy the code

configuration

Add to webpack.config.js

    // webpack.config.js
    let HtmlWebpackPlugin = require('html-webpack-plugin')
    plugins:[
        new HtmlWebpackPlugin({
            template: "./src/index.html"// Package template entry address filename:"index.html"// Get the file entry name after packinghash: true,
            minify: {
                removeAttributeQuotes:true// collapseWhitespace:true}})], // Delete space to make a line}})]Copy the code

Packaging HTML

In package. Jon scripts, add “build:”:”webpack”

npm run build
Copy the code

As a result, index.html is generated in the dist directory

css – loader

Loader enables WebPack to handle different files. Loader can convert all types of files into valid modules that WebPack can handle. Loader doesn’t just deal with CSS. Loader is executed from bottom to top and from left to right

Lodaer has two purposes

  • Identify the files that should be converted by the corresponding loader. (Use the test attribute)
  • Transform these files so that they can be added to the dependency diagram (and eventually to the bundle). (Use attribute)

Documents to prepare

  • New SRC/a.c ss
  • New SRC/index. The CSS
@import './a.css';
body {
    color: red;
}
Copy the code
  • In the index. Js addedrequire('./index.css')At this point, your service will report an error saying “Need loader to handle this type of file”.

Loader configuration

  • Style – loader: handle the import
  • Css-loader: When running a service, the CSS is directly inserted into the HEAD of the HTML. Packaging does not take effect
  • Postcss-loader autoprefixer: adds the prefix of compatible browsers to the CSS

Installation:

npm install style-loader css-loader postcss-loader autoprefixer --save-dev
Copy the code
module: {
    rules: [
            {
                test: /\.css$/,
                use: [
                    {loader: "style-loader"}, // When running the service, insert the CSS directly into the HEAD of the HTML, packaging will not take effect {loader:"css-loader"}, // handle import {loader:"postcss-loader"},]}]}Copy the code

In addition to adding loader to module,

  • Create postcss.config.js in the root directory
// postcss.config.js
module.exports = {
    plugins:[
        require("autoprefixer")]}Copy the code

You will find

  • The style was successfully inserted into the local service
  • But say goodCSS adds the prefix of compatible browserDoes not work, need to change modeprodutiion
  • After packing, there is no CSS in dist, that is because style-loader does not have this processing capability, so it needsmini-css-extract-plugin

CSS generates an external file plug-in mini-CSS-extract-plugin

Can replace style-loader directly

The installation

npm install mini-css-extract-plugin --save-dev
Copy the code

Configuration, instead of style.loader

// webpack.config.js
let MiniCssExtractPlugin = require('mini-css-extract-plugin')
plugins:[
    ...
    new MiniCssExtractPlugin({
        filename: 'main.css'})] module: {rules:[{test:/\.css/,
            use:[
                MiniCssExtractPlugin.loader,
                'css-loader'// parse @import]}... ] }Copy the code

Package + re-brush service, you will see

  • CSS exchaining inserts into HTML
  • After packaging, index.css is generated
  • But if you open main.css, you’ll see that there’s no compression, and that’s what you needoptimize-css-assets-webpack-plugin

Compressing CSS Filesoptimize-css-assets-webpack-plugin

The installation

npm install optimize-css-assets-webpack-plugin --save-dev
Copy the code

configuration

let OptimizeCss = require('optimize-css-assets-webpack-plugin')
...
optimization: {
    minimizer: [
        new OptimizeCSS()
    ]
}
Copy the code

Optimize - CSS-assets-webpack-plugin will disable js compression by default and require manual plugin compression

Compression jsuglifyjs-webpack-plugin

  • The installation
cnpm install uglifyjs-webpack-plugin --save-dev
Copy the code
  • configuration
// webpack-config.js
let UglifyWebpackPlugin = require('uglifyjs-webpack-plugin')... optimization: { minimizer: [ ... new UglufyjsWebapckPlugin({ cache:true.sourceMap: true,
            parallel: true}})]Copy the code

Es6 syntax conversion

  • Babel – loader: turn es5 es6
  • @babel/preset-env: Use the latest JS, pack the smallest JS, and preset browser can be specified
  • @babel/plugin-proposal-decorators: @log syntax
  • Babel /plugin-proposal-class-properties: ES7 syntax Advanced syntax
  • Babel /plugin-transform-runtime: Unpack public methods
  • Babel/Runtime: Production environment required

Document preparation:

// Add code @ to jslog
class A {
    a = 1
}

let a = new A()
console.log(a.a)

function log(target) {console.log(target, 23)} var testprofill = [3,3,5] console.log(testprofill.includes(4))Copy the code

The installation

npm install babel-loader @babel/core --save-dev
Copy the code
npm install @babel/preset-env @babel/plugin-proposal-decorators --save-dev
Copy the code
npm install @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime --save-dev
Copy the code
module: {
    rules: [
        {
            test: /\.js$/,
            use: [{
                loader: 'babel-loader', // es6 to es5 options: {presets: ['@babel/preset-env'], // Use the latest js, package the smallest JS, and can specify the browser // plugins: ['@babel/plugin-proposal-class-properties', {"loose":true},
                    //     '@babel/plugin-transform-runtime'// plugins: [['@babel/plugin-proposal-decorators', { 'legacy': true/ / @}]logGrammar ['@babel/plugin-proposal-class-properties', { "loose" : true}], // es7 syntax ['@babel/plugin-transform-runtime'],// Remove the package public method'/ / /'@babel/runtime'] / / production environment need to]}}], include: path. The resolve (__dirname, 'src// exclude: /node-modules/}]}Copy the code

For js processing, see Babel

Global variable introduction problem

Such as jquery, installation

npm install jquery --save-dev
Copy the code

There are three ways to introduce modules:

1. Expose the expose-loader to Windows

  • The installation
npm install expose-loader --save-dev
Copy the code
  • configuration
// webpack.config.js
module: {
    rules:[
        {
            test: require.resolve('juery'),
            use: [
                {
                    loader: 'expose-loader', // es6 to es5 options: {option:'$'}}] or use:'expose-loader? $'
        }
    ]
}

// index.js
import $ from 'jquery'
console.log($)
consle.log(window.$)
Copy the code
  1. ProviderPlugin provides each person with a $configuration
// webpack.config.js // introduces the webpack modulelet webpack = require('webpack')... plugins:[ new webpack.ProviderPlugin({ $:'jquery'.'jquery': 'jquery'})]Copy the code
  1. The script tag is imported without packaging the externals configuration
// webpack.config.js
externals: {
    jquery: "$"} // The page is manually moved to js <script SRC ="XXX"></script> // js import $from jqueryCopy the code

Image packaging

  • Add a new image in SRC, 1.jpg
  • Create an image in JS to introduce
// index.js
let image = new Img();
image.src = './1.jpp'
document.body.appendChild(image)
Copy the code

If the service is running, an error is also reported. In this case, file-loader is required

File-loader can handle images imported from JS/CSS

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

configuration

// webpack.config.js
{
    test: /\.(png|jpe? g|gif)$/i, use: { loader:'file-loader',}},Copy the code
  • Introduce images in CSS
// index.css
body {
    color: red;
    background: url("./1.jpg") no-repeat;
}
Copy the code

HTML – withimg – loaser installation

npm install html-withimg-loader --save-dev
Copy the code

configuration

// webapck.config.js
{
    test:/\.html$/,
    use: [
        {
            loader: 'html-withimg-loader'}}]Copy the code

Repackage and you’ll notice that the HTML image is still not displayed, but SRC is still changed./1.jpg is an object with default.

<img src='{"default":"5aae522a0485ba2405faad74163971a5.jpg"}' />
Copy the code

Just add esMoudle:true to the configuration of the Lie-loader

Install the url – loader

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

configuration

Url-loader Is compressed into base64 based on the limit specified in options. If the value exceeds the limit, use file-loader to comment out file-loader

// add {to module.exports-module-rules}test: /\.(png|jpe? g|gif)$/i, use: { loader:'url-loader',
        options: {
            esModule: false.limit: 1// 200kb
        }
    }
},
Copy the code

Currently, the packaged files are placed directly under SRC. If you want CSS /image, put it in a separate folder

File sorting and packing

Image: Add outputPath to urL-loader configuration

/ / in {test: /\.(png|jpe? g|gif)$/i, use: { loader:'url-loader',
            options: {
                outputPath: '/img/'Dist /img... }}},Copy the code

CSS: Add the filename before filename of the mini-CSs-extract-plugin

new MiniCssExtractPlugin({
    filename: 'css/main.css', // CSS extract to filename})Copy the code

The result of packing


Finally, really finally

Add an ESLint validation, which is a bit important for code specification, team collaboration, and not having to artificially follow the specification

eslint-loader

The installation

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

configuration

We said the loader executes from bottom to top, but what if I write it first, but it needs the first checkout run

Enforce: ‘Pre’ says it’s time to show strength

{
    test: /\.js$/,
    use: {
        loader: "eslint-loader",
        options: {
            enforce: 'pre'}}, include: path.resolve(__dirname,'src'),
    exclude: /node_modules/
},
Copy the code

That’s not all. You need to create a.eslintrc.json file in the project root directory. Note the “.” in front of the file name.

// .eslintrc.json
{
    "parserOptions": {
        "ecmaVersion": 5,
        "sourceType": "script"."ecmaFeatures": {}},"rules": {
        "constructor-super": 2."for-direction": 2,},"env": {}}Copy the code

Or you might ask, how do the rules know how to write them?

You can go to esLint, check the configuration you want, and download.eslintrc.json. You still need to know the rules for the validation items.

Multiple entry packing

JS multi-entry packaging

Documents to prepare

  • SRC create index.js and other.js. The content is optional.
  • New webpack. Config. Js
let path = require('path')
module.exports = {
    mode: "development"// multientry single entry is the string './ SRC /index.js' entry: {home:'./src/index.js', 
        other: './src/other.js' 
    },
    output: {
        filename: 'bundle.js'Path: path.resolve(__dirname,'dist')}}Copy the code

packaging

Change filename configuration of output to [name].js, after packaging, there will be the corresponding JS

let path = require('path')
module.exports = {
    mode: "development"// multientry single entry is the string './ SRC /index.js' entry: {home:'./src/index.js', 
        other: './src/other.js' 
    },
    output: {
        filename: '[name].js'Path: path.resolve(__dirname,'dist')}}Copy the code

HTML multi-entry packaging

Documents to prepare

  • SRC create index. HTML and other. HTML
// index.html, other.html <! DOCTYPE html> <html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    hello world! webpack 12
</body>
</html>
Copy the code

configuration

Here we are wondering, can we introduce the HTML-webpack-plugin twice

let path = require('path')
let htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    ...
    plugin: [
        new htmlWebpackPlugin({
            template: './src/index.html',
            filename: 'index.html'
        }),
        new htmlWebpackPlugin({
            template: './src/other.html',
            filename: 'other.html']}}),Copy the code

packaging

chunks

. module.exports = { ... plugins: [ new htmlWebpackPlugin({ template:'./src/index.html',
            filename: 'home.html',
            chunks: ['home'}), new htmlWebpackPlugin({template:'./src/other.html',
            filename: 'other.html',
            chunks: ['other'.'home'] // Configure the introduction of multiple js}),]}Copy the code

To configure the source – the map

When the code in the development version is not exactly the same as the code running online, the Source Map technology can be used to facilitate debugging and locate the problem.

The type of devtool value

1) source-map: source code map, which will generate a separate sourcemap file, localization can be fixed to rows and columns, the code is the same as the source code.

4) Cheap -module-eval-source-map: no separate file is generated, positioning can only locate lines.

Documents to prepare

Delete multiple entries and run single pages

configuration

let path = require('path')
let htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    mode: "production"// SRC /index.js' entry: {index:'./src/index.js', 
    },
    output: {
        filename: '[name].js'Path: path.resolve(__dirname,'dist')
    },
    devtool:'source-map'Plugins: [new htmlWebpackPlugin({template:'./src/index.html',
            filename: 'index.html',
            chunks: ['index']
        }),
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'}}}]}}Copy the code

If source-map is not added, the code in the browser is packaged

The use of the watch

module.exports = {
    watch: true, // Monitor changes watchOptions: {poll: 1000, // Monitor several times per second aggregateTimeout: Ignored :/node_modules/ / don't monitor files},}Copy the code

The application of the Webpack widget

  • CleanWebpackPlugin files in the output.path path will be deleted, 4.0+ default, no need to write path details
  • CopyWebpackPlugin copies files/folders to the specified folder
  • The bannerPlugin webpack internal plug-in does not need to be reinstalled, and basically packs a comment into all the packaged files

The installation

npm install copy-webpack-plugin clean-webpack-plugin --save-dev
Copy the code

configuration

const { CleanWebpackPlugin }  = require('clean-webpack-plugin')
let copyWebpackPlugin = require('copy-webpack-plugin')
let webpack = require('webpack'Module.exports = {plugins:[... new CleanWebpackPlugin(), // Default to clean output.path new copyWebpackPlugin({patterns: [ {from:'doc', to: '/'}
            ]
        }),
        new webpack.BannerPlugin('2020') // All packaged files will be preceded by a 2020 comment]Copy the code

Webpack cross domain issues

1) Rewrite the way to proxy the request to express server, such as localhost:8080 request localhost:3000 interface

Documents to prepare

The new server. Js

let express = require('express'Var app = new express() //'/user', (req, res) => {
    res.json({'name': 'hui lin4545? '})
})
app.listen(3000)
Copy the code
// index.js var XML = new XMLHttpRequest()'GET'.'/api/user'.true)
xml.onload = function() {
    console.log(xml.response)
}
xml.send()
Copy the code

In this case, the cross-domain situation will occur, and the data cannot be requested, so the proxy needs to be configured

configuration

module.exports = {
    devserver: {
        proxy: {
            '/api': {
                target: 'http://localhost:3000',
                pathRewrite: {'/api': ' '} // Request at the beginning of the unified control API}}}}Copy the code

You can see that the page can get data across domains

// server.js
app.get('/api/user', (req, res)=>{
    res.json({name: 'hui lin4545'})
})

// index.js
xhr.open('GET'.'/api/user'.true)

// webpack.config.js
devServer: {
    proxy: {
        '/api': 'http://localhost:3000/'// the interface at the beginning of the API, all request http://localhost:3000/}},Copy the code

2) Pure simulation data, directly in the configuration of webpack simulation interface

configuration

devServer: {
    before(app) {
        app.get('/api/user',() = >(req, res){
            res.json({name: 'hui lin-before'}}})})Copy the code

3) Directly start Webpack on the server side, there will be no cross-domain problems

The installation

npm install webpack-dev-middleware --save-dev
Copy the code
let express = require('express')

let app = new express()
let webpack = require('webpack')
let webpackDevMiddleWare = require('webpack-dev-middleware')
let config = require('./webpack.config.js')

let compiler = webpack(config)

app.use(webpackDevMiddleWare(compiler))

app.get('/api/user', (req, res)=>{
    res.json({name: 'hui lin4545'})
})

app.listen(3000)
Copy the code

resolve

Parse the third-party package Common

If we directly import ‘boostrap’, with no suffix, it directly to find, in the node_modules node_modules/boostrap/package. The json/main values

  • If we just want to introduce alone boostrap. CSS import ‘bootstrap/dist/CSS/bootstrap CSS’, will be add to the alias
module.exports = {
    resolve: {
        modules: [path.resolve('node_modulse')].alias: {// Add alias boostrap:'bootstrap/dist/css/bootstrap.css'}, mainFields:'style, main'Json main mainFiles: [], // specify the import file, default is index.js extensions: ['.js'.'.css'.'.json'}}}}}}}}}}}Copy the code

Defining environment variables

Webpack.defineplugin differentiates the current environment in your code

module.exports = {
    plugins: [
        new webpack.DefinePlugin({
            DEV: '"production"'
            DEV2: JSON.stringify('production'), // string FLAG:'true', // Boolean type EXT:'1 + 1'// expression})]} // index.jsif (DEV) console.log(DEV)
Copy the code

Distinguish between different environments

Merge files using webpack-merge

The installation

npm install webpack-merge --save-dev
Copy the code

configuration

// webpack.prod.js
let {smart} = require('webpack-merge')

let base = require('webpack.base.js')

module.exports = smart(base, {
    mode: 'production'
})
Copy the code

Put the common ones in webpack.base.js, and you can configure different config files depending on your development and production environments