webpack5入门教程

166 阅读9分钟

一、搭建框架

1.1 创建npm包管理

// 此命令可以搭建一个简易的npm包管理,并生成package.json文件,用来对依赖包进行管理
npm init -y

image.png

1.2 安装webpack

npm i webpack webpack-cli -D
  • 安装后会生成node_module文件,后续所有的依赖包都会保存在该文件中

image.png

1.3 创建目录结构

image.png

1.4 编写测试代码

image.png

1.4 webpack打包

// npx命令 会自动将 node_module/bin 下作为环境变量
// 使用 webpack 对 main.js 为入口的文件进行打包
// --mode 为指定打包的模式 [development/production]
    // development开发模式下,不会对代码进行压缩
    // production生产模式下,会对代码进行压缩
// 打包完成后,会在根目录下生成dist目录
npx webpack ./src/main.js --mode=development
  • 打包效果

image.png

  • development模式
    • npx webpack ./src/main.js --mode=development

image.png

  • production模式
    • npx webpack ./src/main.js --mode=production

image.png

1.5 查看页面效果

  • 引入打包的资源

image.png

  • vscode安装第三方插件 Live Server,用来打开页面

image.png

  • 在index.html文件中右键打开

image.png

  • 页面效果

image.png

1.6 使用webpack配置文件来配置打包

  • 在项目根目录下创建 webpack.config.js 文件,来对打包进行相关配置
  • webpack配置的基本结构
// 引入nodeJs的path模块
const path = require("path");
module.exports = {
  // 配置 项目 入口
  entry: "./src/main.js",
  // 配置 打包文件 保存的位置
  output: {
    // 打包的绝对路径  path.resolve 会返回一个绝对路径
    path: path.resolve(__dirname, "dist"),
  },
  // 配置 loader 转换器
  // webpack 只能理解 JavaScript 和 JSON 文件.
  // loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
  module: {
    rules: [],
  },
  // 配置插件
  // loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。
  plugins: [],
  // 模式 【development、production】
  mode: "development",
};

  • 当我们配置好webpack.config.js后,就可以使用npx webpack命令,该命令会自动使用webpack.config.js的配置进行打包

image.png

二、webpack基础配置

webpack只能识别js和json文件,对于其他文件(css、图片等)无法解析,但我们可以通过配置来转换这些文件,变成webpack能识别的文件

2.1 处理css资源

  • 当我们创建css并引入后,未进行配置时,打包会报错
  • 从报错信息中,我们可以看出,webpack无法识别css文件

image.png image.png

  • 我们可以查阅官网,来转换css
  • 首先,你需要先安装 css-loaderstyle-loader
npm install --save-dev css-loader style-loader
  • 配置loader
  module: {
    rules: [
      {
        // 匹配 .css 结尾的文件
        test: /\.css$/i,
        use: [
          // use使用时,是从右到左,从下到上解析
          // 即先使用 css-loader 将css解析为commonJs到js文件中,
          // 在使用 style-loader 创建 style 标签将css引入到index.html 文件中
          "style-loader",
          "css-loader",
        ],
      },
    ],
  },
  • 可以看到页面中创建了style标签引入了css资源

image.png

2.2 处理less资源

  • 安装依赖
npm install less less-loader --save-dev
  • 配置
  module: {
    rules: [
      {
        // 匹配 .less 结尾的文件
        test: /\.less$/i,
        use: [
          "style-loader",
          "css-loader",
          // 将 less 转换为 css
          "less-loader",
        ],
      },
    ],
  },

2.3 处理sass/scss资源

  • 安装依赖
npm install sass-loader sass webpack --save-dev
  • 配置
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          "style-loader",
          "css-loader",
          // 将 Sass/Scss 编译成 CSS
          "sass-loader",
        ],
      },
    ],
  },

2.4 处理图片资源

在webpack5中,已经内置了图片资源的处理,即我们引入图片资源时,可以直接打包,但这种打包只是将图片资源直接拷贝到dist目录下,没有对图片进行任何处理;当存在较多小图片时,每次加载页面都请求,会增加服务器的压力,此时我们可以对小图片进行base64转换,减少http请求。

  • 配置
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg|webp)$/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            // 文件大小 小于10kb的,将打包成 base64 的格式,大于的将直接进行文件拷贝
            // 优点:减少http请求   缺点:打包文件体积变大
            maxSize: 10 * 1024, // 10kb
          },
        },
      },
    ],
  },
  • 引入图片资源,200.png大小为3kb,dog1.png大小为30kb image.png

  • 可以看到,通过webpack配置,我们将图片资源打包到dist/static目录下,且目前只打包了一个图片资源

