webpack5

开发环境的基本配置

  1. 当我们使用less等预处理器开发样式 浏览器并不能识别、解析less文件 此时我们需要借助一个工具 将less编译成css 当我们开发js文件时 我们需要用一些ES6或更高的语法 很有可能浏览器不认识这些语法 此时我们也需要一种工具 将浏览器不能识别的语法编译成浏览器能识别的语法 可能将来你还写了一些其他东西 这些东西需要其他东西进行编译处理 上述这些小工具我们需要一个个维护 很麻烦 于是前端提出了一个概念“构建工具”
  2. 构建工具:我用一个大工具将小工具都包含进来 我只用关心大工具如何使用就行了 这个大工具就是构建工具 也就是说 构建工具将我们前端要做的一系列小的操作整合成一个大的工具
  3. webpack就是构建工具的一种
    在这里插入图片描述
    我们会在一个文件中(一般是index.js中)引入整个模块所需要的所有资源 我们首先要告诉webpack打包的起点 也就是入口文件 也就是index.js webpack会将index.js中的每个依赖都记录好 形成依赖关系树状图 然后他会通过依赖关系图的先后顺序依次把资源引进来形成chunk(代码块) 再对chunk进行各项处理 如将less编译成css 将js资源编译成浏览器能识别的语法等 这一过程称为打包 打包后 将这些处理好的资源输出出去 输出的东西叫bundle index.js jquery less等称为静态模块 经打包后生成的bundle(静态资源)就可以在浏览器上平稳运行了 所以webpack被称为静态模块打包器
  4. 核心概念
    (1)入口(Entry)指示webpack以哪个文件为入口起点开始打包,分析构建内部依赖图。我们写的项目有很多个文件 你得告诉webpack该从哪个文件开始打包
    (2)输出(Output)指示webpack打包后的资源bundles输出到哪里去,以及如何命名
    (3)loader让webpack能够去处理那些非JS文件(webpack自身只理解JS)
    (4)插件(Plugins)可以用于执行范围更广的任务。插件的范围包括从打包优化和压缩一直到重新定义环境中的变量等。
    Loader只能翻译 一些强大得功能由Plugins提供
    (5)Mode
    在这里插入图片描述
  5. 运行webpack:webpack
  6. loader:webpack只能理解JS和JSON文件 通过loader让webpack能够处理其他类型的文件 并将它们转换为有效模块 以供应用程序使用 以及被添加到依赖图中
  7. plugin:用于解决loader无法实现的其他事 可用于执行范围更广的任务 为webpack带来很大的灵活性

webpack初体验

  1. 打开终端npm init 初始化包描述文件package.json package name写你自定义的包名 本文为webpack-test 其余全用默认值
  2. 在终端继续输入npm i webpack webpack-cli -g 安装webpack

webpack-cli可以让我们通过指令去使用webpack中的功能 所以也要安装
-g:全局安装

  1. 在终端继续输入npm install webpack webpack-cli -D 开发依赖

-D:将webpack添加到开发依赖 等价于–sever -dev

  1. 建议下载 安装都在项目上(最外层文件)进行 在相应的文件上运行
  2. src代表代码源文件夹目录 此文件夹下有一个index.js文件作为webpack的起点文件 build代表代码经webpack打包后输出的目录
  3. index.js代码
/*
写好index.js后 在终端输入运行指令 如下
  1. 运行指令:
    开发环境:webpack ./src/index.js -o ./build/built.js --mode=development
      webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js
      整体打包环境,是开发环境
    生产环境:webpack ./src/index.js -o ./build/built.js --mode=production
      webpack会以 ./src/index.js 为入口文件开始打包,打包后输出到 ./build/built.js
      整体打包环境,是生产环境

   2. 结论:
    1. webpack能处理js/json资源,不能处理css/img等其他资源
    2. 生产环境和开发环境将ES6模块化编译成浏览器能识别的模块化~
    3. 生产环境比开发环境多一个压缩js代码。
   
   3. 问题
	1. 不能编译打包 css、img 等文件。
	2. 不能将 js 的 es6 基本语法转化为 es5 以下语法。 
*/
// import './index.css';// webpack不能处理css/img等其他资源 引入这个会报错 不能进行打包

import data from './data.json';// webpack能处理js/json资源
console.log(data);

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

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

打包样式资源:less-loader css-loader style-loader

  1. 使用loader来处理非js文件 在配置文件webpack.config.js中配置loader
  2. 注意src文件夹中存放的是源代码 用的ES6模块 webpack.config.js基于nodejs 使用的是commonJS
  3. webpack.config.js代码:
/*
  webpack.config.js  webpack的配置文件
    作用: 指示 webpack 干哪些活(当你运行 webpack 指令时,会加载里面的配置)

    所有构建工具都是基于nodejs平台运行的~模块化默认采用commonjs。
*/

// resolve用来拼接绝对路径的方法 使用绝对路径 需要引入node的path模块
const { resolve } = require('path');

