Webpack



webpack是一种前端资源构建工具,一个静态模块打包器(modulebundler)

在webpack看来,前端的所有资源文件(js/json/css/img/less/…)都会作为模块处理

它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)

webpack五个核心概念

  1. Entry
    • 入口(Entry)指示webpack以哪个文件为入口起点开始打包,分析构建内部依赖图
  2. Output
    • 输出(Output)指示webpack打包后的资源bundles输出到哪里去,以及如何命名
  3. Loader
    • Loader让webpack能够去处理那些非JavaScript文件(webpack自身只理解JavaScript)
  4. Plugins
    • 插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等
  5. Mode
    • 模式(Mode)指示webpack使用相应模式的配置

Webpack

初始化配置

  1. 初始化package.json
    • 输入指令:npminit

2.下载并安装webpack

  • 输入指令:
    • npm install webpack webpack-cli -g //全局安装
    • npm install webpack webpack-cli -D

编译打包应用

  • 开发环境:webpack ./src/index.js -o ./build/built.js –mode=development
    • webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js 整体打包环境是开发环境
    • 功能:webpack能够编译打包js和json文件,并且能将es6的模块化语法转换成浏览器能识别的语法
  • 生产环境:webpack ./src/index.js -o ./build/built.js –mode=production
    • webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js,整体打包环境是生产环境
    • 功能:在开发配置功能上多一个功能,压缩代码

webpack开发环境配置

创建配置文件

  • webpack.config.js
    • webpack的配置文件
    • 作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)
    • 所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      // resolve用来拼接绝对路径的方法
      const { resolve } = require('path');

      module.exports = {
      // webpack配置

      // 入口起点
      entry: './src/index.js',

      // 输出
      output: {
      // 输出文件名
      filename: 'built.js',
      // 输出路径
      // __dirname nodejs的变量,代表当前文件的目录绝对路径
      path: resolve(__dirname, 'build')
      },

      // loader的配置
      module: {
      rules: [
      // 详细loader配置



      // 举例
      // 不同文件必须配置不同loader处理
      {
      // 匹配.css后缀的文件
      test: /\.css$/,
      // 使用哪些loader进行处理
      use: [
      // use数组中loader执行顺序:从右到左,从下到上 依次执行
      // 创建style标签,将js中的样式资源插入进行,添加到head中生效
      'style-loader',
      // 将css文件变成commonjs模块加载js中,里面内容是样式字符串
      'css-loader'
      ]
      },
      {
      // 匹配.less后缀的文件
      test: /\.less$/,
      use: [
      'style-loader',
      'css-loader',
      // 将less文件编译成css文件
      // 需要下载 less-loader和less
      'less-loader'
      ]
      }



      ]
      },

      // plugins的配置
      plugins: [
      // 详细plugins的配置
      ],

      // 模式
      mode: 'development', // 开发模式
      }
  • 运行指令:webpack

打包HTML资源

  1. 下载安装plugin包
    • npm install –save-dev html -webpack-plugin
  2. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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
    // 功能:默认会创建一个空的HTML,自动引入打包输出的所有资源(JS/CSS)
    // 需求:需要有结构的HTML文件
    new HtmlWebpackPlugin({
    // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)
    template: './src/index.html'
    })
    ],
    mode: 'development'
    };
  3. 运行指令:webpack

打包图片资源

  1. 下载安装loader包
    • npm install –save-dev html-loader url-loader file-loader
  2. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    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$/,
    // 要使用多个loader处理用use
    use: ['style-loader', 'css-loader', 'less-loader']
    },

    {
    // 问题:默认处理不了html中img图片
    // 处理图片资源
    test: /\.(jpg|png|gif)$/,
    // 使用一个loader
    // 下载 url-loader file-loader

    loader: 'url-loader',
    options: {
    limit: 8 * 1024,
    // 图片小于8kb,就会被base64处理
    // 优点: 减少请求数量(减轻服务器压力)
    // 缺点:图片体积会更大(文件请求速度更慢)
    // base64在本地解码,所以会减少服务器压力,如果图片过大还继续采用base64编码会导致cpu调用率上升,网页加载时变卡

    // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
    // 解析时会出问题:[object Module]
    // 解决:关闭url-loader的es6模块化,使用commonjs解析
    esModule: false,

    // 给图片进行重命名
    // [hash:10]取图片的hash的前10位
    // [ext]取文件原来扩展名
    name: '[hash:10].[ext]'
    }
    },
    {
    test: /\.html$/,
    // 处理html文件的img图片(负责引入img,从而能被url-loader进行处理)
    loader: 'html-loader'
    }
    ]
    },
    plugins: [
    new HtmlWebpackPlugin({
    template: './src/index.html'
    })
    ],
    mode: 'development'
    };
  3. 运行指令:webpack