image.png

  • 200.png已经转换为了base64格式

image.png

  • dog1.png仍然为静态图片资源

image.png

2.5 修改文件打包输出目录

从之前的打包我们可以看出,不管是js文件还是图片,都直接放到dist目录下,显得有点杂乱,我们可以通过配置,来实现js文件存放在js目录下,图片存放在图片目录下

  • 配置
    • chunkFilename 的使用
// /* webpackChunkName: "math" */ webpack魔法命名
// 这里写文件名称,然后通过chunkFilename配置进行命名
import(/* webpackChunkName: "math" */ "./js/math").then(()=>{})
module.exports = {
  output: {
    // 打包的绝对路径  path.resolve 会返回一个绝对路径
    path: path.resolve(__dirname, "dist"),
    // js文件打包路径
    // name 为原文件名称
    // 入口文件打包名称
    filename: "js/[name].js",
    // 默认的静态资源存放路径
    assetModuleFilename: "assets/[hash][ext][query]",
    // 给打包输出的其他文件命名(动态导入的js文件、非入口js文件)
    chunkFilename:'js/[name].js'
  },
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg|webp)$/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            // 文件大小 小于10kb的,将打包成 base64 的格式,大于的将直接进行文件拷贝
            // 优点:减少http请求   缺点:打包文件体积变大
            maxSize: 10 * 1024, // 10kb
          },
        },
        generator: {
          // 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
          // hash 为文件名哈希
          // [hash:10] 为hash取前10位
          // ext 为原文件的后缀
          // query 为可选参数 如 url?xxxx
          filename: "static/[hash][ext][query]",
        },
      },
    ],
  },
};
  • 可以看到js文件已经存放在js目录下,图片则存放在static目录下,这样阅读起来更舒服

image.png

2.6 打包时自动清空上一次打包文件

在还没有进行对应配置前,webpack每次打包都不会自动清除上一次打包的文件,需要我们手动进行清除,很不方便,此时我们可以通过clean配置自动清除

  • 配置
// 当然,webpack5 以下,需要装插件实现
  output: {
    // 打包的绝对路径  path.resolve 会返回一个绝对路径
    path: path.resolve(__dirname, "dist"),
    // js文件打包路径
    // name 为原文件名称
    filename: "js/[name].js",
    // 默认的静态资源存放路径
    assetModuleFilename: "assets/[hash][ext][query]",
    // 原理:打包前将 path 目录删除,然后再进行打包
    clean: true,
  },

2.7 处理字体图标(阿里巴巴矢量图)

image.png

image.png

image.png

  • 将下载后的矢量图解压到项目中
    • 可以通过demo.html查看具体的用法

image.png

image.png

  • 项目中使用
    • 将解压的文件解压到style/font目录下
    • main.js引用iconfont.css
    • 在index.html中使用icon
      • 格式:
    • 因为设置了默认资源打包路径,所以icon图标被打包到assets目录下
    • 因为前面配置了js文件打包的目录,所以需要更改main.js的引用路径

image.png

image.png

  • 页面效果

image.png

  • 我们也可以单独配置font资源的打包目录

image.png

2.8 关于loader中的type属性值的解释

image.png

  • type有几种属性
    • asset/resource
      • 将资源文件作为独立的文件输出,返回其URL。
      • 比如引用图片路径
      • 常用来处理图片、视频、音频资源等
    • asset/inline
      • 将资源文件作为DataURL内联到JavaScript中。
      • 比如base64
    • asset/source
      • 将资源文件作为字符串导出,并返回其内容。
      • 比如引入txt文本的内容
    • asset
      • 通用资源类型
      • 现在,webpack 将按照默认条件,自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。
      • 比如上文提到的处理图片资源

2.9 处理Eslint

2.9.1 了解Eslint
  • Eslint是用来检测js和jsx语法的工具,可以配置各项功能
  • 我们使用Eslint,关键是写Eslint配置文件,里面写上各种rules规则,来运行Eslint对代码进行检测
  • 配置文件
    • 配置文件有多种写法
    • .eslintrc.*:新建文件,位于项目的根目录
      • .eslintrc
      • .eslintrc.js:使用commonJS写法,可以写注释
      • .eslintrc.json:使用json写法
      • 区别在于配置的格式不一样
    • 或者直接在package.json中的eslintConfig中直接写,这样就不需要创建文件(不推荐)
  • 具体配置模板