module.exports = {// 通过module.exports暴露
  // 在这里面写webpack配置
  // 入口起点
  entry: './src/index.js',
  // 输出
  output: {
    // 输出文件名
    filename: 'built.js',
    // 输出路径
    // __dirname nodejs的变量,代表当前文件的目录绝对路径
    // 在这里是C:\Users\GFQ\Documents\webpack资料\代码\2.webpack开发环境配置\03.打包样式资源
    path: resolve(__dirname, 'build')
    // 输出到C:\Users\GFQ\Documents\webpack资料\代码\2.webpack开发环境配置\03.打包样式资源\bulid文件夹下的built.js中
  },
  // loader的配置
  module: {
    rules: [
      // 详细loader配置
      // 不同文件必须配置不同loader处理
      {
        // 匹配哪些文件
        test: /\.css$/,
        // 使用哪些loader进行处理
        use: [// 这里面的文件都是需要下载的npm install xx -D
          // use数组中loader执行顺序:从右到左,从下到上 依次执行
          // 创建style标签,将js中的样式资源插入进行,添加到head中生效
          'style-loader',
          // 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
          'css-loader'
          // 当webpack遇到一个.css结尾的文件 会使用css-loader将其转换为commonjs模块(里面内容是字符串)并加载到js中
          // 然后使用style-loader创建style标签 将前面那个commonjs模块插入到style标签中 将style标签添加到head标签中 让其生效
        ]
      },
      {// 配置less文件的loader
        test: /\.less$/,
        use: [
          'style-loader',
          'css-loader',
          // 将less文件编译成css文件
          // 需要下载 less-loader和less:npm install less-loader less -D
          'less-loader'
        ]
      }
    ]
  },
  // plugins的配置
  plugins: [
    // 详细plugins的配置
  ],
  // 模式
  mode: 'development', // 开发模式
  // mode: 'production'
}
// 写好之后再终端使用上面的运行指令运行一下就可以了

打包HTML资源:html-webpack-plugin

  1. 使用插件打包HTML资源
  2. webpack.config.js代码:
/*
  loader: 1. 下载   2. 使用(配置loader)
  plugins: 1. 下载  2. 引入  3. 使用
*/
const { resolve } = require('path');
// 下载:npm i html-webpack-plugin -D
const HtmlWebpackPlugin = require('c');// 引入

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文件:配置template属性
    new HtmlWebpackPlugin({
      // 复制 './src/index.html' 文件,并自动引入打包输出的所有资源(JS/CSS)built.js
      template: './src/index.html'// 要引入的HTML文件
      // 我给了插件一个路径./src/index.html 他会自己去找这个文件 然后把这个文件复制进来 
      // 然后输出去 在输出前 会将打包输出的所有资源built.js引入进来 
      // 如果是js文件 就是用script标签引进来 如果是css文件 就通过link标签引进来
    })
  ],
  mode: 'development'
};

打包后的index.html文件如下所示 会多一个script标签
在这里插入图片描述

打包图片资源:url-loader html-loader

webpack.config.js代码:

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图片 在HTML中用img标签引入的图片 默认处理不了 因为他根本无法解析html
        // 处理图片资源
        test: /\.(jpg|png|gif)$/,
        // 下载 url-loader file-loader
        loader: 'url-loader',
        options: {// 使用options可以配置这个loader
          // url-loader在打包图片时 并不是原封不动的输出
          // 他发现图片大小小于8kb(根据limit属性设置的),图片就会被base64处理
          // 优点: 减少请求数量(减轻服务器压力)
          // 缺点:图片体积会更大(文件请求速度更慢)所以我们一般只对8-12kb左右的图片进行base64处理
          limit: 8 * 1024,// 8kb
          // 问题:因为url-loader默认使用es6模块化解析,而html-loader引入图片是commonjs
          // 解析时会出问题:img标签的src=[object Module]
          // 解决:关闭url-loader的es6模块化,使用commonjs解析
          esModule: false,
          // 使用webpack打包后的图片名称是根据图片内容生成的哈希值 很长 所以我们可以给图片进行重命名
          // [hash:10]取图片的hash的前10位
          // [ext]取文件原来扩展名
          name: '[hash:10].[ext]'
        }
      },
      {
        test: /\.html$/,
        // 处理html文件的img图片(html-loader负责引入img(使用commonjs引入),从而能被url-loader进行处理)
        loader: 'html-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};
// 我们发现 一张图片在页面中重复使用 但使用webpack打包后只生成了一张图片
// 所以webpack在解析时发现我们使用了同一个文件 他不会重复打包这个文件 而只输出一次

打包其他资源:file-loader

  1. 其他资源:我们不需要做任何处理 只要原封不动的输出出去就行
  2. webpack.config.js代码:
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/less资源 将前面处理过的资源都排除掉 其余资源都在这里处理
        exclude: /\.(css|js|html|less)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};

devServe

  1. 我们在写代码的时候 写好一部分代码后想看看效果 需要将代码打包然后运行 然后继续写代码 又要重新打包然后运行 每次改了代码都要重新打包 重新运行 很麻烦 我们可以使用devServer来自动打包运行
  2. webpack.config.js代码:
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/less资源
        exclude: /\.(css|js|html|less)$/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]'
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development',
  
  // 开发服务器 devServer:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器~~)
  // 特点:只会在内存中编译打包,不会有任何输出
  // 启动devServer指令为:npx webpack-dev-server
  devServer: {
    // 项目构建后路径
    contentBase: resolve(__dirname, 'build'),
    // 启动gzip压缩
    compress: true,
    // 端口号
    port: 3000,
    // 自动打开浏览器
    open: true
  }
};

开发环境基本配置

  1. 对前面的配置做一个汇总 webpack.config.js代码:
