webpack4 總結(jié)筆記

一、webpack簡(jiǎn)介

1.1 webpack是什么

     webpack 是一種前端資源構(gòu)建工具,一個(gè)靜態(tài)模塊打包器(module bundler)。 

     在 webpack 看來(lái), 前端的所有資源文件(js/json/css/img/less/...)都會(huì)作為模塊處理。 

     它將根據(jù)模塊的依賴關(guān)系進(jìn)行靜態(tài)分析,打包生成對(duì)應(yīng)的靜態(tài)資源(bundle)。

1.2 webpack五個(gè)核心概念

1.2.1 Entry

        入口(Entry)指示 webpack 以哪個(gè)文件為入口起點(diǎn)開(kāi)始打包,分析構(gòu)建內(nèi)部依賴圖。

1.2.2 Output

        輸出(Output)指示 webpack 打包后的資源 bundles 輸出到哪里去,以及如何命名。

1.2.3 Loader

        Loader 讓 webpack 能 夠 去 處 理 那 些 非 JavaScript 文 件 (webpack 自 身 只 理 解 JavaScript) 

1.2.4 Plugins

        插件(Plugins)可以用于執(zhí)行范圍更廣的任務(wù)。插件的范圍包括,從打包優(yōu)化和壓縮, 一直到重新定義環(huán)境中的變量等

1.2.5 Mode

        模式(Mode)指示 webpack 使用相應(yīng)模式的配置。

選項(xiàng) 描述 特點(diǎn)
development 會(huì)將 DefinePlugin 中 process.env.NODE_ENV 的值設(shè)置 為 development。啟用 NamedChunksPlugin 和NamedModulesPlugin。 能讓代碼本地調(diào)試 運(yùn)行的環(huán)境
production 會(huì)將 DefinePlugin 中 process.env.NODE_ENV 的值設(shè)置 為 production。啟用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。 能讓代碼優(yōu)化上線 運(yùn)行的環(huán)境

二、webpack的初體驗(yàn)

2.1 初始化配置

1.初始化 package.json npm init -y

2.下載安裝webpack:(webpack4以上的版本需要全局/本地都安裝webpack-cli)

全局安裝:cnpm i webpack webpack-cli -g

本地安裝:cnpm i webpack webpack-cli -D

2.2 編譯打包應(yīng)用

  1. 創(chuàng)建文件
  2. 運(yùn)行指令
    開(kāi)發(fā)環(huán)境指令:webpack --entry src/js/index.js -o build/js/built.js --mode=development
    功能:webpack 能夠編譯打包 js 和 json 文件,并且能將 es6 的模塊化語(yǔ)法轉(zhuǎn)換成 瀏覽器能識(shí)別的語(yǔ)法。
    生產(chǎn)環(huán)境指令:webpack --entry src/js/index.js -o build/js/built.js --mode=production
    功能:在開(kāi)發(fā)配置功能上多一個(gè)功能,壓縮代碼。
  3. 結(jié)論
    webpack 能夠編譯打包 js 和 json 文件。
    能將 es6 的模塊化語(yǔ)法轉(zhuǎn)換成瀏覽器能識(shí)別的語(yǔ)法。
    能壓縮代碼。
  4. 問(wèn)題
    不能編譯打包 css、img 等文件。
    不能將 js 的 es6 基本語(yǔ)法轉(zhuǎn)化為 es5 以下語(yǔ)法。

三、webpack開(kāi)發(fā)環(huán)境的基本配置

3.1 webpack.config.js

  • webpack的配置文件
  • 作用: 指示 webpack 干哪些活(當(dāng)你運(yùn)行 webpack 指令時(shí),會(huì)加載里面的配置)
  • 所有構(gòu)建工具都是基于nodejs平臺(tái)運(yùn)行的~模塊化默認(rèn)采用commonjs。
  • src文件夾里面的代碼是基于ES6模塊化的

3.2 開(kāi)發(fā)環(huán)境基礎(chǔ)配置

3.2.1 打包樣式資源

// resolve用來(lái)拼接絕對(duì)路徑的方法
const { resolve } = require('path');

module.exports = {
  // webpack配置
  // 入口起點(diǎn)
  entry: './src/index.js',
  // 輸出
  output: {
    // 輸出文件名
    filename: 'built.js',
    // 輸出路徑
    // __dirname nodejs的變量,代表當(dāng)前文件的目錄絕對(duì)路徑
    path: resolve(__dirname, 'build')
  },
  // loader的配置
  module: {
    rules: [
      // 詳細(xì)loader配置
      // 不同文件必須配置不同loader處理
      {
        // 匹配哪些文件
        test: /\.css$/,
        // 使用哪些loader進(jìn)行處理
        use: [
          // use數(shù)組中l(wèi)oader執(zhí)行順序:從右到左,從下到上 依次執(zhí)行
          // 創(chuàng)建style標(biāo)簽,將js中的樣式資源插入進(jìn)行,添加到head中生效
          'style-loader',
          // 將css文件變成commonjs模塊加載js中,里面內(nèi)容是樣式字符串
          'css-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          // 將less文件編譯成css文件
          // 需要下載 less-loader和less
          'less-loader'
        ]
      }
    ]
  },
  // plugins的配置
  plugins: [
    // 詳細(xì)plugins的配置
  ],
  // 模式
  mode: 'development', // 開(kāi)發(fā)模式
  // mode: 'production'
}

3.2.2 打包html資源