// .eslintrc.js
module.exports = {
  env: {
    // 启用node中的全局变量
    node: true,
    // 启用浏览器中的全局变量
    browser: true,
  },
  // 解析选项
  parserOptions: {
    // 配置 ES 语法版本
    ecmaVersion: 6,
    // ES 模块化
    sourceType: "module",
    // ES 其他特性
    ecmaFeatures: {
      jsx: true, // 如果是 React 项目,就需要开启 jsx 语法
    },
  },
  // 具体规则
  rules: {
    // 禁止使用分号
    semi: "error",
    // 强制数组方法的回调函数中有return预计,否则告警
    "array-callback-return": "warn",
    "default-case": [
      // 要求 switch 语句中有 default 分支,否则告警
      "warn",
      // 运行在最后注释 no default,就不会有告警了
      { commentPattern: "^no defalut$" },
    ],
    eqeqeq: [
      // 强制使用 === 和 !===,否则告警
      "warn",
      // 表示强制使用 ===和!==,但有些情况除外,
      // 具体情况查看:https://eslint.nodejs.cn/docs/latest/rules/eqeqeq#smart
      "smart",
    ],
    // 禁止使用var
    "no-var": 2,
  },
  // 继承其他规则
  // 继承eslint默认规则
  extends: ["eslint:recommended"],
  // 其他规则详见:
  // 英文官网:https://eslint.org/docs/latest/rules/
  // 中文官网:https://eslint.nodejs.cn/docs/latest/rules/
};

  • rules 具体规则
    • off0:关闭规则
    • warn1:开启规则,使用警告级别的错误(不会导致程序退出,仅作告警)
    • error2:开启规则,使用错误级别的错误(当被触发时,程序会直接中断)
2.9.2 使用Eslint
  • 安装依赖包
npm i eslint-webpack-plugin eslint -D
  • 配置.eslintrc.js文件(使用上面的模板)
  • webpack配置eslint
// webpack.config.js

// 引入Eslint
const ESLintPlugin = require("eslint-webpack-plugin");

  // plugins配置中添加eslint
  plugins: [
    new ESLintPlugin({
      // 配置哪些文件要进行检查
      context: path.resolve(__dirname, "src"),
    }),
  ],
  • 安装eslint提示插件

image.png

  • 查看效果

image.png

  • 这些爆红是eslint插件的提示,但是我们打包的dist目录下的js也爆红了

image.png

  • 若我们想排除某些文件的告警,可以通过如下配置
    • 在根目录创建 .eslintignore文件(类似于git的忽略文件 .gitignore
    • 在其中配置忽略的文件/目录
    • 可以看到,dist下的main.js已经不爆红了

image.png

2.10 处理Babel

2.10.1 了解Babel
  • 主要用于将ES6语法编写的代码转换为向后兼容的JavaScript语法,以便能够运行在当前和旧版本的浏览器或其他环境中
  • 配置文件有多种写法
    • babel.config.*:新建文件,位于项目根目录
      • babel.config.js
      • babel.config.json
    • .babelrc.*:新建文件,位于项目根目录
      • .babelrc
      • .babelrc.js
      • .babelrc.json
    • package.json中的babel中
2.10.2 配置babel
  • 安装依赖
npm install -D babel-loader @babel/core @babel/preset-env webpack

  • 配置webpack.config.js
// webpack.config.js

  module: {
    rules: [
    // 配置babel转换器
      {
        test: /\.js$/,
        // 排除node_modules目录
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"],
          },
        },
      },
    ],
  },
  • 配置babel规则
// babel.config.js

module.exports = {
  // 智能预设,能够编译 ES6
  presets: ["@babel/preset-env"],
};

  • 还没有配置babel之前,可以看到es6的语法打包之后,仍然还是es6语法(箭头函数)

image.png

  • 配置了babel后,已经不是箭头函数了

image.png

2.11 处理html资源

之前我们打包后,需要手动引入 main.js 的入口文件,当我们改变了打包输出结构的话,又需要手动去更改,非常的不方便,因此我们希望可以实现自动引入(html-webpack-plugin)

  • 安装依赖包
npm install --save-dev html-webpack-plugin

  • 配置 webpack.config.js
// webpack.config.js