打包其他资源

  1. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    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资源,剩下的资源用file loader进行处理
    exclude: /\.(css|js|html|less)$/,
    loader: 'file-loader',
    options: {
    name: '[hash:10].[ext]'
    }
    }
    ]
    },
    plugins: [
    new HtmlWebpackPlugin({
    template: './src/index.html'
    })
    ],
    mode: 'development'
    };
  2. 运行指令:webpack

devserver

  • 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
  • 特点:只会在内存中编译打包,不会有任何输出
  • 下载:npm install webpack-dev-server -D //本地下载
  • 启动devServer指令为:npx webpack-dev-server
  1. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    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',


    devServer: {
    // 项目构建后路径
    contentBase: resolve(__dirname, 'build'),
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 3000,
    // 自动打开浏览器
    open: true
    }
    };
  2. 运行指令:npx webpack-dev-server

开发环境配置

  • 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    /*
    开发环境配置:能让代码运行
    运行项目指令:
    webpack 会将打包结果输出出去
    npx webpack-dev-server 只会在内存中编译打包,没有输出
    */

    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: [
    // 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]',
    // 关闭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
    }
    };

webpack生产环境配置

提取js中的css成单独文件

  1. 下载插件
    • npm install –save-dev mini-css-extract-plugin
  2. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-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: [
    // 创建style标签,将样式放入
    // 'style-loader',
    // 这个loader取代style-loader。作用:提取js中的css成单独文件
    MiniCssExtractPlugin.loader,
    // 将css文件整合到js文件中
    'css-loader'
    ]
    }
    ]
    },
    plugins: [
    new HtmlWebpackPlugin({
    template: './src/index.html'
    }),
    new MiniCssExtractPlugin({
    // 对输出的css文件进行重命名
    filename: 'css/built.css'
    })
    ],
    mode: 'development'
    };
  3. 运行指令:webpack

css兼容性处理

  1. 下载loader
    • npm install –save-dev postcss-loader postcss-preset-env
  2. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    const { resolve } = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');

    // 设置nodejs环境变量
    // 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里面的配置,通过配置加载指定的css兼容性样式

    在package.json中添加:
    "browserslist": {
    // 转换到开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
    "development": [
    "last 1 chrome version",
    "last 1 firefox version",
    "last 1 safari version"
    ],
    // 生产环境:默认是看生产环境
    "production": [
    ">0.2%", 兼容大于99.8%的浏览器
    "not dead", 不用已经消失的浏览器
    "not op_mini all" 不用op_mini浏览器
    ]
    }
    */

    // 使用loader的默认配置
    // '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'
    };
  3. 修改package.json
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    在package.json中添加:
    "browserslist": {
    // 转换到开发环境 --> 设置node环境变量:process.env.NODE_ENV = development
    "development": [
    "last 1 chrome version",
    "last 1 firefox version",
    "last 1 safari version"
    ],
    // 生产环境:默认是看生产环境
    "production": [
    ">0.2%", 兼容大于99.8%的浏览器
    "not dead", 不用已经消失的浏览器
    "not op_mini all" 不用op_mini浏览器
    ]
    }
  4. 运行指令:webpack

压缩css

  1. 下载安装包
    • npm install –save-dev optimize-css-assets-webpack-plugin
  2. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    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')

    // 设置nodejs环境变量
    // 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'
    };
  3. 运行指令:webpack