/*
  loader: 1. 下載   2. 使用(配置loader)
  plugins: 1. 下載  2. 引入  3. 使用
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
    ]
  },
  plugins: [
    // plugins的配置
    // html-webpack-plugin
    // 功能:默認(rèn)會(huì)創(chuàng)建一個(gè)空的HTML,自動(dòng)引入打包輸出的所有資源(JS/CSS)
    // 需求:需要有結(jié)構(gòu)的HTML文件
    new HtmlWebpackPlugin({
      // 復(fù)制 './src/index.html' 文件,并自動(dòng)引入打包輸出的所有資源(JS/CSS)
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

3.2.3 打包圖片資源

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.less$/,
        // 要使用多個(gè)loader處理用use
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 問(wèn)題:默認(rèn)處理不了html中img圖片
        // 處理圖片資源
        test: /\.(jpg|png|gif)$/,
        // 使用一個(gè)loader
        // 下載 url-loader file-loader
        loader: 'url-loader',
        options: {
          // 圖片大小小于8kb,就會(huì)被base64處理
          // 優(yōu)點(diǎn): 減少請(qǐng)求數(shù)量(減輕服務(wù)器壓力)
          // 缺點(diǎn):圖片體積會(huì)更大(文件請(qǐng)求速度更慢)
          limit: 8 * 1024,
          // 問(wèn)題:因?yàn)閡rl-loader默認(rèn)使用es6模塊化解析,而html-loader引入圖片是commonjs
          // 解析時(shí)會(huì)出問(wèn)題:[object Module]
          // 解決:關(guān)閉url-loader的es6模塊化,使用commonjs解析
          esModule: false,
          // 給圖片進(jìn)行重命名
          // [hash:10]取圖片的hash的前10位
          // [ext]取文件原來(lái)擴(kuò)展名
          name: '[hash:10].[ext]'
        }
      },
      {
        test: /\.html$/,
        // 處理html文件的img圖片(負(fù)責(zé)引入img,從而能被url-loader進(jìn)行處理)
        loader: 'html-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

3.2.5 打包其他資源(不需要做優(yōu)化的資源,比如字體圖標(biāo))

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // 打包其他資源(除了html/js/css資源以外的資源)
      {
        // 排除css/js/html資源
        exclude: /\.(css|js|html|less)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

3.2.4 devServer

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // 打包其他資源(除了html/js/css資源以外的資源)
      {
        // 排除css/js/html資源
        exclude: /\.(css|js|html|less)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',

  // 開(kāi)發(fā)服務(wù)器 devServer:用來(lái)自動(dòng)化(自動(dòng)編譯,自動(dòng)打開(kāi)瀏覽器,自動(dòng)刷新瀏覽器~~)
  // 特點(diǎn):只會(huì)在內(nèi)存中編譯打包,不會(huì)有任何輸出
  // 啟動(dòng)devServer指令為:npx webpack-dev-server
  devServer: {
    // 項(xiàng)目構(gòu)建后路徑
    contentBase: resolve(__dirname, 'build'),
    // 啟動(dòng)gzip壓縮
    compress: true,
    // 端口號(hào)
    port: 3000,
    // 自動(dòng)打開(kāi)瀏覽器
    open: true
  }
};

3.3 開(kāi)發(fā)環(huán)境總結(jié)配置

3.3.1 問(wèn)題總結(jié)

  • 處理html中的img圖片問(wèn)題,在webpack5中html-loader中也需要配置,esModule為false,否則還是不生效,像下面這樣
    {
    test: /.html$/,
    /**
    * html-loader可以處理html中的img圖片,可負(fù)責(zé)將其中的圖片引入,然后交由url-loader進(jìn)行解析
    */
    loader: 'html-loader',
    options: {
    esModule: false
    }
    }
  • TypeError: Cannot read property 'tap' of undefined at HtmlWebpackPlugin.apply
    所以考慮到html-webpack-plugin的版本,我們需要把html-webpack-plugin的版本降低一些
    npm uninstall html-webpack-plugin
    npm install html-webpack-plugin@3.2.0 -D
  • ERROR in Error: Child compilation failed:
    Module build failed (from ../node_modules/html-loader/dist/cjs.js):
    TypeError: this.getOptions is not a function
    需要將html-loader降低版本
    npm uninstall html-loader
    npm install html-loader@0.5.5 -D
  • 打包后圖片不顯示
    需要多添加排除的選項(xiàng)|jpg|png|gif,否則圖片不顯示
    {
    // 排除css/js/html等資源
    exclude: /.(html|js|css|less|jpg|png|gif)/,
    loader: "file-loader",
    options: {
    name: "[hash:10].[ext]",
    outputPath: "media",
    },
    },

3.3.2 配置總結(jié)

開(kāi)發(fā)環(huán)境配置:能讓代碼運(yùn)行

運(yùn)行項(xiàng)目指令:

webpack 會(huì)將打包結(jié)果輸出出去

npx webpack-dev-server 只會(huì)在內(nèi)存中編譯打包,沒(méi)有輸出

// resolve用來(lái)拼接絕對(duì)路徑的方法
const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  // webpack配置
  // 入口起點(diǎn)
  entry: "./src/js/index.js",
  // 輸出
  output: {
    // 輸出文件名
    filename: "js/built.js",
    // 輸出路徑
    // __dirname nodejs的變量,代表當(dāng)前文件的目錄絕對(duì)路徑
    path: resolve(__dirname, "build"),
  },
  // loader的配置
  module: {
    rules: [
      // 詳細(xì)loader配置
      // 不同文件必須配置不同loader處理
      {
        // 匹配哪些文件
        test: /\.css$/,
        // 使用哪些loader進(jìn)行處理
        use: [
          // use數(shù)組中l(wèi)oader執(zhí)行順序:從右到左,從下到上 依次執(zhí)行
          // 創(chuàng)建style標(biāo)簽,將js中的樣式資源插入進(jìn)行,添加到head中生效
          "style-loader",
          // 將css文件變成commonjs模塊加載js中,里面內(nèi)容是樣式字符串
          "css-loader",
        ],
      },
      {
        test: /\.less$/,
        use: [
          "style-loader",
          "css-loader",
          // 將less文件編譯成css文件
          // 需要下載 less-loader和less
          "less-loader",
        ],
      },
      {
        // 問(wèn)題:默認(rèn)處理不了html中img圖片
        // 處理圖片資源
        test: /\.(jpg|png|gif)$/,
        // 使用一個(gè)loader
        // 下載 url-loader file-loader
        loader: "url-loader",
        options: {
          // 圖片大小小于8kb,就會(huì)被base64處理
          // 優(yōu)點(diǎn): 減少請(qǐng)求數(shù)量(減輕服務(wù)器壓力)
          // 缺點(diǎn):圖片體積會(huì)更大(文件請(qǐng)求速度更慢)
          //建議對(duì)8kb-12kb的圖片進(jìn)行處理
          limit: 8 * 1024,
          // 問(wèn)題:因?yàn)閡rl-loader默認(rèn)使用es6模塊化解析,而html-loader引入圖片是commonjs
          // 解析時(shí)會(huì)出問(wèn)題:[object Module]
          // 解決:關(guān)閉url-loader的es6模塊化,使用commonjs解析
          esModule: false,
          // 給圖片進(jìn)行重命名
          // [hash:10]取圖片的hash的前10位
          // [ext]取文件原來(lái)擴(kuò)展名
          name: "[hash:10].[ext]",
          outputPath: "imgs",
        },
      },
      {
        test: /\.html$/,
        // 處理html文件的img圖片(負(fù)責(zé)引入img,從而能被url-loader進(jìn)行處理)
        loader: "html-loader",
        //webpack5也需要關(guān)閉es6模塊化,使用commonJs解析才能夠打包
        options: {
          esModule: false,
        },
      },
      // 打包其他資源(除了html/js/css資源以外的資源)
      {
        // 排除css/js/html等資源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: "file-loader",
        options: {
          name: "[hash:10].[ext]",
          outputPath: "media",
        },
      },
    ],
  },
  // plugins的配置
  plugins: [
    // 詳細(xì)plugins的配置
    // html-webpack-plugin
    // 功能:默認(rèn)會(huì)創(chuàng)建一個(gè)空的HTML,自動(dòng)引入打包輸出的所有資源(JS/CSS)
    // new HtmlWebpackPlugin({}),
    // 需求:需要有結(jié)構(gòu)的HTML文件
    new HtmlWebpackPlugin({
      // 復(fù)制 './src/index.html' 文件,并自動(dòng)引入打包輸出的所有資源(JS/CSS)
      template: "./src/index.html",
    }),
  ],
  // 開(kāi)發(fā)服務(wù)器 devServer:用來(lái)自動(dòng)化(自動(dòng)編譯,自動(dòng)打開(kāi)瀏覽器,自動(dòng)刷新瀏覽器~~)
  // 特點(diǎn):只會(huì)在內(nèi)存中編譯打包,不會(huì)有任何輸出,不會(huì)輸出build文件
  // 啟動(dòng)devServer指令為:npx webpack-dev-server(啟動(dòng)前提需要下載webpack-dev-server包)
  devServer: {
    // 項(xiàng)目構(gòu)建后路徑
    contentBase: resolve(__dirname, "build"),
    // 啟動(dòng)gzip壓縮,讓我們代碼體積更小,速度更快
    compress: true,
    // 端口號(hào)
    port: 3000,
    // 自動(dòng)打開(kāi)瀏覽器
    open: true,
  },
  // 模式
  mode: "development", // 開(kāi)發(fā)模式
  // mode: 'production',//生產(chǎn)模式
};

四、webpack生產(chǎn)環(huán)境的基本配置

4.1 生產(chǎn)環(huán)境基礎(chǔ)配置

4.1.1 提取CSS成單獨(dú)文件