// 引入 html处理插件
const HtmlWebpackPlugin = require("html-webpack-plugin");

  plugins: [
    // 配置 html自动引入功能
    new HtmlWebpackPlugin(),
  ],
  • 效果:我们可以看到打包后自动在dist目录下创建了index.html文件,且引入了main.js文件
    • 但丢失了我们之前写的配置

image.png

image.png

  • 可以通过该插件配置生成的html模板,这样就不会丢失我们写好的配置
  plugins: [
    // 配置 html自动引入功能
    new HtmlWebpackPlugin({
      // 配置模板文件
      template: path.resolve(__dirname, "public/index.html"),
    }),
  ],
  • 效果

image.png

2.12 热更新(开发服务器)

在之前的操作中,我们每次对文件修改后,都需要重新打包才能看到效果,非常的鸡肋,因此我们可以通过配置热更新 webpack-dev-server ,实现代码发生变动,自动重新编译,即无需打包就可以看到效果变化

  • 安装依赖
npm install --save-dev webpack-dev-server
  • 配置webpack.config.js
module.export = {
  devServer: {
    // 配置启动服务器的域名
    host: "localhost",
    // 配置服务端口
    port: "8080",
    // 是否自动打开浏览器
    open: true,
  },
}
  • 启动服务器:npx webpack server
    • 运行后,会自动打开浏览器,而且我们修改了文件内容,会自动重新编译
    • 这种运行方式,是在内存中打包的,故dist文件不会发生变化

2.13 配置开发环境和生产环境

针对开发环境和生产环境的不同需求,需要针对进行不同的配置,故可以抽离配置

  • 调整目录
    • 新增config目录,创建生产环境和开发环境的配置文件

image.png

  • 开发环境配置
    • 注意
      • 因为运行的时候是在根目录下运行
      • 故相对路径无需更改
      • 但绝对路径需要做调整
// webpack.dev.js

const path = require("path");

// 引入Eslint
const ESLintPlugin = require("eslint-webpack-plugin");

// 引入 html处理插件
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/main.js",
  output: {
    // 打包的绝对路径  path.resolve 会返回一个绝对路径
    // path: path.resolve(__dirname, "../dist"),
    path: undefined, // 开发环境不需要打包
    // js文件打包路径
    // name 为原文件名称
    filename: "js/[name].js",
    // 默认的静态资源存放路径
    assetModuleFilename: "assets/[hash][ext][query]",
    // 原理:打包前将 path 目录删除,然后再进行打包
    // clean: true,
  },
  module: {
    rules: [
      {
        // 匹配 .css 结尾的文件
        test: /\.css$/i,
        use: [
          // use使用时,是从右到左,从下到上解析
          // 即先使用 css-loader 将css解析为commonJs到js文件中,
          // 在使用 style-loader 创建 style 标签将css引入到index.html 文件中
          "style-loader",
          "css-loader",
        ],
      },
      {
        // 匹配 .less 结尾的文件
        test: /\.less$/i,
        use: [
          "style-loader",
          "css-loader",
          // 将 less 转换为 css
          "less-loader",
        ],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          "style-loader",
          "css-loader",
          // 将 Sass/Scss 编译成 CSS
          "sass-loader",
        ],
      },
      {
        test: /\.(png|jpe?g|gif|svg|webp)$/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            // 文件大小 小于10kb的,将打包成 base64 的格式,大于的将直接进行文件拷贝
            // 优点:减少http请求   缺点:打包文件体积变大
            maxSize: 10 * 1024, // 10kb
          },
        },
        generator: {
          // 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
          // hash 为文件名哈希
          // [hash:10] 为hash取前10位
          // ext 为原文件的后缀
          // query 为可选参数 如 url?xxxx
          filename: "static/[hash][ext][query]",
        },
      },
      {
        test: /\.(ttf|woff2?)$/,
        type: "asset/resource",
        generator: {
          // 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
          filename: "font/[hash][ext][query]",
        },
      },
      {
        test: /\.js$/,
        // 排除node_modules目录
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"],
          },
        },
      },
    ],
  },
  plugins: [
    new ESLintPlugin({
      // 配置哪些文件要进行检查
      context: path.resolve(__dirname, "../src"),
    }),
    // 配置 html自动引入功能
    new HtmlWebpackPlugin({
      // 配置模板文件
      template: path.resolve(__dirname, "../public/index.html"),
    }),
  ],
  // 模式 【development、production】
  mode: "development",
  devServer: {
    // 配置启动服务器的域名
    host: "localhost",
    // 配置服务端口
    port: "8080",
    // 是否自动打开浏览器
    open: true,
  },
};

  • 生产环境配置
    • 注意
      • 因为运行的时候是在根目录下运行
      • 故相对路径无需更改
      • 但绝对路径需要做调整