/*
  开发环境配置:能让代码运行
    运行项目指令:
      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打包后的文件也像源文件一样分级 可以在每个loader下配置自己的outputPath属性

生产环境的基本配置

生产环境做的事:
(1)css文件经过之前的处理整合到了js文件中 这会使得js文件体积特别大 下载变慢 因为是先加载Js文件 然后创建style标签 将css文件插入进去 会出现闪屏 所以我们需要将将css从js文件中提取出来
(2)压缩代码
(3)处理css代码和js代码的兼容问题
等等

提取 css 成单独文件:mini-css-extract-plugin

  1. 在终端输入npm i mini-css-extract-plugin -D安装插件并在 webpack.config.js中引入const MiniCssExtractPlugin = require('mini-css-extract-plugin');
  2. 我们知道 经webpack.config.js处理后CSS文件就在JS文件中了style-loader会创建一个style标签 将处理后的CSS文件插入到页面上 但是我们现在要让CSS文件分离出来 成为一个单独的文件 所以我们要用MiniCssExtractPlugin.loader取代style-loader
  3. webpack.config.js中的代码
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'// 如果不配置这个属性 那么css文件会被提出到build文件夹的main.css中
      // 我们对其重命名是希望提取后的css文件依旧存在层级关系
    })
  ],
  mode: 'development'
};

运行后发现build文件夹下会多一个css文件夹 里面就有打包的built.css文件 其实我们源代码中有好几个css文件 但最终都打包到了built.css中 在打包后的index.html文件中 link标签将打包好的CSS文件引入进来了
在这里插入图片描述

CSS兼容性处理:postcss-loader postcss-preset-env

  1. 使用postcss实现CSS兼容性处理 要想在webpack中使用postcss需要postcss-loader和postcss-preset-env 所以在终端:npm i postcss-loader postcss-preset-env -D下载这两个库
  2. 使用:
    (1)使用loader的默认配置 在use中输入:'postcss-loader'
    (2)在options中修改loader的配置 引入postcss-preset-env插件
  3. postcss-preset-env:帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式
  4. 因为我们下载库都是在外面下载的 所以package.json也在外面 文件层级关系如下:
    在这里插入图片描述
  5. 配置package.json中的browserslist package.json中的代码如下
    在这里插入图片描述
    更多配置详见GitHub上browserslist的仓库
  6. 默认情况下 是根据生产环境production的配置进行兼容 不管你配置的mode是啥 如果想根据开发环境的配置进行兼容 需要添加一行代码process.env.NODE_ENV = 'development';
  7. webpack.config.js中的代码
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-preset-env:帮postcss找到package.json中browserslist里面的配置,通过配置加载指定的css兼容性样式

            "browserslist": {
              // 开发环境 --> 要想以开发环境来开发 设置node环境变量:process.env.NODE_ENV = development
              "development": [
                "last 1 chrome version",// 兼容最新的浏览器的版本
                "last 1 firefox version",
                "last 1 safari version"
              ],
              // 生产环境:默认是看生产环境 跟我们下面配置的mode: 'development'没关系
              "production": [
                ">0.2%",// 大于99.8%的浏览器
                "not dead",// 不要已经死掉的浏览器 比如IE10
                "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'
};

压缩CSS:css-loader optimize-css-assets-webpack-plugin

  1. 在终端输入npm i optimize-css-assets-webpack-plugin -D下载并引入
  2. webpack.config.js中的代码
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'
};

JS语法检查eslint:eslint-config-airbnb-base eslint-plugin-import eslint

  1. 语法检查:我们在团队工作时 我们希望团队里面每个人写的代码风格相似 这时我们就可以用js语法检查 它可以让你的代码写的更规范 同时他还能检查常见的语法错误
  2. 使用eslint进行语法检查 在webpack中eslint依赖eslint-loader 所以我们需要下载这两个包
  3. 通过在package.json中配置eslintConfig来设置检查规则 推荐使用airbnb规则 airbnb:JS代码风格指南 在GitHub上可以查看详情
  4. 在npm官网查询eslint 发现:
    (1)eslint-config-airbnb-base
    (2)eslint-config-airbnb
    他们都是将airbnb应用于eslint的库 二者的区别是eslint-config-airbnb-base不包含react的插件 其中eslint-config-airbnb-base包含eslint-config-airbnb-legacy(包含ES5及以下)和eslint-config-airbnb-base(包含ES6及以上 ) 综合来看 本文使用eslint-config-airbnb-base
  5. 要想使用eslint-config-airbnb-base 需要下载两个库:eslint-plugin-import eslint 所以我们一共需要下载三个库:eslint-config-airbnb-base eslint-plugin-import eslint 在终端输入npm i eslint-config-airbnb-base eslint-plugin-import eslint -D安装
  6. package.json中配置eslintConfig:
"eslintConfig": {
	"extends": "airbnb-base"// // 继承airbnb-base提供的eslint语法检查
	"env": {
      "browser": true// 支持浏览器全局变量
      // eslint并不认识浏览器的语法 但浏览器一定会有window 所以还需要配置这个属性
    }
}
  1. webpack.config.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: [
      {
        test: /\.js$/,
        // 注意:只检查自己写的源代码,第三方的库是不用检查的
        exclude: /node_modules/,// 排除第三方的库 第三方的库是不用检查 他们已经检查过了 只检查我们自己写的源代码就行
        loader: 'eslint-loader',
        options: {
          // 自动修复eslint的错误 即自动修复我们的js语法错误
          fix: true
        }
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ],
  mode: 'development'
};
  1. 可以在js文件中写eslint-disable-next-line 让下一行eslint所有规则都失效 也就是说下一行不进行eslint检查 在调试时可以加这一行 代码要发布上线千万不能写这一句
  2. eslint检测流程也是需要将源码转换为 AST,然后再利用 AST 来检查代码规范化的问题。

JS兼容性处理babel

  1. 使用babel进行兼容性处理 在webpack中想使用babel需要安装babel-loader和@babel/core 此外我们需要@babel/preset-env对预设环境进行兼容性处理 在终端输入npm i babel-loader @babel/core @babel/preset-env -D下载这三个包 rules中的代码如下
{
	test: /\.js$/,
	exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
    // 预设:指示babel做怎么样的兼容性处理
    	presets: ['@babel/preset-env']
    }
}
  1. 现在我们只能对基本的JS语法进行兼容 我们可以使用@babel/polyfill对所有的JS语法进行兼容性处理 在终端输入npm i@babel/polyfill -D下载 在JS文件中引入这个包import '@babel/polyfill';就可以使用了 webpack.config.js中的内容同上
  2. 但是@babel/polyfill会将与JS兼容性有关的东西全部引进来 但我只需要解决部分兼容性问题 这样做让我们的代码体积变大了很多 而且没有必要 我们可以使用core-js进行按需加载 我们哪里需要进行兼容性处理 你就加载需要的库就行 在终端输入npm i core-js -D下载 然后在webpack.config.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 
          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',// 预设环境的兼容性处理 固定写法 一定要写 就算你后来用了@babel/polyfill core-js 这个也一定要写
              {// 不适用按需加载的话 这个对象里的代码都不用写
                // 按需加载
                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'
};
  1. @babel/polyfill和core-js不兼容 只能同时使用一种 但是不管你使用哪一种 不管使不使用这两种 都要写@babel/preset-env
  2. Babel 的工作原理就是先将 ES6 源码转换为 AST,然后再将 ES6 语法的 AST 转换为 ES5 语法的 AST,最后利用 ES5 的 AST 生成 JavaScript 源代码。

压缩HTML、JS

  1. 只要将mode改为production启用生产环境就可以进行JS压缩了 因为在生产环境下webpack会自动加载很多配置 其中UglifyJsPlugin插件就会压缩JS代码 webpack.config.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'
    })
  ],
  // 生产环境下会自动压缩js代码
  mode: 'production'
};
  1. HTML不用做兼容性处理 只需要压缩 配置HtmlWebpackPlugin插件的minify属性即可 webpack.config.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',
      // 压缩html代码
      minify: {
        // 移除空格
        collapseWhitespace: true,
        // 移除注释
        removeComments: true
      }
    })
  ],
  mode: 'production'
};

生产环境基本配置

将上面的内容整合在一起 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');

// 定义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']
        //  使用less-loader将less文件编译成css文件
        //  然后使用postcss-loader对css进行兼容性处理
        //  最后使用MiniCssExtractPlugin.loader将css文件提取成单独的文件
      },
      /*
        正常来讲,一个文件只能被一个loader处理。
        当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
          先执行eslint 在执行babel
          因为eslint用于语法检查 如果先做其他处理 后面一旦我们检查出语法错误 又要更改语法 前面做的都要推翻重来
          babel用于兼容性处理 会将ES6语法转为ES5语法 转换后用eslint进行语法检查会报错
          就算你设置了自动更改错误语法 那么你刚转换的ES5语法白转换了 他这里又会给你转换为ES6语法
      */
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行 不管这个loader放哪里 他永远优先执行
        enforce: 'pre',// 正常情况下 loader的执行顺序没有限制 但是我们希望eslint先执行 所以为eslint设置这个属性
        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'
};