提取css成單獨(dú)文件需要的插件mini-css-extract-plugin

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
//提取css成單獨(dú)文件需要的插件mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 創(chuàng)建style標(biāo)簽,將樣式放入
          // 'style-loader', 
          // 這個(gè)loader取代style-loader。作用:提取js中的css成單獨(dú)文件
          MiniCssExtractPlugin.loader,
          // 將css文件整合到j(luò)s文件中
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      // 對(duì)輸出的css文件進(jìn)行重命名
      filename: 'css/built.css'
    })
  ],
  mode: 'development'
};

4.1.2 CSS兼容性處理

css兼容性處理需要的插件postcss-loader postcss-preset-env

會(huì)出現(xiàn)編譯不成功,將postcss-loader版本從4+降低為3.0.0

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 設(shè)置nodejs環(huán)境變量
// process.env.NODE_ENV = 'development';

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          /*
            css兼容性處理:postcss --> postcss-loader postcss-preset-env

            幫postcss找到package.json中browserslist里面的配置,通過(guò)配置加載指定的css兼容性樣式

            "browserslist": {
              // 開(kāi)發(fā)環(huán)境 --> 設(shè)置node環(huán)境變量:process.env.NODE_ENV = development
              "development": [
                "last 1 chrome version",
                "last 1 firefox version",
                "last 1 safari version"
              ],
              // 生產(chǎn)環(huán)境:默認(rèn)是看生產(chǎn)環(huán)境
              "production": [
                ">0.2%",
                "not dead",
                "not op_mini all"
              ]
            }
          */
          // 使用loader的默認(rèn)配置
          // 'postcss-loader',
          // 修改loader的配置
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    })
  ],
  mode: 'development'
};

4.1.3 壓縮CSS

壓縮CSS需要的插件:optimize-css-assets-webpack-plugin

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')

// 設(shè)置nodejs環(huán)境變量
// process.env.NODE_ENV = 'development';

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => [
                // postcss的插件
                require('postcss-preset-env')()
              ]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    // 壓縮css
    new OptimizeCssAssetsWebpackPlugin()
  ],
  mode: 'development'
};

4.1.4 JS語(yǔ)法檢查

需要下載的插件:eslint-loader eslint

檢查規(guī)則需要的插件:eslint-config-airbnb-base eslint-plugin-import eslint

如果想忽視eslint的語(yǔ)法檢查可以在代碼上一行添加: // eslint-disable-next-line

這樣下一行eslint所有規(guī)則都失效(下一行不進(jìn)行eslint檢查)

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      /*
        語(yǔ)法檢查: eslint-loader  eslint
          注意:只檢查自己寫(xiě)的源代碼,第三方的庫(kù)是不用檢查的
          設(shè)置檢查規(guī)則:
            package.json中eslintConfig中設(shè)置~
              "eslintConfig": {
                "extends": "airbnb-base"
              }
            airbnb --> eslint-config-airbnb-base  eslint-plugin-import eslint
      */
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'eslint-loader',
        options: {
          // 自動(dòng)修復(fù)eslint的錯(cuò)誤
          fix: true
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

4.1.5 JS兼容性處理

js兼容性處理需要下載的插件:babel-loader @babel/core @babel/preset-env @babel/polyfill core-js

  • 基本js兼容性處理 --> @babel/preset-env
    問(wèn)題:只能轉(zhuǎn)換基本語(yǔ)法,如promise高級(jí)語(yǔ)法不能轉(zhuǎn)換

  • 全部js兼容性處理 --> @babel/polyfill
    問(wèn)題:我只要解決部分兼容性問(wèn)題,但是將所有兼容性代碼全部 引入,體積太大了~

  • 需要做兼容性處理的就做:按需加載 --> core-js

    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');

    module.exports = {
    entry: './src/js/index.js',
    output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
    },
    module: {
    rules: [
    /*
    js兼容性處理:babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
    1. 基本js兼容性處理 --> @babel/preset-env
    問(wèn)題:只能轉(zhuǎn)換基本語(yǔ)法,如promise高級(jí)語(yǔ)法不能轉(zhuǎn)換
    2. 全部js兼容性處理 --> @babel/polyfill
    問(wèn)題:我只要解決部分兼容性問(wèn)題,但是將所有兼容性代碼全部引入,體積太大了~
    3. 需要做兼容性處理的就做:按需加載 --> core-js
    */
    {
    test: /.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
    // 預(yù)設(shè):指示babel做怎么樣的兼容性處理
    presets: [
    [
    '@babel/preset-env',
    {
    // 按需加載
    useBuiltIns: 'usage',
    // 指定core-js版本
    corejs: {
    version: 3
    },
    // 指定兼容性做到哪個(gè)版本瀏覽器
    targets: {
    chrome: '60',
    firefox: '60',
    ie: '9',
    safari: '10',
    edge: '17'
    }
    }
    ]
    ]
    }
    }
    ]
    },
    plugins: [
    new HtmlWebpackPlugin({
    template: './src/index.html'
    })
    ],
    mode: 'development'
    };

4.1.6 JS壓縮

將mode設(shè)置為production 就可以自動(dòng)壓縮js代碼

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  // 生產(chǎn)環(huán)境下會(huì)自動(dòng)壓縮js代碼
  mode: 'production'
};

4.1.7 html壓縮

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      // 壓縮html代碼
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注釋
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

4.2 生產(chǎn)環(huán)境總結(jié)配置

4.2.1 問(wèn)題總結(jié)

  • ERROR in ./src/css/a.css
    Module build failed (from ../node_modules/mini-css-extract-plugin/dist/loader.js):
    ModuleBuildError: Module build failed (from ../node_modules/postcss-loader/dist/cjs.js):
    TypeError: this.getOptions is not a function
    at Object.loader (Z:\learn\webpack-study\mycode\node_modules\postcss-loader\dist\index.js:38:24)
    at Z:\learn\webpack-study\mycode\node_modules\webpack\lib\NormalModule.js:316:20
    at Z:\learn\webpack-study\mycode\node_modules\loader-runner\lib\LoaderRunner.js:367:11
    at Z:\learn\webpack-study\mycode\node_modules\loader-runner\lib\LoaderRunner.js:233:18
    @ ./src/js/index.js 1:0-22
    ERROR in ./src/css/b.css
    Module build failed (from ../node_modules/mini-css-extract-plugin/dist/loader.js):
    ModuleBuildError: Module build failed (from ../node_modules/postcss-loader/dist/cjs.js):
    TypeError: this.getOptions is not a function
    at Object.loader (Z:\learn\webpack-study\mycode\node_modules\postcss-loader\dist\index.js:38:24)
    at Z:\learn\webpack-study\mycode\node_modules\webpack\lib\NormalModule.js:316:20
    at Z:\learn\webpack-study\mycode\node_modules\loader-runner\lib\LoaderRunner.js:367:11
    at Z:\learn\webpack-study\mycode\node_modules\loader-runner\lib\LoaderRunner.js:233:18
    @ ./src/js/index.js 2:0-22
    降低postcss-loader的版本,從4+降到了3.0.0,后來(lái)就可以正常編譯了。
  • error 'core-js' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
    1:8 error Unexpected use of file extension "js" for "core-js/modules/es.promise.js" import/extensions
    1:8 error Strings must use singlequote quotes
    2:8 error 'core-js' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
    2:8 error Unexpected use of file extension "js" for "core-js/modules/es.object.to-string.js" import/extensions
    2:8 error Strings must use singlequote quotes
    3:8 error 'core-js' should be listed in the project's dependencies, not devDependencies import/no-extraneous-dependencies
    3:8 error Unexpected use of file extension "js" for "core-js/modules/web.timers.js" import/extensions
    下載corejs插件不限制在開(kāi)發(fā)環(huán)境 不加-D
    還需要添加 enforce: "pre",
    {
    test: /.js$/,
    exclude: /node_modules/,
    loader: "eslint-loader",
    // 優(yōu)先執(zhí)行
    enforce: "pre",
    options: {
    // 自動(dòng)修復(fù)eslint的錯(cuò)誤
    fix: true,
    },
    },