// webpack.prod.js

const path = require("path");

// 引入Eslint
const ESLintPlugin = require("eslint-webpack-plugin");

// 引入 html处理插件
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/main.js",
  output: {
    // 打包的绝对路径  path.resolve 会返回一个绝对路径
    path: path.resolve(__dirname, "../dist"),
    // js文件打包路径
    // name 为原文件名称
    filename: "js/[name].js",
    // 默认的静态资源存放路径
    assetModuleFilename: "assets/[hash][ext][query]",
    // 原理:打包前将 path 目录删除,然后再进行打包
    clean: true,
  },
  module: {
    rules: [
      {
        // 匹配 .css 结尾的文件
        test: /\.css$/i,
        use: [
          // use使用时,是从右到左,从下到上解析
          // 即先使用 css-loader 将css解析为commonJs到js文件中,
          // 在使用 style-loader 创建 style 标签将css引入到index.html 文件中
          "style-loader",
          "css-loader",
        ],
      },
      {
        // 匹配 .less 结尾的文件
        test: /\.less$/i,
        use: [
          "style-loader",
          "css-loader",
          // 将 less 转换为 css
          "less-loader",
        ],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          "style-loader",
          "css-loader",
          // 将 Sass/Scss 编译成 CSS
          "sass-loader",
        ],
      },
      {
        test: /\.(png|jpe?g|gif|svg|webp)$/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            // 文件大小 小于10kb的,将打包成 base64 的格式,大于的将直接进行文件拷贝
            // 优点:减少http请求   缺点:打包文件体积变大
            maxSize: 10 * 1024, // 10kb
          },
        },
        generator: {
          // 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
          // hash 为文件名哈希
          // [hash:10] 为hash取前10位
          // ext 为原文件的后缀
          // query 为可选参数 如 url?xxxx
          filename: "static/[hash][ext][query]",
        },
      },
      {
        test: /\.(ttf|woff2?)$/,
        type: "asset/resource",
        generator: {
          // 将资源打包到指定的目录中,若没有配置,则会使用上面的静态资源路径
          filename: "font/[hash][ext][query]",
        },
      },
      {
        test: /\.js$/,
        // 排除node_modules目录
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"],
          },
        },
      },
    ],
  },
  plugins: [
    new ESLintPlugin({
      // 配置哪些文件要进行检查
      context: path.resolve(__dirname, "../src"),
    }),
    // 配置 html自动引入功能
    new HtmlWebpackPlugin({
      // 配置模板文件
      template: path.resolve(__dirname, "../public/index.html"),
    }),
  ],
  // 模式 【development、production】
  mode: "production",
  // 生产环境不需要开发服务器调试
  // devServer: {
  //   // 配置启动服务器的域名
  //   host: "localhost",
  //   // 配置服务端口
  //   port: "8080",
  //   // 是否自动打开浏览器
  //   open: true,
  // },
};

  • 运行
    • 开发环境
      • npx webpack serve --config .\config\webpack.dev.js
    • 生产环境
      • npx webpack --config .\config\webpack.prod.js
  • 每次运行都需要写这么长的命令,容易写错,故我们可以给命令添加快捷键
// package.json

{
  "name": "webpack-code",
  "version": "1.0.0",
  "description": "",
  "main": "/src/main.js",
  "scripts": {
    "start": "npm run dev",
    "dev": "webpack serve  --config ./config/webpack.dev.js",
    "build": "webpack  --config ./config/webpack.prod.js ",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.22.1",
    "@babel/preset-env": "^7.22.2",
    "babel-loader": "^9.1.2",
    "css-loader": "^6.7.4",
    "eslint": "^8.41.0",
    "eslint-webpack-plugin": "^4.0.1",
    "html-webpack-plugin": "^5.5.1",
    "less": "^4.1.3",
    "less-loader": "^11.1.0",
    "sass": "^1.62.1",
    "sass-loader": "^13.3.0",
    "style-loader": "^3.3.3",
    "webpack": "^5.84.1",
    "webpack-cli": "^5.1.1",
    "webpack-dev-server": "^4.15.0"
  }
}

image.png

image.png

2.14 处理css

2.14.1 抽取css为单独文件
  • 安装依赖