js语法检查

  1. 下载安装包
    • npm install –save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import
  2. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    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: [
    /*
    语法检查: eslint-loader eslint
    注意:只检查自己写的源代码,第三方的库是不用检查的
    设置检查规则:
    package.json中eslintConfig中设置~
    "eslintConfig": {
    "extends": "airbnb-base"
    }
    airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint
    */
    {
    test: /\.js$/,
    exclude: /node_modules/, //排除node_modules
    loader: 'eslint-loader',
    options: {
    // 自动修复eslint的错误
    fix: true
    }
    }
    ]
    },
    plugins: [
    new HtmlWebpackPlugin({
    template: './src/index.html'
    })
    ],
    mode: 'development'
    };
  3. 修改package.json
    1
    2
    3
    4
    5
    6
    7
    8
    设置检查规则:
    //package.json中eslintConfig中设置~
    "eslintConfig": {
    "extends": "airbnb-base",//使用airbnb-base的代码风格
    "env": {
    "browser": true
    }
    },

js兼容性处理

  1. 下载安装包
    • npm install –save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
  2. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    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
    1. 基本js兼容性处理 --> @babel/preset-env
    问题:只能转换基本语法,如promise高级语法不能转换
    2. 全部js兼容性处理 --> @babel/polyfill
    问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了~
    3. 需要做兼容性处理的就做:按需加载 --> core-js
    */
    {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
    // 预设:指示babel做怎么样的兼容性处理
    presets: [
    [
    '@babel/preset-env',
    {
    // 按需加载
    useBuiltIns: 'usage',

    // 指定core-js版本
    corejs: {
    version: 3
    },

    // 指定兼容性做到哪个版本浏览器
    targets: {
    chrome: '60',
    firefox: '60',
    ie: '9',
    safari: '10',
    edge: '17'
    }
    }
    ]
    ]
    }
    }
    ]
    },
    plugins: [
    new HtmlWebpackPlugin({
    template: './src/index.html'
    })
    ],
    mode: 'development'
    };
  3. 运行指令:webpack

js压缩

  1. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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'
    })
    ],
    // 生产环境下会自动压缩js代码
    mode: 'production'
    };
  2. 运行指令:webpack

html压缩

  1. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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'
    };
  2. 运行指令:webpack

生产环境配置

  1. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    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环境变量:决定使用browserslist的哪个环境
    process.env.NODE_ENV = 'production';

    // 复用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]
    },
    {
    test: /\.less$/,
    use: [...commonCssLoader, 'less-loader']
    },
    /*
    正常来讲,一个文件只能被一个loader处理。
    当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
    先执行eslint 在执行babel
    */
    {
    // 在package.json中eslintConfig --> airbnb
    test: /\.js$/,
    exclude: /node_modules/,
    // 优先执行
    enforce: 'pre',
    loader: 'eslint-loader',
    options: {
    fix: true
    }
    },
    {
    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'
    };
  2. 运行指令:webpack

webpack优化环境配置

  1. 开发环境性能优化

    • 优化打包构建速度
      • HMR
    • 优化代码调试
      • source-map
  2. 生产环境性能优化

    • 优化打包构建速度
      • oneOf
      • babel缓存
      • 多进程打包
      • externals
      • dll
    • 优化代码运行的性能
      • 缓存(hash-chunkhash-contenthash)
      • tree shaking
      • code split
      • 懒加载/预加载
      • pwa

HMR

  • HMR: hot module replacement 热模块替换 / 模块热替换

    • 作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块) 极大提升构建速度
  • css文件:可以使用HMR功能:因为style-loader内部实现了~

  • js文件:默认不能使用HMR功能 –> 需要修改js代码,添加支持HMR功能的代码

    • 注意:HMR功能对js的处理,只能处理非入口js文件的其他文件
  • html文件: 默认不能使用HMR功能.同时会导致问题:html文件不能热更新了(不用做HMR功能)

    • 解决:修改entry入口,将html文件引入
  1. 修改配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    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]',
    // 关闭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,
    // 开启HMR功能
    // 当修改了webpack配置,新配置要想生效,必须重新webpack服务
    hot: true
    }
    };
  2. js文件处理
    1
    2
    3
    4
    5
    6
    7
    8
    if (module.hot) {
    // 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
    module.hot.accept('./print.js', function() {
    // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
    // 会执行后面的回调函数
    print();
    });
    }
  3. 运行指令:webpack