4.2.2 總結(jié)配置

注意:正常來(lái)講,一個(gè)文件只能被一個(gè)loader處理。

當(dāng)一個(gè)文件要被多個(gè)loader處理,那么一定要指定loader執(zhí)行的先后順序:

 先執(zhí)行eslint 在執(zhí)行babel

const { resolve } = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");

// 設(shè)置nodejs環(huán)境變量
// process.env.NODE_ENV = "development";

// 復(fù)用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  "css-loader",
  {
    // 還需要在package.json中定義browserslist
    loader: "postcss-loader",
    options: {
      ident: "postcss",
      plugins: () => [require("postcss-preset-env")()],
    },
  },
];

module.exports = {
  entry: "./src/js/index.js",
  output: {
    filename: "js/built.js",
    path: resolve(__dirname, "build"),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [...commonCssLoader],
        // use: [
        //   // 創(chuàng)建style標(biāo)簽,將樣式放入
        //   // 'style-loader',
        //   // 這個(gè)loader取代style-loader。作用:提取js中的css成單獨(dú)文件
        //   MiniCssExtractPlugin.loader,
        //   // 將css文件整合到j(luò)s文件中
        //   "css-loader",
        //   /*
        //     css兼容性處理:postcss --> postcss-loader postcss-preset-env

        //     postcss-preset-env插件幫postcss找到package.json中browserslist里面的配置,通過(guò)配置加載指定的css兼容性樣式

        //     package.json中的配置:
        //     "browserslist": {
        //       // 開(kāi)發(fā)環(huán)境 --> 設(shè)置node環(huán)境變量:process.env.NODE_ENV = development
        //       "development": [
        //         "last 1 chrome version",
        //         "last 1 firefox version",
        //         "last 1 safari version"
        //       ],
        //       // 生產(chǎn)環(huán)境:默認(rèn)是看生產(chǎn)環(huán)境 與mode:development設(shè)置無(wú)關(guān) 與process.env.NODE_ENV = development有關(guān)
        //       "production": [
        //         ">0.2%",
        //         "not dead",
        //         "not op_mini all"
        //       ]
        //     }
        //   */
        //   // 使用loader的默認(rèn)配置
        //   // "postcss-loader",
        //   // 修改loader的配置,就要是如下的寫(xiě)法
        //   {
        //     loader: "postcss-loader",
        //     options: {
        //       //固定寫(xiě)法
        //       ident: "postcss",
        //       plugins: () => [
        //         // postcss的插件
        //         require("postcss-preset-env")(),
        //       ],
        //     },
        //   },
        // ],
      },
      {
        test: /\.less$/,
        use: [...commonCssLoader, "less-loader"],
      },
      /*
        語(yǔ)法檢查: eslint-loader  eslint
          注意:只檢查自己寫(xiě)的源代碼,第三方的庫(kù)是不用檢查的
          設(shè)置檢查規(guī)則:
            package.json中eslintConfig中設(shè)置~
              "eslintConfig": {
                "extends": "airbnb-base"
              }
            airbnb --> eslint-config-airbnb-base eslint  eslint-plugin-import eslint
      */
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
        // 優(yōu)先執(zhí)行
        enforce: "pre",
        options: {
          // 自動(dòng)修復(fù)eslint的錯(cuò)誤
          fix: true,
        },
      },
      /*
        js兼容性處理:babel-loader @babel/core   @babel/preset-env
          1. 基本js兼容性處理 --> @babel/preset-env
            問(wèn)題:只能轉(zhuǎn)換基本語(yǔ)法,如promise高級(jí)語(yǔ)法不能轉(zhuǎn)換
          2. 全部js兼容性處理 --> @babel/polyfill 簡(jiǎn)單粗暴直接引入js文件中
            問(wèn)題:我只要解決部分兼容性問(wèn)題,但是將所有兼容性代碼全部引入,體積太大了~
          3. 需要做兼容性處理的就做:按需加載  --> core-js
      */
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          // 預(yù)設(shè):指示babel做怎么樣的兼容性處理
          presets: [
            [
              "@babel/preset-env",
              {
                // 按需加載
                useBuiltIns: "usage",
                // 指定core-js版本
                corejs: {
                  version: 3,
                },
                // 指定兼容性做到哪個(gè)版本瀏覽器
                targets: {
                  chrome: "60",
                  firefox: "60",
                  ie: "9",
                  safari: "10",
                  edge: "17",
                },
              },
            ],
          ],
        },
      },
      {
        test: /\.(jpg|png|gif)/,
        loader: "url-loader",
        options: {
          limit: 8 * 1024,
          name: "[hash:10].[ext]",
          outputPath: "imgs",
          esModule: false,
        },
      },
      {
        test: /\.html$/,
        loader: "html-loader",
      },
      {
        exclude: /\.(js|css|less|html|jpg|png|gif)/,
        loader: "file-loader",
        options: {
          outputPath: "media",
        },
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      // 壓縮html代碼
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注釋
        removeComments: true,
      },
    }),
    //提取css成單獨(dú)文件
    new MiniCssExtractPlugin({
      // 對(duì)輸出的css文件進(jìn)行重命名
      filename: "css/built.css",
    }),
    // 壓縮css
    new OptimizeCssAssetsWebpackPlugin(),
  ],
  mode: "development",
};

五、webpack優(yōu)化配置

總的來(lái)說(shuō),開(kāi)發(fā)環(huán)境需要優(yōu)化打包構(gòu)建速度,對(duì)開(kāi)發(fā)者更加友好,因?yàn)榇a會(huì)越來(lái)越多,導(dǎo)致包的體積越來(lái)越大,就需要我們注重打包時(shí)間,這樣對(duì)調(diào)試開(kāi)發(fā)更加友好;還需要我們優(yōu)化代碼調(diào)試,了解代碼出錯(cuò)在哪里。

生產(chǎn)環(huán)境配置需要代碼上線運(yùn)行即可,需要優(yōu)化代碼的性能,讓用戶體驗(yàn)感更好,所以要求性能提升。

開(kāi)發(fā)環(huán)境性能優(yōu)化:

  • 優(yōu)化打包構(gòu)建速度
  • HMR
  • 優(yōu)化代碼調(diào)試
  • source-map

生產(chǎn)環(huán)境性能優(yōu)化:

優(yōu)化打包構(gòu)建速度

  • oneOf

  • babel緩存

  • 多進(jìn)程打包

  • externals

  • dll

  • 優(yōu)化代碼運(yùn)行的性能

  • 緩存(hash-chunkhash-contenthash)

  • tree shaking

  • code split

  • 懶加載/預(yù)加載

  • pwa

5.1 開(kāi)發(fā)環(huán)境性能優(yōu)化配置

5.1.1 HMR(熱膜替換)

為什么會(huì)提出這個(gè)HMR?是因?yàn)榧僭O(shè)有1000個(gè)模塊代碼,其中一個(gè)模塊更改,其他都要重新跑一邊,速度會(huì)很慢。所以就提出HMR,一個(gè)模塊發(fā)生變化,只會(huì)重新打包這一個(gè)模塊(而不是打包所有模塊) ;