npm install --save-dev mini-css-extract-plugin
  • 配置webpack.config.js
// webpack.config.js

// 引入 css 处理模块,可以将css提取到单独的文件中
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

  plugins: [
    // css 抽取模块
    new MiniCssExtractPlugin({
      filename: "css/[hash].css",
    }),
  ],
    module: {
    rules: [
      {
        // 匹配 .css 结尾的文件
        test: /\.css$/i,
        use: [
          // use使用时,是从右到左,从下到上解析
          // 即先使用 css-loader 将css解析为commonJs到js文件中,
          // 在使用 style-loader 创建 style 标签将css引入到index.html 文件中
          MiniCssExtractPlugin.loader,
          "css-loader",
        ],
      },
      {
        // 匹配 .less 结尾的文件
        test: /\.less$/i,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          // 将 less 转换为 css
          "less-loader",
        ],
      },
      {
        test: /\.s[ac]ss$/i,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          // 将 Sass/Scss 编译成 CSS
          "sass-loader",
        ],
      },
    ],
  },
  • 效果

image.png

2.14.2 css兼容处理
  • 安装依赖
npm install --save-dev postcss-loader postcss postcss-preset-env
  • 配置webpack.config.js
// 定义公用方法
const getStyleLoader = (pre) => {
  return [
    MiniCssExtractPlugin.loader,
    "css-loader",
    {
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: [
            [
              "postcss-preset-env",
              {
                // 其他选项
              },
            ],
          ],
        },
      },
    },
    pre,
  ].filter(Boolean);
};


  module: {
    rules: [
      {
        // 匹配 .css 结尾的文件
        test: /\.css$/i,
        use: getStyleLoader(),
      },
      {
        // 匹配 .less 结尾的文件
        test: /\.less$/i,
        use: getStyleLoader("less-loader"),
      },
      {
        test: /\.s[ac]ss$/i,
        use: getStyleLoader("sass-loader"),
      },
    ],
  },
  • 配置package.json
  "browserslist": [
    "ie >= 8"
  ]
  
  // 通常配置为
    "browserslist": [
    "last 2 version",
    "> 1%",
    "not dead"
  ]
  • 效果

image.png

2.15 css压缩

  • 安装依赖
npm install css-minimizer-webpack-plugin --save-dev
  • 配置webpack.config.js
// 引入 css 代码压缩插件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");


  plugins: [
    new CssMinimizerPlugin(),
  ],

三、webpack高级配置

3.1 提高开发体验

3.1.1 错误调试 sourceMap
  • SourceMap的值有很多种,但我们一般关心两种
    • cheap-module-source-map
      • 优点:打包编译速度快,只包含行映射
      • 缺点:没有列映射
      • 即我们查看报错信息时,只知道是哪一行报错了,但不知道是哪一列
    • source-map
      • 优点:包含行/列映射
      • 缺点:打包编译速度更慢
  • 一般情况下,我们只在开发环境配置,生产环境一般不允许配置文件映射,可能会被别人反编译找代码漏洞
  • 具体配置
  devtool: "source-map",
  • 可以看到每个文件都有对应的map映射文件

image.png

3.1.2 HMR(热替换HotModuleReplacementPlugin)

当某个文件被修改了,只会重新编译这个文件,并替换掉,即页面不会刷新

  • 不适合生产模式
  • 实际上webpack默认启用了HMR功能
    • css使用 style-loader时,里面默认支持HMR功能
    • js默认不支持HMR,需要手动去配置
  • 若使用vue项目
    • 可以使用vue-loader,内部已经集成了HMR
// webpack.config.js
devServer:{
    hot:true, // 是否开启HMR功能 默认为true
}

  • js配置
// main.js

if(module.hot){
    // 判断是否支持热模块替换功能
    module.hot.appect('./js/count');
    module.hot.appect('./js/sum',function(){})
}

3.2 提高打包构建速度

3.2.1 rule.oneOf

webpack.docschina.org/configurati…

  • 在webpack.config.js中的loader处理规则中,每个文件都是从第一个规则开始匹配,当匹配到对应的规则后,仍然会继续向下匹配,当文件变多的时候,会在一定程度上延长构建的速度
  • 此时我们可以通过oneOf让每个文件只接受一次loader处理
// webpack.config.js