性能优化

开发环境性能优化

优化打包构建速度——HMR

  1. HMR: hot module replacement 热模块替换 / 模块热替换
    作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)极大提升构建速度
  2. 样式文件:可以使用HMR功能:因为style-loader内部实现了

这就是为什么我们在开发环境用style-loader 在生产环境得提取为单独的文件 因为开发环境使用style-loader能让性能更好 打包更快 但是在生产环境里代码需要上线 我们需要考虑代码的性能优化

  1. js文件:默认不能使用HMR功能 如果你想使用HMR 需要修改js代码,添加支持HMR功能的代码 在入口文件index.js中:
if (module.hot) {
  // 一旦 module.hot 为true,说明开启了HMR功能。接下来我们就让HMR功能代码生效
  module.hot.accept('./print.js', function() {
    // 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
    // 会执行后面的回调函数 打包构建print.js
    print();
  });
}
// 如果你还想监听其他的js文件 可以在后面继续写if (module.hot) {...}
  1. 注意:HMR功能对js的处理,只能处理非入口js文件的其他文件。因为入口文件会将其他所有的文件全都引入 一旦入口文件变化 其他文件又会重新引入重新加载 我们是无法阻止的
  2. html文件: 默认不能使用HMR功能 同时会导致问题:html文件不能热更新了(不用做HMR功能)

热更新:本地写的代码并没有重新编译 并没有重新刷新浏览器 就运行了

  1. 解决html文件不能热更新:修改entry入口,将html文件引入

HTML为什么没有HMR 因为HMR是当一个模块发生变化 只会打包这一个模块 而不是打包所有模块 而项目中只有一个HTML模块 只要HTML发生变化 一定会打包这一个文件 一定要全部打包的 没法优化

  1. webpack.config.js中代码如下
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // entry: ./src/js/index.js,
  entry: ['./src/js/index.js', './src/index.html'],// 把上一行代码改成这一行 把html文件加进来 就可以进行热更新了 但依旧不能使用HMR功能
  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
  }
};