/*
  HMR: hot module replacement 熱模塊替換 / 模塊熱替換
    作用:一個(gè)模塊發(fā)生變化,只會(huì)重新打包這一個(gè)模塊(而不是打包所有模塊) 
      極大提升構(gòu)建速度
      
      樣式文件:可以使用HMR功能:因?yàn)閟tyle-loader內(nèi)部實(shí)現(xiàn)了~
      js文件:默認(rèn)不能使用HMR功能 --> 需要修改js代碼,添加支持HMR功能的代碼
        注意:HMR功能對(duì)js的處理,只能處理非入口js文件的其他文件。
      html文件: 默認(rèn)不能使用HMR功能.同時(shí)會(huì)導(dǎo)致問(wèn)題:html文件不能熱更新了~ (不用做HMR功能)
        解決:修改entry入口,將html文件引入
*/

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
      {
        // 處理less資源
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 處理css資源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        // 處理圖片資源
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          // 關(guān)閉es6模塊化
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        // 處理html中img資源
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        // 處理其他資源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    // 開(kāi)啟HMR功能
    // 當(dāng)修改了webpack配置,新配置要想生效,必須重新webpack服務(wù)
    hot: true
  }
};


// 引入
import print from './print';
import '../css/iconfont.css';
import '../css/index.less';

console.log('index.js文件被加載了~');

print();

function add(x, y) {
  return x + y;
}

console.log(add(1, 3));

if (module.hot) {
  // 一旦 module.hot 為true,說(shuō)明開(kāi)啟了HMR功能。 --> 讓HMR功能代碼生效
  module.hot.accept('./print.js', function() {
    // 方法會(huì)監(jiān)聽(tīng) print.js 文件的變化,一旦發(fā)生變化,其他模塊不會(huì)重新打包構(gòu)建。
    // 會(huì)執(zhí)行后面的回調(diào)函數(shù)
    print();
  });
}

5.1.2 source-map

/*
  source-map: 一種 提供源代碼到構(gòu)建后代碼映射 技術(shù) (如果構(gòu)建后代碼出錯(cuò)了,通過(guò)映射可以追蹤源代碼錯(cuò)誤)

    [inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

    source-map:外部
      錯(cuò)誤代碼準(zhǔn)確信息 和 源代碼的錯(cuò)誤位置
    inline-source-map:內(nèi)聯(lián)
      只生成一個(gè)內(nèi)聯(lián)source-map
      錯(cuò)誤代碼準(zhǔn)確信息 和 源代碼的錯(cuò)誤位置
    hidden-source-map:外部
      錯(cuò)誤代碼錯(cuò)誤原因,但是沒(méi)有錯(cuò)誤位置
      不能追蹤源代碼錯(cuò)誤,只能提示到構(gòu)建后代碼的錯(cuò)誤位置
    eval-source-map:內(nèi)聯(lián)
      每一個(gè)文件都生成對(duì)應(yīng)的source-map,都在eval
      錯(cuò)誤代碼準(zhǔn)確信息 和 源代碼的錯(cuò)誤位置(位置是哈希值)
    nosources-source-map:外部
      錯(cuò)誤代碼準(zhǔn)確信息, 但是沒(méi)有任何源代碼信息
    cheap-source-map:外部
      錯(cuò)誤代碼準(zhǔn)確信息 和 源代碼的錯(cuò)誤位置 
      只能精確的行,不能精確到列
    cheap-module-source-map:外部
      錯(cuò)誤代碼準(zhǔn)確信息 和 源代碼的錯(cuò)誤位置 
      module會(huì)將loader的source map加入

    內(nèi)聯(lián) 和 外部的區(qū)別:1. 外部生成了文件,內(nèi)聯(lián)沒(méi)有 2. 內(nèi)聯(lián)構(gòu)建速度更快

    開(kāi)發(fā)環(huán)境:速度快,調(diào)試更友好
      速度快(eval>inline>cheap>...)
        eval-cheap-souce-map
        eval-source-map
      調(diào)試更友好  
        souce-map
        cheap-module-souce-map
        cheap-souce-map

      --> eval-source-map(vue腳手架是這個(gè))  / eval-cheap-module-souce-map

    生產(chǎn)環(huán)境:源代碼要不要隱藏? 調(diào)試要不要更友好
      內(nèi)聯(lián)會(huì)讓代碼體積變大,所以在生產(chǎn)環(huán)境不用內(nèi)聯(lián)
      nosources-source-map 全部隱藏
      hidden-source-map 只隱藏源代碼,會(huì)提示構(gòu)建后代碼錯(cuò)誤信息

      --> source-map / cheap-module-souce-map
*/

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: ['./src/js/index.js', './src/index.html'],
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
      {
        // 處理less資源
        test: /\.less$/,
        use: ['style-loader', 'css-loader', 'less-loader']
      },
      {
        // 處理css資源
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        // 處理圖片資源
        test: /\.(jpg|png|gif)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: '[hash:10].[ext]',
          // 關(guān)閉es6模塊化
          esModule: false,
          outputPath: 'imgs'
        }
      },
      {
        // 處理html中img資源
        test: /\.html$/,
        loader: 'html-loader'
      },
      {
        // 處理其他資源
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          outputPath: 'media'
        }
      }
    ]
  },
  plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    hot: true
  },
  devtool: 'eval-source-map'
};

5.2 生產(chǎn)環(huán)境性能優(yōu)化配置

5.2.1優(yōu)化打包構(gòu)建速度

5.2.1.1 oneOf

優(yōu)化生產(chǎn)環(huán)境構(gòu)建速度的

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 定義nodejs環(huán)境變量:決定使用browserslist的哪個(gè)環(huán)境
process.env.NODE_ENV = 'production';

// 復(fù)用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 還需要在package.json中定義browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 優(yōu)先執(zhí)行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只會(huì)匹配一個(gè)
        // 注意:不能有兩個(gè)配置處理同一種類型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常來(lái)講,一個(gè)文件只能被一個(gè)loader處理。
            當(dāng)一個(gè)文件要被多個(gè)loader處理,那么一定要指定loader執(zhí)行的先后順序:
              先執(zhí)行eslint 在執(zhí)行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: {version: 3},
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ]
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

5.2.1.2 緩存(babel緩存和文件資源緩存)

為什么會(huì)有babel緩存呢?因?yàn)閎abel會(huì)對(duì)我們js代碼進(jìn)行編譯處理,編譯成瀏覽器能夠識(shí)別的語(yǔ)法。

HMR是對(duì)開(kāi)發(fā)環(huán)境來(lái)說(shuō)的,生產(chǎn)環(huán)境下不能用HMR功能,因?yàn)镠MR是基于devserver的,而生產(chǎn)環(huán)境是不需要devServer。

而babel想做的是其中一個(gè)js文件變,其他文件不變,就需要開(kāi)啟babel緩存。假設(shè)對(duì)100個(gè)文件進(jìn)行編譯后的緩存,當(dāng)后面再去做的時(shí)候文件沒(méi)有變化,就是使用緩存了,而不是重新構(gòu)建一次了。

文件資源緩存:通過(guò)添加hash值去更新文件,當(dāng)文件改變之后,只有改變的文件對(duì)應(yīng)的打包文件的hash值會(huì)變化

推薦使用contenthash

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

/*
  緩存:
    babel緩存
      cacheDirectory: true
      --> 讓第二次打包構(gòu)建速度更快
    文件資源緩存
      hash: 每次wepack構(gòu)建時(shí)會(huì)生成一個(gè)唯一的hash值。
        問(wèn)題: 因?yàn)閖s和css同時(shí)使用一個(gè)hash值。
          如果重新打包,會(huì)導(dǎo)致所有緩存失效。(可能我卻只改動(dòng)一個(gè)文件)
      chunkhash:根據(jù)chunk生成的hash值。如果打包來(lái)源于同一個(gè)chunk,那么hash值就一樣
        問(wèn)題: js和css的hash值還是一樣的
          因?yàn)閏ss是在js中被引入的,所以同屬于一個(gè)chunk
      contenthash: 根據(jù)文件的內(nèi)容生成hash值。不同文件hash值一定不一樣    
      --> 讓代碼上線運(yùn)行緩存更好使用
*/