module.export = {
    module:{
        rules:[
            {
                // 以下的规则匹配每个文件只会触发一次
                oneOf:[
                  {
                    // 匹配 .css 结尾的文件
                    test: /\.css$/i,
                    use: getStyleLoader(),
                  },
                  {
                    // 匹配 .less 结尾的文件
                    test: /\.less$/i,
                    use: getStyleLoader("less-loader"),
                  },
                  {
                    test: /\.s[ac]ss$/i,
                    use: getStyleLoader("sass-loader"),
                  },
                ]
            }
        ]
    }
}
3.2.2 include/exclude(包含/排除)
  • loader、plugins等都可以通过includeexclude对文件进行包含/排除
    • 但这两个在同一个地方只能存在一个,若同时存在会报错
module.export = {
    module:{
        rules:{
          {
            test: /\.js$/,
            // 排除node_modules目录
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: ["@babel/preset-env"],
              },
            },
          },
        }
    },
    plugins:[
        new ESLintPlugin({ 
            // 配置哪些文件要进行检查 
            context: path.resolve(__dirname, "../src"),
            include:path.resolve(__dirname,'../src')
       }),
    ]
}
3.2.3 Eslit和Babel的缓存功能
  • 构建项目的时候,绝大部分时间花在Eslint代码检查和Babel代码兼容转换过程中
  • 我们可以为Eslint和Babel开启缓存
    • 即第一次构建速度不变
    • 但第二次及之后的构建因为有缓存,所以一定程度上提高了

image.png

3.2.4 Eslint、Babel、Terser多进程打包处理
  • 提升打包速度,其实就是提升js的打包速度,因为其他文件都比较少

  • 而对js文件处理的主要就是eslint、babel、terser三个工具,所以我们要提升他们的运行速度

  • 可以开启多进程同时处理js文件

  • 安装依赖

npm i thread-loader -D
  • 配置
// 引入node模块
const os = require('os')
// cpu核数
const threads = os.cpus().length;
// 这个插件默认已经安装了,直接引入就好
const TerserWebpackPlugin = require('terser-webpack-plugin');

  module: {
    rules: [
    // 配置babel转换器
      {
        test: /\.js$/,
        // 排除node_modules目录
        exclude: /node_modules/,
        use: [
            {
                // 开启多进程
                loader:'thread-loader',
                options:{
                    // 进程数量
                    works:threads,
                }
            },
            {
              loader: "babel-loader",
              options: {
                 // presets: ["@babel/preset-env"],
                 // 开启babel缓存
                 cacheDirectory:true,
                 // 关闭缓存文件压缩
                 cacheCompression:false,
              },
            },
        ]
      },
    ],
  },
  // plugins配置中添加eslint
  plugins: [
    new ESLintPlugin({
      // 配置哪些文件要进行检查
      context: path.resolve(__dirname, "src"),
      exclude:'node_modules', // 默认值
      cache:true, // 开启缓存
      // 配置eslint缓存文件地址
      cacheLocation:path.resolve(__dirname,'../node_modules/.cache/eslintcache'),
      threads, // 进程数
    }),
    // 压缩js,这个配置默认就有了,只是要配置进程数,所以重新写配置
    new TerserWebpackPlugin({
        parallel:threads, // 开启多进程和设置进程数量
    })
  ],

3.3 减少代码体积

3.3.1 Tree Shaking
  • 可以用于移除javaScript中没有使用上的代码
    • 如 一个js文件中定义了多个方法,但main.js中只引用并使用了其中一个方法,则打包时仅打包编译那个引用的方法,其他方法不打包编译
  • 注意:它依赖于 ES Module
  • 故在commonJs中无法使用
  • webpack默认已经支持并开启了该功能
3.3.2 Babel
  • Babel为编译的每个文件都插入了辅助代码,使代码体积过大
  • Babel对一些公用方法使用了非常小的辅助代码,比如_extend。默认情况下会被添加到每一个需要它的文件中(即重复引入)
  • 我们可以将这些辅助代码作为一个独立模块,来避免重复引入
  • @babel/plugin-transform-runtime
    • 禁用了Babel自动对每个文件的runtime注入,而是引入
    • 并且使所有辅助代码从这里引用
  • 安装依赖
npm i @babel/plugin-transform-runtime -D
  • 配置
  module: {
    rules: [
    // 配置babel转换器
      {
        test: /\.js$/,
        // 排除node_modules目录
        exclude: /node_modules/,
        use: [
            {
                // 开启多进程
                loader:'thread-loader',
                options:{
                    // 进程数量
                    works:threads,
                }
            },
            {
              loader: "babel-loader",
              options: {
                 // presets: ["@babel/preset-env"],
                 // 开启babel缓存
                 cacheDirectory:true,
                 // 关闭缓存文件压缩
                 cacheCompression:false,
                 plugins:['@babel/plugin-transform-runtime'], // 减少代码体积
              },
            },
        ]
      },
    ],
  },