优化代码调试——source-map

  1. webpack.config.js中代码如下
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,
    hot: true
  },
  devtool: 'eval-source-map'// 会生成一个.map文件 新增
};
  1. source-map: 一种提供源代码到构建后代码映射的技术 (如果构建后代码出错了,通过映射可以追踪到源代码的错误)
  2. 配置webpack.config.js中的devtool属性即可开启source-map技术 devtool属性的取值:[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
    (1)source-map:外部
    可以提供:错误代码准确信息 和 源代码的错误位置
    (2)inline-source-map:内联 在打包后的built.js中生成一个base64编码的source-map文件 它只生成一个内联source-map
    可以提供:错误代码准确信息 和 源代码的错误位置
    (3)hidden-source-map:外部
    可以提供:错误代码错误原因,但是没有错误位置 不能追踪源代码错误,只能提示到构建后代码的错误位置
    (4)eval-source-map:内联 每一个文件都生成对应的source-map,都在eval函数中 并内嵌在打包后的built.js中
    可以提供:错误代码准确信息 和 源代码的错误位置

react vue脚手架里用的这个

(5)nosources-source-map:外部
可以提供:错误代码准确信息, 但是没有任何源代码信息
(6)cheap-source-map:外部
可以提供:错误代码准确信息 和 源代码的错误位置 只能精确的行 别的都能精确到行和这一行的哪一个地方出错了 但cheap-source-map只能精确到哪一行
(7)cheap-module-source-map:外部 module会将loader的source map加入
可以提供:错误代码准确信息 和 源代码的错误位置 只能精确的行 别的都能精确到行和这一行的哪一个地方出错了 但cheap-source-map只能精确到哪一行

内联 和 外部的区别:1. 外部生成了文件,内联没有 2. 内联构建速度更快

  1. 开发环境:考虑:速度快,调试更友好
    (1)速度快(eval>inline>cheap>…)(从下到上越来越快)
    eval-cheap-souce-map
    eval-source-map
    (2)调试更友好(从下到上越来越友好)
    souce-map
    cheap-module-souce-map
    cheap-souce-map

综合来看eval-source-map / eval-cheap-module-souce-map 这两种最好(兼顾速度快 调试友好)不过左边调试更友好 右边速度更快

  1. 生产环境:考虑:源代码要不要隐藏? 调试要不要更友好
    注意:内联会让代码体积变大,所以在生产环境不用内联
    (1)源代码要隐藏:
    nosources-source-map 全部隐藏
    hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
    (2)调试从好到差(从左到右):①source-map②cheap-source-map/cheap-module-source-map③nosources-source-map/hidden-source-map

不隐藏源代码可以使用下面两种方法 只考虑调试用左边 要想速度快用右边:source-map / cheap-module-souce-map

生产环境性能优化

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

oneOf

以前rules中的代码:

	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'
        }
      }
    ]

但是 对于一种文件 他只可能匹配一项 比如less文件只会被第一个loader处理 但是如果你这样写的话 它处理完了还会去匹配其他的loader 没必要 所以我们可以使用oneof进行优化 oneof中的loader只会匹配并执行一个 所以oneof中不能有两个loader来处理同一个文件 像上面的babel-loader和eslint-loader都是处理js文件的 要想两个都处理JS文件 则需要从oneof中提取一个出来 不能把两个都放在oneof中 使用rules优化后的代码:

rules: [
      {
        // 在package.json中eslintConfig --> airbnb
        test: /\.js$/,
        exclude: /node_modules/,
        // 优先执行
        enforce: 'pre',
        loader: 'eslint-loader',
        options: {
          fix: true
        }
      },
      {
        oneOf: [
        // 以下loader只会匹配一个
        // 注意:不能有两个配置处理同一种类型文件
          {
            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'
                    }
                  }
                ]
              ]
            }
          },
          {
            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'
            }
          }
        ]
      }
    ]

缓存

  1. babel缓存:在写代码时 永远是JS代码最多 babel会对我们写的JS代码进行编译处理 编译成我们浏览器能识别的语法 即兼容性处理 假如我有100个JS模块 我只改动了其中1个JS模块 我希望只编译这一个JS模块 其他99个是不变的 这和HMR功能很像 但HMR是基于dev-serve的 生产环境不需要dev-serve 生产环境中为了实现这一需求可以开启babel缓存 在babel-loader的options中添加代码:cacheDirectory: true 可以将babel之间编译的100个文件缓存 以后如果这100个文件没有改变 会直接使用缓存 而不会再重新打包一次
  2. 文件资源缓存:当我们的代码上线后 都会对资源做缓存处理(强制缓存 协商缓存) 这样用户第二次访问时会快很多 假如资源在强制缓存期间出现了bug 我们需要更改代码 但由于资源被强制缓存了 不会向服务器发请求 每次都是直接从缓存中拿 也就是说 在强制缓存期间 我不想走缓存 我该怎么办?此时我们可以更改资源名称 比如给资源名称增加版本号等 那么下次我更新时 更新了版本号 资源名称变了 就会重新发请求 所以我们可以给文件名加上hash值
    (1)hash: 每次wepack构建时 会生成一个唯一的hash值。不管你文件有没有变化 每次webpack构建时 这个hash值都会变化
    问题: 因为js和css使用的都是webpack打包生成的hash值 如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)CSS和JS分开缓存了 但是他们公用同一个hash值 假如js文件改了CSS没变化 这个hash值也会改变 导致JS缓存和CSS缓存都失效了
    (2)chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
    问题: js和css的hash值还是一样的 因为css是在js中被引入的,所以同属于一个chunk
    (3)==contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样 ==
  3. 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');