// 定義nodejs環(huán)境變量:決定使用browserslist的哪個(gè)環(huán)境
process.env.NODE_ENV = 'production';

// 復(fù)用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 還需要在package.json中定義browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 優(yōu)先執(zhí)行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只會(huì)匹配一個(gè)
        // 注意:不能有兩個(gè)配置處理同一種類型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常來(lái)講,一個(gè)文件只能被一個(gè)loader處理。
            當(dāng)一個(gè)文件要被多個(gè)loader處理,那么一定要指定loader執(zhí)行的先后順序:
              先執(zhí)行eslint 在執(zhí)行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ],
              // 開(kāi)啟babel緩存
              // 第二次構(gòu)建時(shí),會(huì)讀取之前的緩存
              cacheDirectory: true
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

5.2.1.3 tree shaking(樹(shù)搖)

目的:為了去除我們應(yīng)用程序中沒(méi)有使用的代碼,減少打包體積,提高加載速度

注意要在package.json中配置 "sideEffects": [".css", ".less"],防止把副作用文件給干掉

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

/*
  tree shaking:去除無(wú)用代碼
    前提:1. 必須使用ES6模塊化  2. 開(kāi)啟production環(huán)境
    作用: 減少代碼體積

    在package.json中配置 
      "sideEffects": false 所有代碼都沒(méi)有副作用(都可以進(jìn)行tree shaking)
        問(wèn)題:可能會(huì)把css / @babel/polyfill (副作用)文件干掉
      "sideEffects": ["*.css", "*.less"]
*/

// 定義nodejs環(huán)境變量:決定使用browserslist的哪個(gè)環(huán)境
process.env.NODE_ENV = 'production';

// 復(fù)用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 還需要在package.json中定義browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 優(yōu)先執(zhí)行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只會(huì)匹配一個(gè)
        // 注意:不能有兩個(gè)配置處理同一種類型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常來(lái)講,一個(gè)文件只能被一個(gè)loader處理。
            當(dāng)一個(gè)文件要被多個(gè)loader處理,那么一定要指定loader執(zhí)行的先后順序:
              先執(zhí)行eslint 在執(zhí)行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ],
              // 開(kāi)啟babel緩存
              // 第二次構(gòu)建時(shí),會(huì)讀取之前的緩存
              cacheDirectory: true
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

5.2.1.3 code split (代碼分割)

目的:將打包的一個(gè)文件輸出成多個(gè)文件,比如說(shuō)將一個(gè)大的文件分割成三個(gè)文件,加載的時(shí)候可以并行加載,從而速度更快,還可以實(shí)現(xiàn)按需加載的功能(就是需要這個(gè)文件我們就加載使用)

demo1:多入口打包

單頁(yè)面對(duì)應(yīng)單入口,多頁(yè)面應(yīng)用對(duì)應(yīng)多入口

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 單入口
  // entry: './src/js/index.js',
  entry: {
    // 多入口:有一個(gè)入口,最終輸出就有一個(gè)bundle
    index: './src/js/index.js',
    test: './src/js/test.js'
  },
  output: {
    // [name]:取文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

demo2:

demo1問(wèn)題是多入口不太靈活,如果后面有多個(gè)入口,改來(lái)改去太麻煩,所以就有demo2

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 單入口
  // entry: './src/js/index.js',
  entry: {
    index: './src/js/index.js',
    test: './src/js/test.js'
  },
  output: {
    // [name]:取文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  /*
    1. 可以將node_modules中代碼單獨(dú)打包一個(gè)chunk最終輸出
    2. 自動(dòng)分析多入口chunk中,有沒(méi)有公共的文件。如果有會(huì)打包成單獨(dú)一個(gè)chunk
  */
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  mode: 'production'
};

demo3:

demo2問(wèn)題:可以對(duì)node_modules中代碼進(jìn)行單獨(dú)打包一個(gè)chunk最終輸出,并且也可以對(duì)多入口的情況下,對(duì)公共的文件打包成一個(gè)chunk,但是對(duì)單入口下,自己寫(xiě)的公共js代碼無(wú)法進(jìn)行打包成一個(gè)chunk。

通過(guò)js代碼,讓單入口情況下,某個(gè)文件單獨(dú)打包成一個(gè)chunk

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

/*
  通過(guò)js代碼,讓某個(gè)文件被單獨(dú)打包成一個(gè)chunk
  import動(dòng)態(tài)導(dǎo)入語(yǔ)法:能將某個(gè)文件單獨(dú)打包
  加上/* webpackChunkName: 'test' */是給打包的chunk取一個(gè)名字為test
*/
import(/* webpackChunkName: 'test' */'./test')
  .then(({ mul, count }) => {
    // 文件加載成功~
    // eslint-disable-next-line
    console.log(mul(2, 5));
  })
  .catch(() => {
    // eslint-disable-next-line
    console.log('文件加載失敗~');
  });

// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));


const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 單入口
  entry: './src/js/index.js',
  output: {
    // [name]:取文件名
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  /*
    1. 可以將node_modules中代碼單獨(dú)打包一個(gè)chunk最終輸出
    2. 自動(dòng)分析多入口chunk中,有沒(méi)有公共的文件。如果有會(huì)打包成單獨(dú)一個(gè)chunk
  */
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  mode: 'production'
};

5.2.1.4 lazy loading (懶加載/預(yù)加載)

懶加載指的是js文件的懶加載

利用代碼分割的思路,將import語(yǔ)法放入異步回調(diào)代碼中;

懶加載中添加預(yù)加載,預(yù)加載:在觸發(fā)js代碼之前就加載了,之后點(diǎn)擊按鈕進(jìn)行回調(diào)是讀取的緩存中的js

console.log('index.js文件被加載了~');

// import { mul } from './test';

document.getElementById('btn').onclick = function() {
  // 懶加載~:當(dāng)文件需要使用時(shí)才加載~
  // 預(yù)加載 prefetch:會(huì)在使用之前,提前加載js文件 
  // 正常加載可以認(rèn)為是并行加載(同一時(shí)間加載多個(gè)文件) 
  // 預(yù)加載 prefetch:等其他資源加載完畢,瀏覽器空閑了,再偷偷加載資源  但是這種兼容性不好,只能在高版本的瀏覽器中使用,在移動(dòng)端和IE慎用
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};


const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 單入口
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    })
  ],
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  mode: 'production'
};

5.2.1.5 pwa (離線可訪問(wèn)技術(shù))

PWA:漸進(jìn)式網(wǎng)絡(luò)應(yīng)用程序

目的:實(shí)現(xiàn)像應(yīng)用程序一樣,離線也可以訪問(wèn),性能也更好,缺點(diǎn)是兼容性不太好

需要下載插件 workbox-webpack-plugin

問(wèn)題:打包報(bào)錯(cuò)

'workbox-routing/registerRoute.js' is imported by ..\node_modules\workbox-precaching\addRoute.js, but could not be resolved – treating it as an external dependency
'workbox-core/private/getFriendlyURL.js' is imported by ..\node_modules\workbox-precaching\PrecacheRoute.js, but could not be resolved – treating it as an external dependency
'workbox-core/private/logger.js' is imported by ..\node_modules\workbox-precaching\PrecacheRoute.js, but could not be resolved – treating it as an external dependency
'workbox-routing/Route.js' is imported by ..\node_modules\workbox-precaching\PrecacheRoute.js, but could not be resolved – treating it as an external dependency
'workbox-core/private/assert.js' is imported by ..\node_modules\workbox-precaching\PrecacheController.js, but could not be resolved – treating it as an external dependency
'workbox-core/private/cacheNames.js' is imported by ..\node_modules\workbox-precaching\PrecacheController.js, but could not be resolved – treating it as an external dependency