source-map

  • source-map: 一种 提供源代码到构建后代码映射 技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)

  • 内联 和 外部的区别:

    1. 外部生成了文件,内联没有
    2. 内联构建速度更快
  • inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

    • source-map:外部
      • 错误代码准确信息 和 源代码的错误位置
    • inline-source-map:内联
      • 只生成一个内联source-map
      • 错误代码准确信息 和 源代码的错误位置
    • hidden-source-map:外部
      • 错误代码错误原因,但是没有错误位置
      • 不能追踪源代码错误,只能提示到构建后代码的错误位置
    • eval-source-map:内联
      • 每一个文件都生成对应的source-map,都在eval
      • 错误代码准确信息 和 源代码的错误位置
    • nosources-source-map:外部
      • 错误代码准确信息, 但是没有任何源代码信息
    • cheap-source-map:外部
      • 错误代码准确信息 和 源代码的错误位置
      • 只能精确到行
    • cheap-module-source-map:外部
      • 错误代码准确信息 和 源代码的错误位置
      • module会将loader的source map加入
  • 开发环境:速度快,调试更友好

    • 速度快(eval>inline>cheap>…)
      • eval-cheap-souce-map
      • eval-source-map
    • 调试更友好
      • souce-map
      • cheap-module-souce-map
      • cheap-souce-map
    • 平衡性好
      • eval-source-map / eval-cheap-module-souce-map
  • 生产环境:源代码要不要隐藏? 调试要不要更友好

    • 内联会让代码体积变大,所以在生产环境不用内联
    • nosources-source-map 全部隐藏
    • hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
    • 平衡性
      • source-map / cheap-module-souce-map
  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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的配置
    ]
    },
    plugins: [
    // plugins的配置
    new HtmlWebpackPlugin({
    template: './src/index.html'
    })
    ],
    mode: 'development',
    devServer: {
    contentBase: resolve(__dirname, 'build'),
    compress: true,
    port: 3000,
    open: true,
    hot: true
    },
    // source -map 配置
    devtool: 'eval-source-map'
    };
  2. 运行指令:webpack

oneof

  • 提升构建速度,把所有的loader都放进oneof里就不会加载多次loader了
  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    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环境变量:决定使用browserslist的哪个环境
    process.env.NODE_ENV = 'production';

    // 复用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/,
    // 优先执行
    enforce: 'pre',
    loader: 'eslint-loader',
    options: {
    fix: true
    }
    },
    {
    // 以下loader只会匹配一个
    // 注意:不能有两个配置处理同一种类型文件,所以在js语法检查和兼容性问题时,需要将其中一个loader拿到oneof外面
    oneOf: [
    {
    test: /\.css$/,
    use: [...commonCssLoader]
    },
    {
    test: /\.less$/,
    use: [...commonCssLoader, 'less-loader']
    },
    /*
    正常来讲,一个文件只能被一个loader处理。
    当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
    先执行eslint 在执行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'
    };

    缓存

  • babel缓存
    • cacheDirectory: true
    • –> 让第二次打包构建速度更快
  • 文件资源缓存
    • hash: 每次wepack构建时会生成一个唯一的hash值。
      • 问题: 因为js和css同时使用一个hash值。
        • 如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
    • chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
      • 问题: js和css的hash值还是一样的
        • 因为css是在js中被引入的,所以同属于一个chunk
    • contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样
    • –> 让代码上线运行缓存更好使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';

// 复用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/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{

oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},

{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
],
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
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'
};

tree shaking

  • tree shaking:去除无用代码

    • 前提:
      1. 必须使用ES6模块化
      2. 开启production环境
    • 作用: 减少代码体积
  • 在package.json中配置

    • “sideEffects”: false 所有代码都没有副作用(都可以进行tree shaking)
    • 问题:可能会把css / @babel/polyfill (副作用)文件干掉
      “sideEffects”: [“.css”, “.less”]
  1. 配置package.json文件
    1
    2
    3
    "sideEffects": [
    "*.css"
    ]

code split

  • 将一个大的js文件分割成几个小文件,这样就可以实现按需加载
  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    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中代码单独打包一个chunk最终输出
    2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
    */
    optimization: {
    splitChunks: {
    chunks: 'all'
    }
    },
    mode: 'production'
    };

懒加载和预加载

1
2
3
4
5
6
7
8
9
document.getElementById('btn').onclick = function() {
// 懒加载:当文件需要使用时才加载
// 预加载 prefetch:会在使用之前,提前加载js文件
// 正常加载可以认为是并行加载(同一时间加载多个文件)
// 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
console.log(mul(4, 5));
});
};