3.3.3 图片压缩

image.png

3.4 优化代码运行性能

3.4.1 Code Split
  • 打包代码时会将所有的js文件打包到一个文件中,体积太大了,如果只需要渲染首页,就应该只加载首页的js文件,其他文件不应该加载
  • 所以我们需要将打包生成的文件进行代码分割,生成多个js文件,渲染那个页面就加载哪个js文件,这样加载的资源就越少,速度就更快
  • 代码分割:
    • 分割文件:将打包生成的文件进行分割,生成多个js文件
    • 按需加载:需要哪个文件就加载哪个文件
3.4.1.1 多入口
  • 创建一个测试项目
  • 安装依赖包
npm i html-webpack-plugin webpack webpack-cli -D

image.png

3.4.1.2 多入口提取公用模块
  • 当存在多个入口文件时
    • 多个入口文件同时引用一份公用代码
    • 会导致该代码每个入口文件都打包一次
  • 我们可以通过配置将公用部分进行抽取,减少代码体积
    • 通过webpack.config.js中的optimization.splitChunks进行配置

image.png

image.png

3.4.2 preload/prefetch 预加载资源

image.png

3.4.3 文件映射提取
  • 在没有配置之前,
    • 我们在a文件引用b文件的方法
    • 当b文件发生变动,a文件没有发生变动
  • 当我们打包时,如果文件名是用的hash或其他可变动的名称来命名
    • 由于a文件中引用了b文件,当b文件名变化后,a文件的引入也会变
    • 故打包时,a文件和b文件都重新编译并打包,会影响构建编译速度
  • 解决方案
    • 将文件引用映射抽取出来,这样每次修改b文件时,只会重新构建映射文件和修改的b文件
  • 配置

image.png

image.png

3.4.4 core-js解决js兼容性问题

image.png

  • 安装依赖
    • 前提是配置了babel
    • 按需自动导入
npm i core-js -D
npm install -D babel-loader @babel/core @babel/preset-env
  • 配置
// webpack.config.js

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: {
    app: "./src/app.js",
    main: "./src/main.js",
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "[name].js",
    clean: true,
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "./public/index.html"),
    }),
  ],
  optimization: {
  // 配置分包,将node_module分包打包
    splitChunks: {
      chunks: "all",
    },
  },
  module: {
    rules: [
      // 使用babel
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
        },
      },
    ],
  },
  mode: "production",
};

// babel.config.js

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        // usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加。
        useBuiltIns: "usage",
        // 指定用的corejs版本
        corejs: 3,
      },
    ],
  ],
};

  • 效果

image.png image.png

3.4.5 PWA技术(网络离线访问页面)

开发web App项目,项目一旦处于网络离线的情况,就没法访问了,此时我们希望项目提供离线访问体验

  • 渐进式网络应用程序(progressive web application - PWA)
    • 是一种可以提供类似于native app(原生应用程序)体验的web App技术
    • 其中最重要的是在离线时应用程序仍能够继续运行
    • 内部通过Service Workers技术实现
    • webpack.docschina.org/guides/prog…
  • 安装依赖
npm install workbox-webpack-plugin --save-dev
  • 配置
// webpack.config.js

const WorkboxPlugin = require('workbox-webpack-plugin');

  plugins: [
    // 使用插件
    new WorkboxPlugin.GenerateSW({
      // 这些选项帮助快速启用 ServiceWorkers
      // 不允许遗留任何“旧的” ServiceWorkers
      clientsClaim: true,
      skipWaiting: true,
    }),
  ],
  • 注册Service Workers
// main.js

// 注册 service works
if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker
      .register("/service-worker.js")
      .then((registration) => {
        console.log("SW registered: ", registration);
      })
      .catch((registrationError) => {
        console.log("SW registration failed: ", registrationError);
      });
  });
}
  • 查看
    • 需要部署服务端环境
    • 若没有部署环境
      • 可以使用http-server插件
      • npm i http-server -g 安装插件
      • 在dist目录下运行http-server

image.png

  • 第一次进入后,此时把网络断开,仍然能访问

image.png

image.png

webpack配置文档

待更新