因?yàn)閣orkbox-webpack-plugin版本的問(wèn)題,pwa技術(shù)在cache里面沒(méi)有緩存,后來(lái)我是降低版本為5.0.0.才解決這個(gè)問(wèn)題的

index.js

import { mul } from './test';
import '../css/index.css';

function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

// eslint-disable-next-line
console.log(mul(2, 3));
// eslint-disable-next-line
console.log(sum(1, 2, 3, 4));

/*
  1. eslint不認(rèn)識(shí) window、navigator全局變量
    解決:需要修改package.json中eslintConfig配置
      "env": {
        "browser": true // 支持瀏覽器端全局變量
      }
   2. sw代碼必須運(yùn)行在服務(wù)器上
      --> nodejs
      -->
        npm i serve -g
        serve -s build 啟動(dòng)服務(wù)器,將build目錄下所有資源作為靜態(tài)資源暴露出去
*/
// 注冊(cè)serviceWorker
// 處理兼容性問(wèn)題
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker
      .register('/service-worker.js')
      .then(() => {
        console.log('sw注冊(cè)成功了~');
      })
      .catch(() => {
        console.log('sw注冊(cè)失敗了~');
      });
  });
}

webpack.config.js

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

/*
  PWA: 漸進(jìn)式網(wǎng)絡(luò)開(kāi)發(fā)應(yīng)用程序(離線可訪問(wèn))
    workbox --> workbox-webpack-plugin
*/

// 定義nodejs環(huán)境變量:決定使用browserslist的哪個(gè)環(huán)境
process.env.NODE_ENV = 'production';

// 復(fù)用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 還需要在package.json中定義browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 優(yōu)先執(zhí)行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只會(huì)匹配一個(gè)
        // 注意:不能有兩個(gè)配置處理同一種類型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常來(lái)講,一個(gè)文件只能被一個(gè)loader處理。
            當(dāng)一個(gè)文件要被多個(gè)loader處理,那么一定要指定loader執(zhí)行的先后順序:
              先執(zhí)行eslint 在執(zhí)行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              presets: [
                [
                  '@babel/preset-env',
                  {
                    useBuiltIns: 'usage',
                    corejs: { version: 3 },
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ],
              // 開(kāi)啟babel緩存
              // 第二次構(gòu)建時(shí),會(huì)讀取之前的緩存
              cacheDirectory: true
            }
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    }),
    new WorkboxWebpackPlugin.GenerateSW({
      /*
        1. 幫助serviceworker快速啟動(dòng)
        2. 刪除舊的 serviceworker

        生成一個(gè) serviceworker 配置文件~
      */
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

5.2.1.6 多進(jìn)程打包

多進(jìn)程打包:
進(jìn)程啟動(dòng)大概為600ms,進(jìn)程通信也有開(kāi)銷。
只有工作消耗時(shí)間比較長(zhǎng),才需要多進(jìn)程打包

一個(gè)項(xiàng)目中通常js文件比較多,所以通常是對(duì)js進(jìn)行多進(jìn)程打包的,減少整體打包的時(shí)間

const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');

/*
  PWA: 漸進(jìn)式網(wǎng)絡(luò)開(kāi)發(fā)應(yīng)用程序(離線可訪問(wèn))
    workbox --> workbox-webpack-plugin
*/

// 定義nodejs環(huán)境變量:決定使用browserslist的哪個(gè)環(huán)境
process.env.NODE_ENV = 'production';

// 復(fù)用loader
const commonCssLoader = [
  MiniCssExtractPlugin.loader,
  'css-loader',
  {
    // 還需要在package.json中定義browserslist
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [require('postcss-preset-env')()]
    }
  }
];

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 優(yōu)先執(zhí)行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        // 以下loader只會(huì)匹配一個(gè)
        // 注意:不能有兩個(gè)配置處理同一種類型文件
        oneOf: [
          {
            test: /\.css$/,
            use: [...commonCssLoader]
          },
          {
            test: /\.less$/,
            use: [...commonCssLoader, 'less-loader']
          },
          /*
            正常來(lái)講,一個(gè)文件只能被一個(gè)loader處理。
            當(dāng)一個(gè)文件要被多個(gè)loader處理,那么一定要指定loader執(zhí)行的先后順序:
              先執(zhí)行eslint 在執(zhí)行babel
          */
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              /* 
                開(kāi)啟多進(jìn)程打包。 
                進(jìn)程啟動(dòng)大概為600ms,進(jìn)程通信也有開(kāi)銷。
                只有工作消耗時(shí)間比較長(zhǎng),才需要多進(jìn)程打包
              */
              {
                loader: 'thread-loader',
                options: {
                  workers: 2 // 進(jìn)程2個(gè)
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  presets: [
                    [
                      '@babel/preset-env',
                      {
                        useBuiltIns: 'usage',
                        corejs: { version: 3 },
                        targets: {
                          chrome: '60',
                          firefox: '50'
                        }
                      }
                    ]
                  ],
                  // 開(kāi)啟babel緩存
                  // 第二次構(gòu)建時(shí),會(huì)讀取之前的緩存
                  cacheDirectory: true
                }
              }
            ]
          },
          {
            test: /\.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              outputPath: 'imgs',
              esModule: false
            }
          },
          {
            test: /\.html$/,
            loader: 'html-loader'
          },
          {
            exclude: /\.(js|css|less|html|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/built.[contenthash:10].css'
    }),
    new OptimizeCssAssetsWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: {
        collapseWhitespace: true,
        removeComments: true
      }
    }),
    new WorkboxWebpackPlugin.GenerateSW({
      /*
        1. 幫助serviceworker快速啟動(dòng)
        2. 刪除舊的 serviceworker

        生成一個(gè) serviceworker 配置文件~
      */
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

5.2.1.7 externals

目的:防止將一些包打包輸出在我們的budle中。有時(shí)候我們不想講juery包進(jìn)行打包,我們是使用的CDN進(jìn)行juery的獲取,此時(shí)我們就需要在打包的時(shí)候排除打包juery

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'production',
  externals: {
    // 拒絕jQuery被打包進(jìn)來(lái)
    jquery: 'jQuery'
  }
};

5.2.1.8 dll(動(dòng)態(tài)鏈接庫(kù))

dll:讓某些庫(kù)單獨(dú)打包,后直接引入到 build 中。可以在 code split 分割出 node_modules 后再用 dll 更細(xì)的分割,優(yōu)化代碼運(yùn)行的性能

webpack.dll.js

/*
  使用dll技術(shù),對(duì)某些庫(kù)(第三方庫(kù):jquery、react、vue...)進(jìn)行單獨(dú)打包
    當(dāng)你運(yùn)行 webpack 時(shí),默認(rèn)查找 webpack.config.js 配置文件
    需求:需要運(yùn)行 webpack.dll.js 文件
      --> webpack --config webpack.dll.js
*/

const { resolve } = require('path');
const webpack = require('webpack');

module.exports = {
  entry: {
    // 最終打包生成的[name] --> jquery
    // ['jquery'] --> 要打包的庫(kù)是jquery
    jquery: ['jquery'],
  },
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash]' // 打包的庫(kù)里面向外暴露出去的內(nèi)容叫什么名字
  },
  plugins: [
    // 打包生成一個(gè) manifest.json --> 提供和jquery映射  將來(lái)我們可以知道我們juery不需要打包,juery庫(kù)的名稱是[name]_[hash]
    new webpack.DllPlugin({
      name: '[name]_[hash]', // 映射庫(kù)的暴露的內(nèi)容名稱
      path: resolve(__dirname, 'dll/manifest.json') // 輸出文件路徑
    })
  ],
  mode: 'production'
};