pwa

  • 渐进式网络开发应用程序(离线可访问)
    • workbox –> workbox-webpack-plugin
  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    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');


    // 定义nodejs环境变量:决定使用browserslist的哪个环境
    process.env.NODE_ENV = 'production';


    module.exports = {
    entry: './src/js/index.js',
    output: {
    filename: 'js/built.[contenthash:10].js',
    path: resolve(__dirname, 'build')
    },

    module: {
    rules: [

    ]
    },
    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快速启动
    2. 删除旧的 serviceworker

    生成一个 serviceworker 配置文件~
    */
    clientsClaim: true,
    skipWaiting: true
    })
    ],
    mode: 'production',
    devtool: 'source-map'
    };
  2. 修改入口js文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    */
    1. eslint不认识 window、navigator全局变量
    解决:需要修改package.json中eslintConfig配置
    "env": {
    "browser": true // 支持浏览器端全局变量
    }
    2. sw代码必须运行在服务器上
    --> nodejs
    -->
    npm i serve -g
    serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
    */
    // 注册serviceWorker
    // 处理兼容性问题
    if ('serviceWorker' in navigator) {
    window.addEventListener('load', () => {
    navigator.serviceWorker
    .register('/service-worker.js')
    .then(() => {
    console.log('sw注册成功了~');
    })
    .catch(() => {
    console.log('sw注册失败了~');
    });
    });
    }