// 定义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/,
            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

  1. tree shaking:去除无用代码 减少代码体积
  2. 任何开启tree shaking:1. 必须使用ES6模块化 2. 开启production环境
  3. 以上方法会有个Bug 可能会将css当作无用代码 给去除了 模拟测试:在package.json中配置:"sideEffects": false 表明所有代码都可以进行tree shaking 此时会发现打包后的built文件中没有css文件
  4. 解决:在package.json中配置:"sideEffects": ["*.css", "*.less"] 你可以在数组里面标记不进行tree shaking的资源

code split

  1. 代码分割:将我们打包输出的一个文件分割成多个文件 这样加载时我们可以并行加载这么多文件 加载速度更快 此外我们可以据此实现按需加载 我需要用这个文件我再加载 不需要用就不加载
  2. 方法一:设置多个入口文件 一个入口文件会输出一个bundle 多个入口文件就会打包输出多个bundle webpack.config.js中代码如下
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 单入口 单页面应用的配置
  // entry: './src/js/index.js',
  entry: {
    // 多入口:有一个入口,最终输出就有一个bundle 多页面应用的配置方法
    index: './src/js/index.js',// index就是文件名 就是下面[name]的值
    test: './src/js/test.js'// test就是文件名 就是下面[name]的值
  },
  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'
};

问题:假如我今天有一个入口 明天有两个入口 后天有三个入口 每次都需要改 改来改去很麻烦

  1. 方法二:配置optimization属性 webpack.config.js中代码如下
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 单入口
  // entry: './src/js/index.js',
  entry: {
    // 多入口:有一个入口,最终输出就有一个bundle 多页面应用的配置方法
    index: './src/js/index.js',// index就是文件名 就是下面[name]的值
    test: './src/js/test.js'// test就是文件名 就是下面[name]的值
  },
  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'
};

问题:针对单页面应用 他只会把node_modules中代码单独打包成一个chunk并输出 将我们写的所有代码都打包成一个chunk 但是我们希望 我们写的代码可以打包成多个chunk

  1. 方法三:在js代码中 使用import动态导入语法,让某个文件被单独打包成一个chunk index.js中代码如下
function sum(...args) {
  return args.reduce((p, c) => p + c, 0);
}

/*
  import动态导入语法:能将某个文件单独打包 这里将./test文件单独打包
*/
/* webpackChunkName: 'test' */ // 这个注释会对你打包后的chunk进行重命名 重命名为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));

JS懒加载和预加载

  1. 懒加载:当文件需要使用时才加载 使用import动态导入语法 懒加载是一定会进行代码分割的
    index.js文件中的代码如下
console.log('index.js文件被加载了~');

document.getElementById('btn').onclick = function() {
  import(/* webpackChunkName: 'test' */'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};

这样 点击按钮才会加载./test文件

  1. 预加载:会在使用之前,提前加载js文件 存在兼容性问题 配置注释使用

预加载和正常加载的区别:正常加载可以认为是并行加载(同一时间加载多个文件) 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源

index.js文件中的代码如下

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

// import { mul } from './test';// 正常加载

document.getElementById('btn').onclick = function() {
  import(/* webpackPrefetch: true */'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};

PWA:workbox-webpack-plugin

  1. PWA:渐进式网络开发应用程序(离线可访问) 在离线状态下也可以访问网站
  2. 通过workbox来使用PWA技术 在webpack中 需要借助workbox-webpack-plugin插件来使用PWA技术 在终端输入npm i workbox-webpack-plugin -D下载该插件并引入
  3. 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');

// 定义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/,
            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({// 通过WorkboxWebpackPlugin实例的GenerateSW方法才能使用PWA技术
      /*
        1. 帮助serviceworker快速启动
        2. 删除旧的 serviceworker

        最终会生成一个 serviceworker 配置文件~ 接下来通过serviceworker配置文件来注册serviceworker 在入口文件中配置
      */
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};
  1. 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不认识 window、navigator全局变量
    解决:需要修改package.json中eslintConfig配置
      "env": {
        "browser": true // 支持浏览器端全局变量
      }
   2. serviceWorker代码必须运行在服务器上
      --> nodejs
      -->
        npm i serve -g 帮助我们快速创建一个静态资源服务器
        serve -s build 启动服务器,将build目录下所有资源作为静态资源暴露出去
*/

// 注册serviceWorker
// 处理兼容性问题
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
  // 绑定load事件 等全局资源加载完毕再注册serviceWorker
    navigator.serviceWorker
      .register('/service-worker.js')
        // /service-worker.js这个文件会由webpack.config.js中的new WorkboxWebpackPlugin.GenerateSW生成
      .then(() => {
        console.log('sw注册成功了~');
        // 注册成功后 以后断网了 浏览器就会从serviceWorker中加载资源 就可以拿到我之前通过serviceWorker缓存的资源
      })
      .catch(() => {
        console.log('sw注册失败了~');
      });
  });
}

多进程打包

  1. JS主线程是单线程的 同一时间只能干一件事 如果我的事情比较多 他就要排队等很久很久才能干下一件事 会比较慢 所以我们可以通过多进程来优化打包速度 同一时间 我有好几个进程来干这个事 会快一些 终端输入:npm i thread-loader -D下载
  2. 一般给babel-loader使用thread-loader babel-loader工作时 就会开启多进程了
  3. 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');

// 定义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,进程通信也有开销。
                只有工作消耗时间比较长,才需要多进程打包
                假如你这个工作100ms就干完了 你交给多进程去做 光进程启动就要花600Ms 得不偿失
                由于项目中JS文件比较多 所以我们首先想到打包JS时进行优化
                会对JS进行处理的loader有两个:eslint-loader babel-loader
                前者只进行语法检查 后者需要进行编译、转换等 消耗时间比较长 也是我们工作时间最长的loader
                所以我们优先想着对babel-loader进行优化
              */
              // 'thread-loader',// 它每次启动会根据CPU核数-1的数量来启动进程 如果你想指定 可以写下面这个对象里的代码
              {
                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({
      clientsClaim: true,
      skipWaiting: true
    })
  ],
  mode: 'production',
  devtool: 'source-map'
};

externals

  1. externals:防止将某些包打包到我们最终输出的bundle中
  2. webpack.config.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'
    })
  ],
  mode: 'production',
  externals: {
    // 拒绝jQuery被打包进来
    // 这里面的包不准打包进bundle中
    // 库名: 'npm下载的包名'
    jquery: 'jQuery'
  }
};
  1. 可以在index.js文件中 通过script标签引入jQuery使用 不引入的话就相当于你没有用
  2. 应用场景:希望某些包不打包进最终输出的bundle中 少打包一个包可以提升打包速度 不打包的包我们可以通过script标签引入使用