webpack.config.js

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'built.js',
    path: resolve(__dirname, 'build')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    // 告訴webpack哪些庫(kù)不參與打包,同時(shí)使用時(shí)的名稱也得變~
    new webpack.DllReferencePlugin({
      manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    // 將某個(gè)文件打包輸出去,并在html中自動(dòng)引入該資源
    new AddAssetHtmlWebpackPlugin({
      filepath: resolve(__dirname, 'dll/jquery.js')
    })
  ],
  mode: 'production'
};

六、webpack配置詳情

6.1 entry

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

/*
  entry: 入口起點(diǎn)
    1. string --> './src/index.js'
      單入口
      打包形成一個(gè)chunk。 輸出一個(gè)bundle文件。
      此時(shí)chunk的名稱默認(rèn)是 main
    2. array  --> ['./src/index.js', './src/add.js']
      多入口
      所有入口文件最終只會(huì)形成一個(gè)chunk, 輸出出去只有一個(gè)bundle文件。
        --> 只有在HMR功能中讓html熱更新生效~
    3. object
      多入口
      有幾個(gè)入口文件就形成幾個(gè)chunk,輸出幾個(gè)bundle文件
      此時(shí)chunk的名稱是 key

      --> 特殊用法
        {
          // 所有入口文件最終只會(huì)形成一個(gè)chunk, 輸出出去只有一個(gè)bundle文件。
          index: ['./src/index.js', './src/count.js'], 
          // 形成一個(gè)chunk,輸出一個(gè)bundle文件。
          add: './src/add.js'
        }
*/

module.exports = {
  entry: {
    index: ['./src/index.js', './src/count.js'], 
    add: './src/add.js'
  },
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

6.2 output

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    // 文件名稱(指定名稱+目錄)
    filename: 'js/[name].js',
    // 輸出文件目錄(將來(lái)所有資源輸出的公共目錄)
    path: resolve(__dirname, 'build'),
    // 所有資源引入公共路徑前綴 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
    publicPath: '/',
    chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名稱
    // library: '[name]', // 整個(gè)庫(kù)向外暴露的變量名
    // libraryTarget: 'window' // 變量名添加到哪個(gè)上 browser
    // libraryTarget: 'global' // 變量名添加到哪個(gè)上 node
    // libraryTarget: 'commonjs'
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

6.3 module

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      // loader的配置
      {
        test: /\.css$/,
        // 多個(gè)loader用use
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.js$/,
        // 排除node_modules下的js文件
        exclude: /node_modules/,
        // 只檢查 src 下的js文件
        include: resolve(__dirname, 'src'),
        // 優(yōu)先執(zhí)行
        enforce: 'pre',
        // 延后執(zhí)行
        // enforce: 'post',
        // 單個(gè)loader用loader
        loader: 'eslint-loader',
        options: {}
      },
      {
        // 以下配置只會(huì)生效一個(gè)
        oneOf: []
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

6.4 resolve

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development',
  // 解析模塊的規(guī)則
  resolve: {
    // 配置解析模塊路徑別名: 優(yōu)點(diǎn)簡(jiǎn)寫(xiě)路徑 缺點(diǎn)路徑?jīng)]有提示
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    // 配置省略文件路徑的后綴名
    extensions: ['.js', '.json', '.jsx', '.css'],
    // 告訴 webpack 解析模塊是去找哪個(gè)目錄
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  }
};

6.5 dev server

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].js',
    path: resolve(__dirname, 'build')
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development',
  resolve: {
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    extensions: ['.js', '.json', '.jsx', '.css'],
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  },
  devServer: {
    // 運(yùn)行代碼的目錄
    contentBase: resolve(__dirname, 'build'),
    // 監(jiān)視 contentBase 目錄下的所有文件,一旦文件變化就會(huì) reload
    watchContentBase: true,
    watchOptions: {
      // 忽略文件
      ignored: /node_modules/
    },
    // 啟動(dòng)gzip壓縮
    compress: true,
    // 端口號(hào)
    port: 5000,
    // 域名
    host: 'localhost',
    // 自動(dòng)打開(kāi)瀏覽器
    open: true,
    // 開(kāi)啟HMR功能
    hot: true,
    // 不要顯示啟動(dòng)服務(wù)器日志信息
    clientLogLevel: 'none',
    // 除了一些基本啟動(dòng)信息以外,其他內(nèi)容都不要顯示
    quiet: true,
    // 如果出錯(cuò)了,不要全屏提示~
    overlay: false,
    // 服務(wù)器代理 --> 解決開(kāi)發(fā)環(huán)境跨域問(wèn)題
    proxy: {
      // 一旦devServer(5000)服務(wù)器接受到 /api/xxx 的請(qǐng)求,就會(huì)把請(qǐng)求轉(zhuǎn)發(fā)到另外一個(gè)服務(wù)器(3000)
      '/api': {
        target: 'http://localhost:3000',
        // 發(fā)送請(qǐng)求時(shí),請(qǐng)求路徑重寫(xiě):將 /api/xxx --> /xxx (去掉/api)
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
};

6.6 optimization

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin')

module.exports = {
  entry: './src/js/index.js',
  output: {
    filename: 'js/[name].[contenthash:10].js',
    path: resolve(__dirname, 'build'),
    chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'production',
  resolve: {
    alias: {
      $css: resolve(__dirname, 'src/css')
    },
    extensions: ['.js', '.json', '.jsx', '.css'],
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
  },
  optimization: {
    splitChunks: {
      chunks: 'all'
      // 默認(rèn)值,可以不寫(xiě)~
      /* minSize: 30 * 1024, // 分割的chunk最小為30kb
      maxSiza: 0, // 最大沒(méi)有限制
      minChunks: 1, // 要提取的chunk最少被引用1次
      maxAsyncRequests: 5, // 按需加載時(shí)并行加載的文件的最大數(shù)量
      maxInitialRequests: 3, // 入口js文件最大并行請(qǐng)求數(shù)量
      automaticNameDelimiter: '~', // 名稱連接符
      name: true, // 可以使用命名規(guī)則
      cacheGroups: {
        // 分割chunk的組
        // node_modules文件會(huì)被打包到 vendors 組的chunk中。--> vendors~xxx.js
        // 滿足上面的公共規(guī)則,如:大小超過(guò)30kb,至少被引用一次。
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          // 優(yōu)先級(jí)
          priority: -10
        },
        default: {
          // 要提取的chunk最少被引用2次
          minChunks: 2,
          // 優(yōu)先級(jí)
          priority: -20,
          // 如果當(dāng)前要打包的模塊,和之前已經(jīng)被提取的模塊是同一個(gè),就會(huì)復(fù)用,而不是重新打包模塊
          reuseExistingChunk: true
        } 
      }*/
    },
    // 將當(dāng)前模塊的記錄其他模塊的hash單獨(dú)打包為一個(gè)文件 runtime
    // 解決:修改a文件導(dǎo)致b文件的contenthash變化
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}`
    },
    minimizer: [
      // 配置生產(chǎn)環(huán)境的壓縮方案:js和css
      new TerserWebpackPlugin({
        // 開(kāi)啟緩存
        cache: true,
        // 開(kāi)啟多進(jìn)程打包
        parallel: true,
        // 啟動(dòng)source-map
        sourceMap: true
      })
    ]
  }
};
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容