多进程打包

  • thread-loader
  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    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');

    // 定义nodejs环境变量:决定使用browserslist的哪个环境
    process.env.NODE_ENV = 'production';

    // 复用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/,
    // 优先执行
    enforce: 'pre',
    loader: 'eslint-loader',
    options: {
    fix: true
    }
    },
    {
    // 以下loader只会匹配一个
    // 注意:不能有两个配置处理同一种类型文件
    oneOf: [
    {
    test: /\.css$/,
    use: [...commonCssLoader]
    },
    {
    test: /\.less$/,
    use: [...commonCssLoader, 'less-loader']
    },
    /*
    正常来讲,一个文件只能被一个loader处理。
    当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
    先执行eslint 在执行babel
    */
    {
    test: /\.js$/,
    exclude: /node_modules/,
    use: [
    /*
    开启多进程打包。
    进程启动大概为600ms,进程通信也有开销。
    只有工作消耗时间比较长,才需要多进程打包
    */
    {
    loader: 'thread-loader',
    options: {
    workers: 2 // 进程2个
    }
    },
    {
    loader: 'babel-loader',
    options: {
    presets: [
    [
    '@babel/preset-env',
    {
    useBuiltIns: 'usage',
    corejs: { version: 3 },
    targets: {
    chrome: '60',
    firefox: '50'
    }
    }
    ]
    ],
    // 开启babel缓存
    // 第二次构建时,会读取之前的缓存
    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快速启动
    2. 删除旧的 serviceworker

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

externals

  • 防止将某些包打包到最终输出的文件中
  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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被打包进来
    jquery: 'jQuery'
    }
    };

dll

  • 动态链接库,类似于externals,不同的是dll会将多个库单独的打包,将多个库打包成一个chunk
  1. 配置dll.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    /*
    使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
    当你运行 webpack 时,默认查找 webpack.config.js 配置文件
    需求:需要运行 webpack.dll.js 文件
    --> webpack --config webpack.dll.js
    */

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

    module.exports = {
    entry: {
    // 最终打包生成的[name] --> jquery
    // ['jquery'] --> 要打包的库是jquery
    jquery: ['jquery'],
    },
    output: {
    filename: '[name].js',
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
    },
    plugins: [
    // 打包生成一个 manifest.json --> 提供和jquery映射
    new webpack.DllPlugin({
    name: '[name]_[hash]', // 映射库的暴露的内容名称
    path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
    })
    ],
    mode: 'production'
    };
  1. 配置config文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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哪些库不参与打包,同时使用时的名称也得变~
    new webpack.DllReferencePlugin({
    manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    // 将某个文件打包输出去,并在html中自动引入该资源
    new AddAssetHtmlWebpackPlugin({
    filepath: resolve(__dirname, 'dll/jquery.js')
    })
    ],
    mode: 'production'
    };
  2. 运行指令:webpack

webpack配置详解

entry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

/*
entry: 入口起点
1. string --> './src/index.js'
单入口
打包形成一个chunk。 输出一个bundle文件。
此时chunk的名称默认是 main
2. array --> ['./src/index.js', './src/add.js']
多入口
所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
--> 只有在HMR功能中让html热更新生效~
3. object
多入口
有几个入口文件就形成几个chunk,输出几个bundle文件
此时chunk的名称是 key

--> 特殊用法
{
// 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
index: ['./src/index.js', './src/count.js'],
// 形成一个chunk,输出一个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'
};

output

  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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'),
    // 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
    publicPath: '/',
    chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名称
    // library: '[name]', // 整个库向外暴露的变量名
    // libraryTarget: 'window' // 变量名添加到哪个属性上 browser
    // libraryTarget: 'global' // 变量名添加到哪个属性上 node
    // libraryTarget: 'commonjs'
    },
    plugins: [new HtmlWebpackPlugin()],
    mode: 'development'
    };

module

  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    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$/,
    // 多个loader用use
    use: ['style-loader', 'css-loader']
    },
    {
    test: /\.js$/,
    // 排除node_modules下的js文件
    exclude: /node_modules/,
    // 只检查 src 下的js文件
    include: resolve(__dirname, 'src'),
    // 优先执行
    enforce: 'pre',
    // 延后执行
    // enforce: 'post',
    // 单个loader用loader
    loader: 'eslint-loader',
    options: {}
    },
    {
    // 以下配置只会生效一个
    oneOf: []
    }
    ]
    },
    plugins: [new HtmlWebpackPlugin()],
    mode: 'development'
    };

resolve

  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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'],
    // 告诉 webpack 解析模块是去找哪个目录
    modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
    }
    };

    dev server

  2. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    devServer: {
    // 运行代码的目录
    contentBase: resolve(__dirname, 'build'),
    // 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
    watchContentBase: true,
    watchOptions: {
    // 忽略文件
    ignored: /node_modules/
    },
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 5000,
    // 域名
    host: 'localhost',
    // 自动打开浏览器
    open: true,
    // 开启HMR功能
    hot: true,
    // 不要显示启动服务器日志信息
    clientLogLevel: 'none',
    // 除了一些基本启动信息以外,其他内容都不要显示
    quiet: true,
    // 如果出错了,不要全屏提示~
    overlay: false,
    // 服务器代理 --> 解决开发环境跨域问题
    proxy: {
    // 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
    '/api': {
    target: 'http://localhost:3000',
    // 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
    pathRewrite: {
    '^/api': ''
    }
    }
    }
    }

optimization

  1. 配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    optimization: {



    splitChunks: {
    chunks: 'all'
    // 默认值,可以不写~
    /* minSize: 30 * 1024, // 分割的chunk最小为30kb
    maxSiza: 0, // 最大没有限制
    minChunks: 1, // 要提取的chunk最少被引用1次
    maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
    maxInitialRequests: 3, // 入口js文件最大并行请求数量
    automaticNameDelimiter: '~', // 名称连接符
    name: true, // 可以使用命名规则
    cacheGroups: {
    // 分割chunk的组
    // node_modules文件会被打包到 vendors 组的chunk中。--> vendors~xxx.js
    // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
    vendors: {
    test: /[\\/]node_modules[\\/]/,
    // 优先级
    priority: -10
    },
    default: {
    // 要提取的chunk最少被引用2次
    minChunks: 2,
    // 优先级
    priority: -20,
    // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
    reuseExistingChunk: true
    }
    }*/
    },
    // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
    // 解决:修改a文件导致b文件的contenthash变化



    runtimeChunk: {
    name: entrypoint => `runtime-${entrypoint.name}`
    },



    minimizer: [
    // 配置生产环境的压缩方案:js和css
    new TerserWebpackPlugin({
    // 开启缓存
    cache: true,
    // 开启多进程打包
    parallel: true,
    // 启动source-map
    sourceMap: true
    })
    ]
    }

webpack5(正在开发中 71%)

此版本重点关注以下内容:

  • 通过持久缓存提高构建性能.
  • 使用更好的算法和默认值来改善长期缓存.
  • 通过更好的树摇和代码生成来改善捆绑包大小.
  • 清除处于怪异状态的内部结构,同时在 v4 中实现功能而不引入任何重大更改.
  • 通过引入重大更改来为将来的功能做准备,以使我们能够尽可能长时间地使用 v5.

下载

  • npm i webpack@next webpack-cli -D

自动删除 Node.js Polyfills

  • 早期,webpack 的目标是允许在浏览器中运行大多数 node.js 模块,但是模块格局发生了变化,许多模块用途现在主要是为前端目的而编写的。
    webpack <= 4 附带了许多 node.js 核心模块的 polyfill,一旦模块使用任何核心模块(即 crypto 模块),这些模块就会自动应用。

  • 尽管这使使用为 node.js 编写的模块变得容易,但它会将这些巨大的 polyfill 添加到包中。在许多情况下,这些 polyfill 是不必要的。

  • webpack 5 会自动停止填充这些核心模块,并专注于与前端兼容的模块。

迁移:

  • 尽可能尝试使用与前端兼容的模块。
  • 可以为 node.js 核心模块手动添加一个 polyfill。错误消息将提示如何实现该目标。

Chunk 和模块 ID

  • 添加了用于长期缓存的新算法。在生产模式下默认情况下启用这些功能。

chunkIds: "deterministic", moduleIds: "deterministic"

Chunk ID

  • 你可以不用使用 import(/* webpackChunkName: "name" */ "module") 在开发环境来为 chunk 命名,生产环境还是有必要的

  • webpack 内部有 chunk 命名规则,不再是以 id(0, 1, 2)命名了

Tree Shaking

  1. webpack 现在能够处理对嵌套模块的 tree shaking
1
2
3
4
5
6
7
8
9
10
11
// inner.js
export const a = 1;
export const b = 2;

// module.js
import * as inner from './inner';
export { inner };

// user.js
import * as module from './module';
console.log(module.inner.a);
  • 在生产环境中, inner 模块暴露的 b 会被删除
  1. webpack 现在能够多个模块之前的关系
1
2
3
4
5
6
7
8
9
import { something } from './something';

function usingSomething() {
return something;
}

export function test() {
return usingSomething();
}
  • 当设置了"sideEffects": false时,一旦发现test方法没有使用,不但删除test,还会删除"./something"
  1. webpack 现在能处理对 Commonjs 的 tree shaking

Output

  • webpack 4 默认只能输出 ES5 代码

  • webpack 5 开始新增一个属性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代码.

  • 如:output.ecmaVersion: 2015

SplitChunk

1
2
// webpack4
minSize: 30000;
1
2
3
4
5
// webpack5
minSize: {
javascript: 30000,
style: 50000,
}

Caching

1
2
3
4
5
6
7
8
9
// 配置缓存
cache: {
// 磁盘存储
type: "filesystem",
buildDependencies: {
// 当配置修改时,缓存失效
config: [__filename]
}
}

-缓存将存储到 node_modules/.cache/webpack

监视输出文件

  • 之前 webpack 总是在第一次构建时输出全部文件,但是监视重新构建时会只更新修改的文件。

  • 此次更新在第一次构建时会找到输出文件看是否有变化,从而决定要不要输出全部文件。

默认值

  • entry: "./src/index.js
  • output.path: path.resolve(__dirname, "dist")
  • output.filename: "[name].js"

更多内容

https://github.com/webpack/changelog-v5