dll

  1. dll:动态链接库 类似于externals 会告诉webpack那些库不参与打包 不同的是 dll会对某些库进行单独打包将多个库打包成一个chunk
  2. 由于我们对代码做了分割 node_modules中的代码会被我们打包成一个chunk 但是我们的第三方库又特别特别多 如果全部打包成一个文件 那么这个文件体积太大了 所以通过dll技术 可以将这些第三方库单独拆分出来 打包成不同的chunk 有利于性能优化
  3. 我们需要重新创建个配置文件 再这里对某些库进行单独打包 文件名随便取 这里取webpack.dll.js 内容如下:
/*
  使用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', 'xxx']
    jquery: ['jquery'],
  },
  output: {
    filename: '[name].js',// [name]与上面那个“最终打包生成的[name]”一致
    path: resolve(__dirname, 'dll'),
    library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
  },
  // 接下来我们需要简历一个依赖关系 告诉webpack将来打包时不要再打包jquery了 此时需要借助webpack插件来生成一个映射文件
  plugins: [
    // 打包生成一个 manifest.json --> 提供和jquery映射 这样webpack就知道jquery库不需要打包 并且jquery库和[name]_[hash]相对应
    new webpack.DllPlugin({
      name: '[name]_[hash]', // 映射库的暴露的内容名称
      path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
    })
  ],
  mode: 'production'
};

在终端输入:webpack --config webpack.dll.js 运行webpack.dll.js文件 会对jquery库进行单独打包 并生成manifest.json文件提供映射关系

  1. 以后jquery库就不用打包了 直接在源代码中引入就可以了 我们还需要更改webpack.config.js中的配置:
    (1)使用webpack插件告诉webpack哪些库不参与打包,同时使用时的名称也得变
    (2)我们发现输出的打包文件built.js文件中就没有jquery库 所以还需要使用add-asset-html-webpack-plugin插件将jquery库打包输出出去 输出出来之后add-asset-html-webpack-plugin插件会自动在built.js文件中引入它
  2. 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哪些库不参与打包,同时使用时的名称也得变~
    new webpack.DllReferencePlugin({
      manifest: resolve(__dirname, 'dll/manifest.json')
    }),
    // webpack不打包jquery库 在输出的打包文件built.js文件中就没有jquery库 
    // 所以我们还需要将jquery库打包输出出去 再在built.js文件中引入它
    // 将某个文件打包输出去,并在html中自动引入该资源
    new AddAssetHtmlWebpackPlugin({
      filepath: resolve(__dirname, 'dll/jquery.js')
    })
  ],
  mode: 'production'
};

之后你的源代码改了 jquery没改 只用运行webpack.config.js就可以了

webpack配置详解

entry

entry: 入口起点

  1. string --> './src/index.js'
    (1)单入口
    (2)打包形成一个chunk(不管你源代码有几个文件 都打包在一个chunk中)。 输出一个bundle文件。
    (3)此时chunk的名称默认是 main
  2. array --> ['./src/index.js', './src/add.js']
    (1)多入口
    (2)所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
    (3)此时chunk的名称默认是 main
    (4)作用:只有在HMR功能中让html热更新生效~
  3. object
    (1)多入口
    (2)有几个入口文件就形成几个chunk,输出几个bundle文件
    (3)此时chunk的名称是 key
entry: {
			index: './src/index.js', // 这个文件打包到index.js文件中
            add: './src/add.js'// 这一个文件打包到add.js文件中
        } 
  1. webpack.config.js中代码如下
module.exports = {
  // entry: './src/index.js',
  entry: {
    index: ['./src/index.js', './src/count.js'], // 这两个文件打包到index.js文件中
    add: './src/add.js'// 这一个文件打包到add.js文件中
  },
  output: {
    filename: '[name].js',
    path: resolve(__dirname, 'build')
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

output

webpack.config.js中代码如下

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'
    // 假如一张图片的路径是imgs/a.jpg 经过publicPath: '/'处理后就变成了/imgs/a.jpg
    // imgs/a.jpg代表在当前路径下去找imgs/a.jpg
    // /imgs/a.jpg会以当前服务器地址做补充 取服务器根目录下找imgs目录 再在里面找a.jpg 代码上线后更倾向于使用这种
    publicPath: '/',// 路径前加不加/ 比如我script标签引入了某个资源 这个资源前面加不加/ 样式文件通过link标签引入 路径前加不加/ 等
    
    chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名称 entry指定的文件就叫入口文件
    // 非入口chunk:(1)import语法动态导入某个文件时 会将一个文件单独分割成一个chunk 这个chunk就会采用chunkFilename命名
    // (2)通过optimization将node_modules中的东西分割成单独chunk 这些chunk就会采用chunkFilename命名

    // library: '[name]', // 打包后的main.js整个库向外暴露的变量名 打包后的main.js中的内容向外暴露的变量名 外面可以直接引用使用
    // libraryTarget: 'window' // 变量名添加到哪个上 browser
    // libraryTarget: 'global' // 变量名添加到哪个上 node
    // libraryTarget: 'commonjs'
  },
  plugins: [new HtmlWebpackPlugin()],
  mode: 'development'
};

module

  1. module中我们一般只写rules rules中写loader的配置
  2. webpack.config.js中代码如下
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中写正则匹配规则 你要匹配的文件名
        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. resolve:解析模块的规则
  2. 将来我们写项目的时候 文件可能会嵌套很多层 目录层级会很深 要回过头去找某个目录会很费劲 配置路径别名就可以解决这个问题 原本你要写../css/index.css配置了resolve的alias属性
    alias: {
      $css: resolve(__dirname, 'src/css')
    }

后 你就可以写$css/index.css了 他自己会通过$css找到绝对路径 再去里面找index.css

  1. extensions:配置省略文件路径的后缀名 默认值是extensions: ['.js', '.json'] 假如你有个index.css文件 配置了下面这段代码后 你就可以写index了 他会先匹配index.js 发现没有这个文件 再匹配index.json 发现没有 再匹配index.jsx 发现没有 再匹配index.css 发现有 就使用 都没找到会报错 只要找到一个就不会再往后面找了 所以文件名千万不要起一样 优点:简写路径 缺点:路径没有提示
  2. modules:告诉 webpack 解析模块是去找哪个目录 默认是modules: ['node_modules'] 他会先在当前目录下找node_modules 找不到再去上一级中找 再找不到再去上一级中找 一直往上找 如果我们目录层级比较深 这样找太麻烦了 所以我们可以通过绝对路径告诉它在哪里modules: [resolve(__dirname, '../../node_modules'), 'node_modules'] 如果resolve(__dirname, '../../node_modules')没找到 会按照前面我们说的方式去找node_modules 这就是配置两个的原因
  3. webpack.config.js中代码如下
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

  1. 用于开发环境
  2. webpack.config.js中代码如下
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: {
    // 运行代码的目录
    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

webpack.config.js中代码如下

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'
      // 下面是默认值,一般不写~
      /* minSize: 30 * 1024, // 分割的chunk最小为30kb 小于30kb不分割 大于30kb才分割
      maxSize: 0, // 最大没有限制
      minChunks: 1, // 要提取的chunk最少被引用1次 不引用直接别打包了
      maxAsyncRequests: 5, // 按需加载时并行加载的文件的最大数量
      maxInitialRequests: 3, // 入口js文件最大并行请求数量
      automaticNameDelimiter: '~', // 名称连接符
      name: true, // 可以使用命名规则
      cacheGroups: {
        // 分割chunk的组
        // node_modules中的文件会被打包到 vendors 组的chunk中。--> 命名规则:vendors~xxx.js xxx是模块名称
        // 满足上面的公共规则,如:大小超过30kb,至少被引用一次。
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          // 打包的优先级
          priority: -10
        },
        default: {
          // 要提取的chunk最少被引用2次
          minChunks: 2,
          // 优先级
          priority: -20,
          // 如果当前要打包的模块,和之前已经被提取的模块是同一个,就会复用,而不是重新打包模块
          reuseExistingChunk: true
        } 
      }*/
    },
    // 将当前模块的记录其他模块的hash单独打包为一个文件 runtime
    // 解决:修改a文件导致b文件的contenthash变化
    /*
    b文件引用了a文件 改动了a文件 a文件的hash值发生变化 因为b文件引用了a文件 所以b文件中保留了a文件的hash值
    但是现在由于改动了a文件 a文件的hash值变了 所以b文件的内容也变了(b引用了a文件的hash值 这个hash值变了)
    所以b也要重新打包 为了解决这个问题 我们可以使用runtime将b文件中引用的a文件的hash值提取出来打包在其他文件里
     */
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}`
    //  这样 当a文件变化时 只有a文件和a文件的runtime文件会重新打包 其他不会变
    },
    minimizer: [
      // 配置生产环境的压缩方案:js和css
      //  之前我们压缩js css用的是UglifyJsPlugin插件 但这个插件已经不维护了
      //  以后我们压缩js css用Terser 如果你使用默认配置 可以不用管
      //  在终端输入 npm i terser-webpack-plugin -D 下载terser-webpack-plugin
      //  下面更改Terser默认配置
      new TerserWebpackPlugin({
        // 开启缓存
        cache: true,
        // 开启多进程打包
        parallel: true,
        // 启动source-map
        sourceMap: true
      })
    ]
  }
};

webpack5介绍和使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值