webpack5打包工具的使用

news/2024/4/20 18:09:20/文章来源:https://blog.csdn.net/weixin_43526403/article/details/129118174

目录

-----------------------------基础篇-------------------------------

一、为什么需要打包工具

二、基本使用

1、模式

2、使用步骤

三、基本配置

1、五大核心概念

2、准备 Webpack 配置文件

3、运行指令

四、开发模式

五、处理样式资源

1、处理CSS资源

2、处理less资源

3、处理sass和scss资源

4、处理stylus资源

六、处理图片资源

七、修改输出资源的名称和路径(打包资源文件的分类)

八、自动清空上次打包资源

九、处理字体图标资源

十、处理其他资源

十一、处理js资源

1、Eslint

2、Babel

十二、处理html资源

十三、开发服务器和自动化

十四、生产模式介绍

2.1、webpack.dev.js

2.2、webpack.prod.js

2.3、配置运行指令

十五、生产模式配置---css处理

1、提取 Css 成单独文件

2、Css 兼容性处理

3、css压缩

十六、生产模式配置---html压缩

-----------------------------高级篇-------------------------------

一、提升开发体验

二、提升打包构建速度

1、HotModuleReplacement

2、OneOf

3、Include/Exclude

4、Cache

5、Thead

三、减少代码体积

1、Tree Shaking

2、Babel

3、Image Minimizer  图片压缩

四、优化代码运行性能

1、Code Split

(1)多入口

(2)多入口提取公共模块

(3)多入口按需加载,动态导入

(4)单入口

2、Preload / Prefetch

3、Network Cache

4、Core-js

5、PWA

-----------------------------项目篇-------------------------------

一、开发模式配置

二、生产模式配置

三、其他配置

package.json

.eslintrc.js

babel.config.js

四、合并开发和生产配置

五、优化配置

-----------------------------原理篇-------------------------------

一、loader原理

二、plugin原理


-----------------------------基础篇-------------------------------

一、为什么需要打包工具

开发时,我们会使用框架(React、Vue),ES6 模块化语法,Less/Sass 等 css 预处理器等语法进行开发。这样的代码要想在浏览器运行必须经过编译成浏览器能识别的 JS、Css 等语法,才能运行。所以我们需要打包工具帮我们做完这些事。除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等

打包工具:

  • Grunt
  • Gulp
  • Parcel
  • Webpack
  • Rollup
  • Vite
  • ...

二、基本使用

Webpack 是一个静态资源打包工具。它会以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去。输出的文件就是编译好的文件,就可以在浏览器段运行了。我们将 Webpack 输出的文件叫做 bundle

1、模式

  • 开发模式:仅能编译 JS 中的 ES Module 语法
  • 生产模式:能编译 JS 中的 ES Module 语法,还能压缩 JS 代码

2、使用步骤

2.1、创建项目文件src

2.2、下载依赖

(1)初始化package.json

打开终端,来到项目根目录。运行以下指令:

npm init -y

此时会生成一个基础的 package.json 文件。需要注意的是 package.json 中 name 字段不能叫做 webpack, 否则下一步会报错。

 (2)下载依赖webpack和webpack-cli

npm i webpack webpack-cli -D

webpack是打包工具,webpack-cli是webpack指令

(3)执行webpack指令,进行打包:

npx webpack ./src/main.js --mode=模式(开发模式development/生产模式production)

npx指令会将node_module下的.bin临时添加为环境变量,我们就可以访问环境变量里的程序

 打包成功的文件输出在dist文件夹里

三、基本配置

1、五大核心概念

1.1、entry(入口)

指示 Webpack 从哪个文件开始打包

1.2、output(输出)

指示 Webpack 打包完的文件输出到哪里去,如何命名等

1.3、loader(加载器)

webpack 本身只能处理 js、json 等资源,其他资源需要借助 loader,Webpack 才能解析

1.4、plugins(插件)

扩展 Webpack 的功能

1.5、mode(模式)

  • 开发模式:development
  • 生产模式:production

2、准备 Webpack 配置文件

在项目根目录下新建文件:webpack.config.js

所有配置文件都是在node.js平台运行的,采用的模块化都是CommonJS模块化

const path = require("path")  // node.js核心模块,专门用来处理核心问题module.exports = {// 入口entry: "./src/main.js",  // 要求用相对路径// 输出output: {path: path.resolve(__dirname, "dist"),   // 文件输出路径,要求用绝对路径,需要用到node.js的path模块来处理路径问题// path的resolve方法会返回一个绝对路径,有两个参数,第一个参数__dirname表示当前文件夹的文件夹目录,第二个参数表示文件名filename:"main.js",  // 文件名称
},// 加载器module: {rules: [  // loader的配置],},// 插件plugins: [],// 模式mode: "development",
};

3、运行指令

npx webpack

plugins中没有东西打包会报错,可以注释掉

四、开发模式

先开发开发模式,在开发生产模式。

开发模式顾名思义就是我们开发代码时使用的模式。这个模式下我们主要做两件事:

  1. 编译代码,使浏览器能识别运行:开发时我们有样式资源、字体图标、图片资源、html 资源等,webpack 默认都不能处理这些资源,所以我们要加载配置来编译这些资源
  2. 代码质量检查,树立代码规范:提前检查代码的一些隐患,让代码运行时能更加健壮。提前检查代码规范和格式,统一团队编码风格,让代码更优雅美观。

五、处理样式资源

Webpack 本身是不能识别样式资源的,所以我们需要借助 Loader 来帮助 Webpack 解析样式资源我们找 Loader 都应该去官方文档(Loaders | webpack 中文文档 (docschina.org))中找到对应的 Loader。使用官方文档找不到的话,可以从社区 Github 中搜索查询。

1、处理CSS资源

1.1、下载包

npm i css-loader style-loader -D
  • css-loader:负责将 Css 文件编译成 Webpack 能识别的模块
  • style-loader:会动态创建一个 Style 标签,里面放置 Webpack 中 Css 模块内容

1.2、在main.js里引入css资源

1.3、配置webpack配置文件

const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "main.js",},module: {rules: [  // loader的配置{test: /\.css$/,  // 正则表达式表明用来匹配 .css 结尾的文件use: ["style-loader", "css-loader"],  // use 数组里面 Loader 执行顺序是从右到左// style-loader:将js中css通过创建style标签添加html文件中生效// css-loader:将css资源编译成commonjs的模块到js中},],},plugins: [],mode: "development",
};

2、处理less资源

2.1、下载包

npm i less less-loader -D
  • less-loader:负责将 Less 文件编译成 Css 文件

2.2、在main.js里引入less资源

2.3、配置文件

const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "main.js",},module: {rules: [{test: /\.css$/,use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},],},plugins: [],mode: "development",
};

3、处理sass和scss资源

3.1、下载包

npm i sass-loader sass -D
  • sass-loader:负责将 Sass 文件编译成 css 文件
  • sasssass-loader 依赖 sass 进行编译

3.2、在main.js里引入sass/scss资源

3.3、配置文件

const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "main.js",},module: {rules: [{test: /\.css$/,use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,   // s[ac]ss=sass/scssuse: ["style-loader", "css-loader", "sass-loader"],},],},plugins: [],mode: "development",
};

4、处理stylus资源

4.1、下载包

npm i stylus-loader stylus -D
  • stylus-loader:负责将 Styl 文件编译成 Css 文件
  • stylus:stylus-loader 依赖 stylus 进行编译

4.2、在main.js里引入stylus资源

4.3、配置文件

const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "main.js",},module: {rules: [{test: /\.css$/,use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},],},plugins: [],mode: "development",
};

六、处理图片资源

过去在 Webpack4 时,我们处理图片资源通过 file-loader 和 url-loader 进行处理。现在 Webpack5 已经将两个 Loader 功能内置到 Webpack 里了,我们只需要简单配置即可处理图片资源。

对于小图片,会将其转换成base64的代码,这样转换后图片体积会更小或者大一丢。这样做可以减少请求数量,减轻服务器压力。

const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "main.js",},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024 // 小于10kb的图片会被base64处理}}},],},plugins: [],mode: "development",
};

七、修改输出资源的名称和路径(打包资源文件的分类)

为了分类管理资源

const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中,其他资源还是在dist文件夹里},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},generator: {  // 修改图片打包输出的路径和文件名// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},],},plugins: [],mode: "development",
};
├── dist└── static├── imgs│    └── 7003350e.png└── js└── main.js

八、自动清空上次打包资源

配置文件里output添加clean属性,原理是在打包前将path整个目录内容清空再进行打包

const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "static/js/main.js",clean: true, // 自动将上次打包目录资源清空},module: {rules: [{test: /\.css$/,use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 40 * 1024, // 小于40kb的图片会被base64处理},},generator: {filename: "static/imgs/[hash:8][ext][query]",},},],},plugins: [],mode: "development",
};

九、处理字体图标资源

Unicode兼容性更好,用法上font class更简单

将字体图标资源放入项目文件夹里:

type: "asset/resource"type: "asset"的区别:

  1. type: "asset/resource" 相当于file-loader, 将文件转化成 Webpack 能识别的资源,其他不做处理
  2. type: "asset" 相当于url-loader, 将文件转化成 Webpack 能识别的资源,同时小于某个大小的资源会处理成 data URI 形式
const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true, // 自动将上次打包目录资源清空},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {filename: "static/imgs/[hash:8][ext][query]",},},{   // 处理字体图标资源test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},],},plugins: [],mode: "development",
};

十、处理其他资源

处理字体图标资源基础上增加其他文件类型(如:音视频),统一处理即可

const path = require("path");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true, // 自动将上次打包目录资源清空},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?|mp4|mp3|avi)$/,   // 处理字体图标资源基础上增加其他文件类型,统一处理即可type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},],},plugins: [],mode: "development",
};

十一、处理js资源

js 资源 Webpack 不能已经处理了吗,为什么我们还要处理呢?原因是 Webpack 对 js 处理是有限的,只能编译 js 中 ES 模块化语法,不能编译其他语法,导致 js 不能在 IE 等浏览器运行,所以我们希望做一些兼容性处理。

其次开发中,团队对代码格式是有严格要求的,我们不能由肉眼去检测代码格式,需要使用专业的工具来检测。

  • 针对 js 兼容性处理,我们使用 Babel 来完成
  • 针对代码格式,我们使用 Eslint 来完成

我们先完成 Eslint,检测代码格式无误后,在由 Babel 做代码兼容性处理

1、Eslint

1.1、什么是Eslint

可组装的 JavaScript 和 JSX 检查工具。它是用来检测 js 和 jsx 语法的工具,可以配置各项功能

我们使用 Eslint,关键是写 Eslint 配置文件,里面写上各种 rules 规则,将来运行 Eslint 时就会以写的规则对代码进行检查。

1.2、Eslint配置文件

方式一:新建文件,位于项目根目录

文件名称可以是:.eslintrc.* /    .eslintrc / .eslintrc.js / .eslintrc.json

方式二:package.json 中 eslintConfig:不需要创建文件,在原有文件基础上写

文件配置规则(https://eslint.bootcss.com/docs/user-guide/configuring)

由于js文件比较方便使用,下面配置.eslintrc.js文件,基本配置如下:

module.exports = {// 解析选项parserOptions: {},// 具体检查规则rules: {},// 继承其他规则extends: [],// ...
};
module.exports = {// 解析选项parserOptions: {ecmaVersion: 6, // ES 语法版本sourceType: "module", // ES 模块化ecmaFeatures: { // ES 其他特性jsx: true // 如果是 React 项目,就需要开启 jsx 语法}
}// 具体检查规则rules: {semi: "error", // 禁止使用分号'array-callback-return': 'warn', // 强制数组方法的回调函数中有 return 语句,否则警告'default-case': ['warn', // 要求 switch 语句中有 default 分支,否则警告{ commentPattern: '^no default$' } // 允许在最后注释 no default, 就不会有警告了],eqeqeq: ['warn', // 强制使用 === 和 !==,否则警告'smart' // https://eslint.bootcss.com/docs/rules/eqeqeq#smart 除了少数情况下不会有警告],},// 继承其他规则extends: [],// ...
};

rules:

  • "off" 或 0 - 关闭规则
  • "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  • "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)

继承:Vue Cli 官方的规则open in new window:plugin:vue/essential

1.3、在webpack中使用

(1)下载包

npm i eslint-webpack-plugin eslint -D

(2)配置文件

先引入插件,在可以使用

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true, // 自动将上次打包目录资源清空},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录,检测哪些文件context: path.resolve(__dirname, "src"),}),],mode: "development",
};

(3).eslintignore

会对项目所有文件默认进行 Eslint 检查了,我们 dist 目录下的打包后文件就会报错。但是我们只需要检查 src 下面的文件,不需要检查 dist 下面的文件。可以使用 Eslint 忽略文件解决。在项目根目录新建下面文件:

# 忽略dist目录下所有文件
dist

2、Babel

JavaScript 编译器。主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中

2.1、 配置文件

配置文件由很多种写法:

  • babel.config.*:新建文件,位于项目根目录
    • babel.config.js
    • babel.config.json
  • .babelrc.*:新建文件,位于项目根目录
    • .babelrc
    • .babelrc.js
    • .babelrc.json
  • package.json 中 babel:不需要创建文件,在原有文件基础上写

Babel 会查找和自动读取它们,所以以上配置文件只需要存在一个即可

module.exports = {// 预设presets: [],
};

presets 预设。简单理解:就是一组 Babel 插件, 扩展 Babel 功能

  • @babel/preset-env: 一个智能预设,允许您使用最新的 JavaScript。
  • @babel/preset-react:一个用来编译 React jsx 语法的预设
  • @babel/preset-typescript:一个用来编译 TypeScript 语法的预设

2、在webpack中使用

(1)下载包

npm i babel-loader @babel/core @babel/preset-env -D

(2)配置文件

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true, // 自动将上次打包目录资源清空},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{ // 配置babeltest: /\.js$/,   exclude: /node_modules/, // 排除node_modules代码不编译loader: "babel-loader",options: {  // 可以在webpack里写,也可以单独写在babel配置文件里presets: ["@babel/preset-env"]},},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "src"),}),],mode: "development",
};

将presets单独写在babel配置文件里:

module.exports = {presets: ["@babel/preset-env"],
};

十二、处理html资源

对于多个js文件,如果都要手动引入到html里,再打包,就太麻烦了,我们希望引入一个插件HtmlWebpackPlugin来处理。

1、下载包

npm i html-webpack-plugin -D

2、配置

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true, // 自动将上次打包目录资源清空},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,exclude: /node_modules/, // 排除node_modules代码不编译loader: "babel-loader",},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "src"),}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "public/index.html"),  // 没有这一句,打包文件index.html里就没有html等别的资源了}),],mode: "development",
};

十三、开发服务器和自动化

每次更新完代码打包时都要重新编译才能起效果,有点麻烦。我们希望利用一个配置devServer,使得每次更新完代码都会自动打包

1、下载包

npm i webpack-dev-server -D

2、配置文件

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "dist"),filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true, // 自动将上次打包目录资源清空},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,exclude: /node_modules/, // 排除node_modules代码不编译loader: "babel-loader",},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "src"),}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "public/index.html"),}),],// 开发服务器:在dist文件夹里不会输出资源,资源是在内存中编译打包的devServer: {host: "localhost", // 启动服务器域名port: "3000", // 启动服务器端口号open: true, // 是否自动打开浏览器},mode: "development",
};

3、打包指令

npx webpack serve

十四、生产模式介绍

生产模式是开发完成代码后,我们需要得到代码将来部署上线。这个模式下我们主要对代码进行优化,让其运行性能更好。优化主要从两个角度出发:

  1. 优化代码运行性能
  2. 优化代码打包速度

1、在根目录下创建config文件夹,存放生产模式下的打包资源(webpack.prod.js)和开发环境下的打包资源(webpack.dev.js)

2、修改配置文件

2.1、webpack.dev.js

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: undefined, // 开发模式没有输出,不需要指定输出目录filename: "static/js/main.js", // clean: true, // 开发模式没有输出,不需要清空输出结果},module: {rules: [{test: /\.css$/,use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, },},generator: {filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]", },},{test: /\.js$/,exclude: /node_modules/,loader: "babel-loader",},],},plugins: [new ESLintWebpackPlugin({context: path.resolve(__dirname, "../src"),  // 绝对路径需要回退一层目录}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"),  // 绝对路径需要回退一层目录}),],devServer: {host: "localhost",port: "3000", open: true, },mode: "development",
};

运行指令:

npx webpack serve --config ./config/webpack.dev.js

2.2、webpack.prod.js

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/main.js",clean: true,},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,exclude: /node_modules/, loader: "babel-loader",},],},plugins: [new ESLintWebpackPlugin({context: path.resolve(__dirname, "../src"),  // 绝对路径要回退一层目录}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"),  // 绝对路径要回退一层目录}), ],// 生产模式不需要devServer// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",
};

运行指令:

npx webpack --config ./config/webpack.prod.js

2.3、配置运行指令

为了方便运行不同模式的指令,我们将指令定义在 package.json 中 scripts 里面。dev表示开发模式,build表示生产模式

// package.json
{// 其他省略"scripts": {"start": "npm run dev","dev": "npx webpack serve --config ./config/webpack.dev.js","build": "npx webpack --config ./config/webpack.prod.js"}
}

以后启动指令:

  • 开发模式:npm start 或 npm run dev
  • 生产模式:npm run build

十五、生产模式配置---css处理

1、提取 Css 成单独文件

Css 文件目前被打包到 js 文件中,当 js 文件加载时,会创建一个 style 标签来生成样式。这样对于网站来说,会出现闪屏现象,用户体验不好。我们应该是单独的 Css 文件,通过 link 标签加载性能才好

1.1、下载包

npm i mini-css-extract-plugin -D

1.2、配置 webpack.prod.js

不再使用style-loader,替换成MiniCssExtractPlugin.loader才会把css文件提取成单独的文件。

MiniCssExtractPlugin需要在插件处用new调用

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true,},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: [MiniCssExtractPlugin.loader, "css-loader"],},{test: /\.less$/,use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],},{test: /\.styl$/,use: [MiniCssExtractPlugin.loader, "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,exclude: /node_modules/, // 排除node_modules代码不编译loader: "babel-loader",},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/main.css",}),],// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",
};

1.3、运行指令

npm run build

2、Css 兼容性处理

2.1、下载包

npm i postcss-loader postcss postcss-preset-env -D

2.2、配置 webpack.prod.js

postcss-loader需要写在css-loader和css预处理器-loader之间

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true,},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},],},{test: /\.less$/,use: [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},"less-loader",],},{test: /\.s[ac]ss$/,use: [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},"sass-loader",],},{test: /\.styl$/,use: [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},"stylus-loader",],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,exclude: /node_modules/, // 排除node_modules代码不编译loader: "babel-loader",},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/main.css",}),],// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",
};

2.3、兼容到什么程度

我们可以在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度。

{"browserslist": ["ie >= 8"]
}

想要知道更多的 browserslist 配置,查看browserslist 文档

实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:

{"browserslist": ["last 2 version", "> 1%", "not dead"]
}

2.4、封装样式loader函数,替换rules里的MiniCssExtractPlugin.loader

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true,},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,exclude: /node_modules/, // 排除node_modules代码不编译loader: "babel-loader",},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/main.css",}),],// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",
};

3、css压缩

3.1、下载包

npm i css-minimizer-webpack-plugin -D

3.2、配置 webpack.prod.js

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true,},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,exclude: /node_modules/, // 排除node_modules代码不编译loader: "babel-loader",},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/main.css",}),// css压缩new CssMinimizerPlugin(),],// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",
};

十六、生产模式配置---html压缩

默认生产模式已经开启了:html 压缩和 js 压缩。不需要额外进行配置。

-----------------------------高级篇-------------------------------

所谓高级配置其实就是进行 Webpack 优化,让我们代码在编译/运行时性能更好。

一、提升开发体验

所有 css 和 js 合并成了一个文件,并且多了其他代码。此时如果代码运行出错那么提示代码错误位置我们是看不懂的。一旦将来开发代码文件很多,那么很难去发现错误出现在哪里。所以我们需要更加准确的错误提示,来帮助我们更好的开发代码。

1、SourceMap

SourceMap(源代码映射)是一个用来生成源代码与构建后代码一一映射的文件的方案。它会生成一个 xxx.map 文件,里面包含源代码和构建后代码每一行、每一列的映射关系。当构建后代码出错了,会通过 xxx.map 文件,从构建后代码出错位置找到映射后源代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快的找到错误根源。

1.1、如何使用

通过查看Webpack DevTool 文档open in new window可知,SourceMap 的值有很多种情况

(1)开发模式

cheap-module-source-map

  • 优点:打包编译速度快,只包含行映射
  • 缺点:没有列映射
module.exports = {// 其他省略mode: "development",devtool: "cheap-module-source-map",
};

(2)生产模式

source-map

  • 优点:包含行/列映射
  • 缺点:打包编译速度更慢
module.exports = {// 其他省略mode: "production",devtool: "source-map",
};

二、提升打包构建速度

1、HotModuleReplacement

HotModuleReplacement(HMR/热模块替换):在程序运行中,替换、添加或删除模块,而无需重新加载整个页面。

作用:开发时我们修改了其中一个模块代码,Webpack 默认会将所有模块全部重新打包编译,速度很慢。所以我们需要做到修改某个模块代码,就只有这个模块代码需要重新打包编译,其他模块不变,这样打包速度就能很快。

1.1、配置文件

module.exports = {// 其他省略devServer: {host: "localhost", // 启动服务器域名port: "3000", // 启动服务器端口号open: true, // 是否自动打开浏览器hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了)},
};

此时 css 样式经过 style-loader 处理,已经具备 HMR 功能了。 但是 js 还不行。所以js也要配置:

// main.js
import count from "./js/count";
import sum from "./js/sum";
// 引入资源,Webpack才会对其打包
import "./css/iconfont.css";
import "./css/index.css";
import "./less/index.less";
import "./sass/index.sass";
import "./sass/index.scss";
import "./styl/index.styl";const result1 = count(2, 1);
console.log(result1);
const result2 = sum(1, 2, 3, 4);
console.log(result2);// 判断是否支持HMR功能
if (module.hot) {module.hot.accept("./js/count.js", function (count) {const result1 = count(2, 1);console.log(result1);});module.hot.accept("./js/sum.js", function (sum) {const result2 = sum(1, 2, 3, 4);console.log(result2);});
}

上面这样写会很麻烦,所以实际开发我们会使用其他 loader 来解决。比如:vue-loaderopen in new window, react-hot-loaderopen in new window

2、OneOf

只能匹配上一个 loader, 剩下的就不匹配了。

作用:打包时每个文件都会经过所有 loader 处理,虽然因为 test 正则原因实际没有处理上,但是都要过一遍。比较慢。

2.1、配置

生产模式和开发模式都一样

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: undefined, // 开发模式没有输出,不需要指定输出目录filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中// clean: true, // 开发模式没有输出,不需要清空输出结果},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,exclude: /node_modules/, // 排除node_modules代码不编译loader: "babel-loader",},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),],// 开发服务器devServer: {host: "localhost", // 启动服务器域名port: "3000", // 启动服务器端口号open: true, // 是否自动打开浏览器hot: true, // 开启HMR功能},mode: "development",devtool: "cheap-module-source-map",
};

3、Include/Exclude

  • include

包含,只处理 xxx 文件

  • exclude

排除,除了 xxx 文件以外其他文件都处理

作用:开发时我们需要使用第三方的库或插件,所有文件都下载到 node_modules 中了。而这些文件是不需要编译可以直接使用的。所以我们在对 js 文件处理时,要排除 node_modules 下面的文件。

3.1、配置

开发模式和生产模式都一样

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: undefined, // 开发模式没有输出,不需要指定输出目录filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中// clean: true, // 开发模式没有输出,不需要清空输出结果},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,// exclude: /node_modules/, // 排除node_modules代码不编译include: path.resolve(__dirname, "../src"), // 也可以用包含loader: "babel-loader",},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),exclude: "node_modules", // 默认值}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),],// 开发服务器devServer: {host: "localhost", // 启动服务器域名port: "3000", // 启动服务器端口号open: true, // 是否自动打开浏览器hot: true, // 开启HMR功能},mode: "development",devtool: "cheap-module-source-map",
};

4、Cache

对 Eslint 检查 和 Babel 编译结果进行缓存。

作用:每次打包时 js 文件都要经过 Eslint 检查 和 Babel 编译,速度比较慢。我们可以缓存之前的 Eslint 检查 和 Babel 编译结果,这样第二次打包时速度就会更快了。

4.1、配置

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {entry: "./src/main.js",output: {path: undefined, // 开发模式没有输出,不需要指定输出目录filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中// clean: true, // 开发模式没有输出,不需要清空输出结果},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: ["style-loader", "css-loader"],},{test: /\.less$/,use: ["style-loader", "css-loader", "less-loader"],},{test: /\.s[ac]ss$/,use: ["style-loader", "css-loader", "sass-loader"],},{test: /\.styl$/,use: ["style-loader", "css-loader", "stylus-loader"],},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,// exclude: /node_modules/, // 排除node_modules代码不编译include: path.resolve(__dirname, "../src"), // 也可以用包含loader: "babel-loader",options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 关闭缓存文件压缩},},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),exclude: "node_modules", // 默认值cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),],// 开发服务器devServer: {host: "localhost", // 启动服务器域名port: "3000", // 启动服务器端口号open: true, // 是否自动打开浏览器hot: true, // 开启HMR功能},mode: "development",devtool: "cheap-module-source-map",
};

5、Thead

多进程打包:开启电脑的多个进程同时干一件事,速度更快。

需要注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为 600ms 左右开销。

作用:当项目越来越庞大时,打包速度越来越慢,甚至于需要一个下午才能打包出来代码。这个速度是比较慢的。我们想要继续提升打包速度,其实就是要提升 js 的打包速度,因为其他文件都比较少。而对 js 文件处理主要就是 eslint 、babel、Terser 三个工具,所以我们要提升它们的运行速度。我们可以开启多进程同时处理 js 文件,这样速度就比之前的单进程打包更快了。

5.1、使用

(1)获取cpu的核

// nodejs核心模块,直接使用
const os = require("os");
// cpu核数
const threads = os.cpus().length;

(2)下载包

npm i thread-loader -D

(3)配置

const os = require("os");
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");// cpu核数
const threads = os.cpus().length;// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true,},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,// exclude: /node_modules/, // 排除node_modules代码不编译include: path.resolve(__dirname, "../src"), // 也可以用包含use: [{loader: "thread-loader", // 开启多进程options: {workers: threads, // 数量},},{loader: "babel-loader",options: {cacheDirectory: true, // 开启babel编译缓存},},],},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),exclude: "node_modules", // 默认值cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),threads, // 开启多进程}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/main.css",}),// css压缩// new CssMinimizerPlugin(),],optimization: {minimize: true,minimizer: [// css压缩也可以写到optimization.minimizer里面,效果一样的new CssMinimizerPlugin(),// 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了new TerserPlugin({parallel: threads // 开启多进程})],},// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",devtool: "source-map",
};

三、减少代码体积

1、Tree Shaking

Tree Shaking 是一个术语,通常用于描述移除 JavaScript 中的没有使用上的代码。注意:它依赖 ES Module

作用:开发时我们定义了一些工具函数库,或者引用第三方工具函数库或组件库。如果没有特殊处理的话我们打包时会引入整个库,但是实际上可能我们可能只用上极小部分的功能。这样将整个库都打包进来,体积就太大了。

使用:Webpack 已经默认开启了这个功能,无需其他配置。

2、Babel

@babel/plugin-transform-runtime: 禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用

原因:Babel 为编译的每个文件都插入了辅助代码,使代码体积过大!Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。你可以将这些辅助代码作为一个独立模块,来避免重复引入。

使用:

(1)下载包

npm i @babel/plugin-transform-runtime -D

(2)配置

const os = require("os");
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");// cpu核数
const threads = os.cpus().length;// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true,},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|webp)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,// exclude: /node_modules/, // 排除node_modules代码不编译include: path.resolve(__dirname, "../src"), // 也可以用包含use: [{loader: "thread-loader", // 开启多进程options: {workers: threads, // 数量},},{loader: "babel-loader",options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积},},],},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),exclude: "node_modules", // 默认值cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),threads, // 开启多进程}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/main.css",}),// css压缩// new CssMinimizerPlugin(),],optimization: {minimizer: [// css压缩也可以写到optimization.minimizer里面,效果一样的new CssMinimizerPlugin(),// 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了new TerserPlugin({parallel: threads, // 开启多进程}),]],// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",devtool: "source-map",
};

3、Image Minimizer  图片压缩

image-minimizer-webpack-plugin: 用来压缩图片的插件。

原因:开发如果项目中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢。我们可以对图片进行压缩,减少图片体积。注意:如果项目中图片都是在线链接,那么就不需要了。本地项目静态图片才需要进行压缩

使用:

(1)下载包

npm i image-minimizer-webpack-plugin imagemin -D

无损压缩:

npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D

有损压缩:

npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D

(2)配置

const os = require("os");
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");// cpu核数
const threads = os.cpus().length;// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中clean: true,},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},generator: {// 将图片文件输出到 static/imgs 目录中// 将图片文件命名 [hash:8][ext][query]// [hash:8]: hash值取8位// [ext]: 使用之前的文件扩展名// [query]: 添加之前的query参数filename: "static/imgs/[hash:8][ext][query]",},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",generator: {filename: "static/media/[hash:8][ext][query]",},},{test: /\.js$/,// exclude: /node_modules/, // 排除node_modules代码不编译include: path.resolve(__dirname, "../src"), // 也可以用包含use: [{loader: "thread-loader", // 开启多进程options: {workers: threads, // 数量},},{loader: "babel-loader",options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积},},],},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),exclude: "node_modules", // 默认值cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),threads, // 开启多进程}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/main.css",}),// css压缩// new CssMinimizerPlugin(),],optimization: {minimizer: [// css压缩也可以写到optimization.minimizer里面,效果一样的new CssMinimizerPlugin(),// 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了new TerserPlugin({parallel: threads, // 开启多进程}),// 压缩图片new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],},// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",devtool: "source-map",
};

(4)打包时出错

我们需要安装两个文件到 node_modules 中才能解决, 文件可以从课件中找到:

  • jpegtran.exe

需要复制到 node_modules\jpegtran-bin\vendor 下面

  • optipng.exe

需要复制到 node_modules\optipng-bin\vendor 下面

四、优化代码运行性能

1、Code Split

代码分割(Code Split)主要做了两件事:

  1. 分割文件:将打包生成的文件进行分割,生成多个 js 文件。
  2. 按需加载:需要哪个文件就加载哪个文件。

原因:打包代码时会将所有 js 文件打包到一个文件中,体积太大了。我们如果只要渲染首页,就应该只加载首页的 js 文件,其他文件不应该加载。所以我们需要将打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样加载的资源就少,速度就更快。

使用:

(1)多入口

下载包:

npm i webpack webpack-cli html-webpack-plugin -D

配置:

// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {// 单入口// entry: './src/main.js',// 多入口entry: {main: "./src/main.js",app: "./src/app.js",},output: {path: path.resolve(__dirname, "./dist"),// [name]是webpack命名规则,使用chunk的name作为输出的文件名。// 什么是chunk?打包的资源就是chunk,输出出去叫bundle。// chunk的name是啥呢? 比如: entry中xxx: "./src/xxx.js", name就是xxx。注意是前面的xxx,和文件名无关。// 为什么需要这样命名呢?如果还是之前写法main.js,那么打包生成两个js文件都会叫做main.js会发生覆盖。(实际上会直接报错的)filename: "js/[name].js",clear: true,},plugins: [new HtmlWebpackPlugin({template: "./public/index.html",}),],mode: "production",
};

打包指令:

npx webpack

在 dist 目录我们能看到输出了两个 js 文件。

总结:配置了几个入口,至少输出几个 js 文件。

(2)多入口提取公共模块

如果多入口文件中都引用了同一份代码,我们不希望这份代码被打包到两个文件中,导致代码重复,体积更大。我们需要提取多入口的重复代码,只打包生成一个 js 文件,其他文件引用它就好。

// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {// 单入口// entry: './src/main.js',// 多入口entry: {main: "./src/main.js",app: "./src/app.js",},output: {path: path.resolve(__dirname, "./dist"),// [name]是webpack命名规则,使用chunk的name作为输出的文件名。// 什么是chunk?打包的资源就是chunk,输出出去叫bundle。// chunk的name是啥呢? 比如: entry中xxx: "./src/xxx.js", name就是xxx。注意是前面的xxx,和文件名无关。// 为什么需要这样命名呢?如果还是之前写法main.js,那么打包生成两个js文件都会叫做main.js会发生覆盖。(实际上会直接报错的)filename: "js/[name].js",clean: true,},plugins: [new HtmlWebpackPlugin({template: "./public/index.html",}),],mode: "production",optimization: {// 代码分割配置splitChunks: {chunks: "all", // 对所有模块都进行分割// 以下是默认值// minSize: 20000, // 分割代码最小的大小// minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0// minChunks: 1, // 至少被引用的次数,满足条件才会代码分割// maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量// maxInitialRequests: 30, // 入口js文件最大并行请求数量// enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)// cacheGroups: { // 组,哪些模块要打包到一个组//   defaultVendors: { // 组名//     test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块//     priority: -10, // 权重(越大越高)//     reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块//   },//   default: { // 其他没有写的配置会使用上面的默认值//     minChunks: 2, // 这里的minChunks权重更大//     priority: -20,//     reuseExistingChunk: true,//   },// },// 修改配置cacheGroups: {// 组,哪些模块要打包到一个组// defaultVendors: { // 组名//   test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块//   priority: -10, // 权重(越大越高)//   reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块// },default: {// 其他没有写的配置会使用上面的默认值minSize: 0, // 我们定义的文件体积太小了,所以要改打包的最小文件体积minChunks: 2,priority: -20,reuseExistingChunk: true,},},},},
};

(3)多入口按需加载,动态导入

作用:页面有一些一上来就用不到的资源,可以按需加载

一旦通过 import函数 动态导入语法导入模块,模块就被代码分割,同时也能按需加载了

(4)单入口

开发时我们可能是单页面应用(SPA),只有一个入口(单入口)。那么我们需要这样配置:

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {// 单入口entry: "./src/main.js",// 多入口// entry: {//   main: "./src/main.js",//   app: "./src/app.js",// },output: {path: path.resolve(__dirname, "./dist"),// [name]是webpack命名规则,使用chunk的name作为输出的文件名。// 什么是chunk?打包的资源就是chunk,输出出去叫bundle。// chunk的name是啥呢? 比如: entry中xxx: "./src/xxx.js", name就是xxx。注意是前面的xxx,和文件名无关。// 为什么需要这样命名呢?如果还是之前写法main.js,那么打包生成两个js文件都会叫做main.js会发生覆盖。(实际上会直接报错的)filename: "js/[name].js",clean: true,},plugins: [new HtmlWebpackPlugin({template: "./public/index.html",}),],mode: "production",optimization: {// 代码分割配置splitChunks: {chunks: "all", // 对所有模块都进行分割// 以下是默认值// minSize: 20000, // 分割代码最小的大小// minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0// minChunks: 1, // 至少被引用的次数,满足条件才会代码分割// maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量// maxInitialRequests: 30, // 入口js文件最大并行请求数量// enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)// cacheGroups: { // 组,哪些模块要打包到一个组//   defaultVendors: { // 组名//     test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块//     priority: -10, // 权重(越大越高)//     reuseExistingChunk: true, // 如果当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块//   },//   default: { // 其他没有写的配置会使用上面的默认值//     minChunks: 2, // 这里的minChunks权重更大//     priority: -20,//     reuseExistingChunk: true,//   },// },},
};

(5)

2、Preload / Prefetch

  • Preload:告诉浏览器立即加载资源。

  • Prefetch:告诉浏览器在空闲时才开始加载资源。

它们共同点:

  • 都只会加载资源,并不执行。
  • 都有缓存。

它们区别:

  • Preload加载优先级高,Prefetch加载优先级低。
  • Preload只能加载当前页面需要使用的资源,Prefetch可以加载当前页面资源,也可以加载下一个页面需要使用的资源

它们的问题:兼容性较差。Preload 相对于 Prefetch 兼容性好一点。

(1)下载包

npm i @vue/preload-webpack-plugin -D

(2)配置webpack.prod.js

const os = require("os");
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");// cpu核数
const threads = os.cpus().length;// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出filename: "static/js/[name].js", // 入口文件打包输出资源命名方式chunkFilename: "static/js/[name].chunk.js", // 动态导入输出资源命名方式assetModuleFilename: "static/media/[name].[hash][ext]", // 图片、字体等资源命名方式(注意用hash)clean: true,},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},// generator: {//   // 将图片文件输出到 static/imgs 目录中//   // 将图片文件命名 [hash:8][ext][query]//   // [hash:8]: hash值取8位//   // [ext]: 使用之前的文件扩展名//   // [query]: 添加之前的query参数//   filename: "static/imgs/[hash:8][ext][query]",// },},{test: /\.(ttf|woff2?)$/,type: "asset/resource",// generator: {//   filename: "static/media/[hash:8][ext][query]",// },},{test: /\.js$/,// exclude: /node_modules/, // 排除node_modules代码不编译include: path.resolve(__dirname, "../src"), // 也可以用包含use: [{loader: "thread-loader", // 开启多进程options: {workers: threads, // 数量},},{loader: "babel-loader",options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积},},],},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),exclude: "node_modules", // 默认值cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),threads, // 开启多进程}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/[name].css",chunkFilename: "static/css/[name].chunk.css",}),// css压缩// new CssMinimizerPlugin(),new PreloadWebpackPlugin({rel: "preload", // preload兼容性更好as: "script",// rel: 'prefetch' // prefetch兼容性更差}),],optimization: {minimizer: [// css压缩也可以写到optimization.minimizer里面,效果一样的new CssMinimizerPlugin(),// 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了new TerserPlugin({parallel: threads, // 开启多进程}),// 压缩图片new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],// 代码分割配置splitChunks: {chunks: "all", // 对所有模块都进行分割// 其他内容用默认配置即可},},// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",devtool: "source-map",
};

3、Network Cache

它们都会生成一个唯一的 hash 值。

  • fullhash(webpack4 是 hash)

每次修改任何一个文件,所有文件名的 hash 至都将改变。所以一旦修改了任何一个文件,整个项目的文件缓存都将失效。

  • chunkhash

根据不同的入口文件(Entry)进行依赖文件解析、构建对应的 chunk,生成对应的哈希值。我们 js 和 css 是同一个引入,会共享一个 hash 值。

  • contenthash

根据文件内容生成 hash 值,只有文件内容变化了,hash 值才会变化。所有文件 hash 值是独享且不同的。

原因:将来开发时我们对静态资源会使用缓存来优化,这样浏览器第二次请求资源就能读取缓存了,速度很快。但是这样的话就会有一个问题, 因为前后输出的文件名是一样的,都叫 main.js,一旦将来发布新版本,因为文件名没有变化导致浏览器会直接读取缓存,不会加载新资源,项目也就没法更新了。所以我们从文件名入手,确保更新前后文件名不一样,这样就可以做缓存了。

配置:

const os = require("os");
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");// cpu核数
const threads = os.cpus().length;// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出// [contenthash:8]使用contenthash,取8位长度filename: "static/js/[name].[contenthash:8].js", // 入口文件打包输出资源命名方式chunkFilename: "static/js/[name].[contenthash:8].chunk.js", // 动态导入输出资源命名方式assetModuleFilename: "static/media/[name].[hash][ext]", // 图片、字体等资源命名方式(注意用hash)clean: true,},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},// generator: {//   // 将图片文件输出到 static/imgs 目录中//   // 将图片文件命名 [hash:8][ext][query]//   // [hash:8]: hash值取8位//   // [ext]: 使用之前的文件扩展名//   // [query]: 添加之前的query参数//   filename: "static/imgs/[hash:8][ext][query]",// },},{test: /\.(ttf|woff2?)$/,type: "asset/resource",// generator: {//   filename: "static/media/[hash:8][ext][query]",// },},{test: /\.js$/,// exclude: /node_modules/, // 排除node_modules代码不编译include: path.resolve(__dirname, "../src"), // 也可以用包含use: [{loader: "thread-loader", // 开启多进程options: {workers: threads, // 数量},},{loader: "babel-loader",options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积},},],},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),exclude: "node_modules", // 默认值cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),threads, // 开启多进程}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/[name].[contenthash:8].css",chunkFilename: "static/css/[name].[contenthash:8].chunk.css",}),// css压缩// new CssMinimizerPlugin(),new PreloadWebpackPlugin({rel: "preload", // preload兼容性更好as: "script",// rel: 'prefetch' // prefetch兼容性更差}),],optimization: {minimizer: [// css压缩也可以写到optimization.minimizer里面,效果一样的new CssMinimizerPlugin(),// 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了new TerserPlugin({parallel: threads, // 开启多进程}),// 压缩图片new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],// 代码分割配置splitChunks: {chunks: "all", // 对所有模块都进行分割// 其他内容用默认配置即可},},// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",devtool: "source-map",
};

4、Core-js

core-js 是专门用来做 ES6 以及以上 API 的 polyfillpolyfill翻译过来叫做垫片/补丁。就是用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该新特性。

原因:过去我们使用 babel 对 js 代码进行了兼容性处理,其中使用@babel/preset-env 智能预设来处理兼容性问题。它能将 ES6 的一些语法进行编译转换,比如箭头函数、点点点运算符等。但是如果是 async 函数、promise 对象、数组的一些方法(includes)等,它没办法处理。所以此时我们 js 代码仍然存在兼容性问题,一旦遇到低版本浏览器会直接报错。所以我们想要将 js 兼容性问题彻底解决。

配置:

(1)下载包

npm i @babel/eslint-parser -D

(2).eslintrc.js

module.exports = {// 继承 Eslint 规则extends: ["eslint:recommended"],parser: "@babel/eslint-parser", // 支持最新的最终 ECMAScript 标准env: {node: true, // 启用node中全局变量browser: true, // 启用浏览器中全局变量},plugins: ["import"], // 解决动态导入import语法报错问题 --> 实际使用eslint-plugin-import的规则解决的parserOptions: {ecmaVersion: 6, // es6sourceType: "module", // es module},rules: {"no-var": 2, // 不能使用 var 定义变量},
};

此时观察打包输出的 js 文件,我们发现 Promise 语法并没有编译转换,所以我们需要使用 core-js 来进行 polyfill

(3)下载包

npm i core-js

(4)手动引入到js文件中:

import "core-js";

(5)babel.config.js:此时就会自动根据我们代码中使用的语法,来按需加载相应的 polyfill 了

module.exports = {// 智能预设:能够编译ES6语法presets: [["@babel/preset-env",// 按需加载core-js的polyfill{ useBuiltIns: "usage", corejs: { version: "3", proposals: true } },],],
};

5、PWA

渐进式网络应用程序(progressive web application - PWA):是一种可以提供类似于 native app(原生应用程序) 体验的 Web App 的技术。其中最重要的是,在 离线(offline) 时应用程序能够继续运行功能。内部通过 Service Workers 技术实现的。

原因:开发 Web App 项目,项目一旦处于网络离线情况,就没法访问了。我们希望给项目提供离线体验。

(1)下载包

npm i workbox-webpack-plugin -D

(2)配置

const os = require("os");
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");
const WorkboxPlugin = require("workbox-webpack-plugin");// cpu核数
const threads = os.cpus().length;// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: path.resolve(__dirname, "../dist"), // 生产模式需要输出// [contenthash:8]使用contenthash,取8位长度filename: "static/js/[name].[contenthash:8].js", // 入口文件打包输出资源命名方式chunkFilename: "static/js/[name].[contenthash:8].chunk.js", // 动态导入输出资源命名方式assetModuleFilename: "static/media/[name].[hash][ext]", // 图片、字体等资源命名方式(注意用hash)clean: true,},module: {rules: [{oneOf: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},// generator: {//   // 将图片文件输出到 static/imgs 目录中//   // 将图片文件命名 [hash:8][ext][query]//   // [hash:8]: hash值取8位//   // [ext]: 使用之前的文件扩展名//   // [query]: 添加之前的query参数//   filename: "static/imgs/[hash:8][ext][query]",// },},{test: /\.(ttf|woff2?)$/,type: "asset/resource",// generator: {//   filename: "static/media/[hash:8][ext][query]",// },},{test: /\.js$/,// exclude: /node_modules/, // 排除node_modules代码不编译include: path.resolve(__dirname, "../src"), // 也可以用包含use: [{loader: "thread-loader", // 开启多进程options: {workers: threads, // 数量},},{loader: "babel-loader",options: {cacheDirectory: true, // 开启babel编译缓存cacheCompression: false, // 缓存文件不要压缩plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积},},],},],},],},plugins: [new ESLintWebpackPlugin({// 指定检查文件的根目录context: path.resolve(__dirname, "../src"),exclude: "node_modules", // 默认值cache: true, // 开启缓存// 缓存目录cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),threads, // 开启多进程}),new HtmlWebpackPlugin({// 以 public/index.html 为模板创建文件// 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源template: path.resolve(__dirname, "../public/index.html"),}),// 提取css成单独文件new MiniCssExtractPlugin({// 定义输出文件名和目录filename: "static/css/[name].[contenthash:8].css",chunkFilename: "static/css/[name].[contenthash:8].chunk.css",}),// css压缩// new CssMinimizerPlugin(),new PreloadWebpackPlugin({rel: "preload", // preload兼容性更好as: "script",// rel: 'prefetch' // prefetch兼容性更差}),new WorkboxPlugin.GenerateSW({// 这些选项帮助快速启用 ServiceWorkers// 不允许遗留任何“旧的” ServiceWorkersclientsClaim: true,skipWaiting: true,}),],optimization: {minimizer: [// css压缩也可以写到optimization.minimizer里面,效果一样的new CssMinimizerPlugin(),// 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了new TerserPlugin({parallel: threads, // 开启多进程}),// 压缩图片new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],// 代码分割配置splitChunks: {chunks: "all", // 对所有模块都进行分割// 其他内容用默认配置即可},},// devServer: {//   host: "localhost", // 启动服务器域名//   port: "3000", // 启动服务器端口号//   open: true, // 是否自动打开浏览器// },mode: "production",devtool: "source-map",
};

此时如果直接通过 VSCode 访问打包后页面,在浏览器控制台会发现 SW registration failed。为了解决路径问题:

下载包:

npm i serve -g

运行指令:

serve dist

-----------------------------项目篇-------------------------------

使用前面所学的知识来从零开始搭建Vue-cli。

下载包:

npm i vue-loader vue-template-complier -D

一、开发模式配置

// webpack.dev.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const { DefinePlugin } = require("webpack");
const CopyPlugin = require("copy-webpack-plugin");const getStyleLoaders = (preProcessor) => {return ["vue-style-loader","css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: undefined,filename: "static/js/[name].js",chunkFilename: "static/js/[name].chunk.js",assetModuleFilename: "static/js/[hash:10][ext][query]",},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",},{test: /\.(jsx|js)$/,include: path.resolve(__dirname, "../src"),loader: "babel-loader",options: {cacheDirectory: true,cacheCompression: false,plugins: [// "@babel/plugin-transform-runtime" // presets中包含了],},},// vue-loader不支持oneOf{test: /\.vue$/,loader: "vue-loader", // 内部会给vue文件注入HMR功能代码options: {// 开启缓存cacheDirectory: path.resolve(__dirname,"node_modules/.cache/vue-loader"),},},],},plugins: [new ESLintWebpackPlugin({context: path.resolve(__dirname, "../src"),exclude: "node_modules",cache: true,cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"),}),new CopyPlugin({patterns: [{from: path.resolve(__dirname, "../public"),to: path.resolve(__dirname, "../dist"),toType: "dir",noErrorOnMissing: true,globOptions: {ignore: ["**/index.html"],},info: {minimized: true,},},],}),new VueLoaderPlugin(),// 解决页面警告new DefinePlugin({__VUE_OPTIONS_API__: "true",__VUE_PROD_DEVTOOLS__: "false",}),],optimization: {splitChunks: {chunks: "all",},runtimeChunk: {name: (entrypoint) => `runtime~${entrypoint.name}`,},},resolve: {extensions: [".vue", ".js", ".json"], // 自动补全文件扩展名,让vue可以使用},devServer: {open: true,host: "localhost",port: 3000,hot: true,compress: true,historyApiFallback: true, // 解决vue-router刷新404问题},mode: "development",devtool: "cheap-module-source-map",
};

二、生产模式配置

// webpack.prod.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const { DefinePlugin } = require("webpack");const getStyleLoaders = (preProcessor) => {return [MiniCssExtractPlugin.loader,"css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env", // 能解决大多数样式兼容性问题],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: undefined,filename: "static/js/[name].[contenthash:10].js",chunkFilename: "static/js/[name].[contenthash:10].chunk.js",assetModuleFilename: "static/js/[hash:10][ext][query]",clean: true,},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",},{test: /\.(jsx|js)$/,include: path.resolve(__dirname, "../src"),loader: "babel-loader",options: {cacheDirectory: true,cacheCompression: false,plugins: [// "@babel/plugin-transform-runtime" // presets中包含了],},},// vue-loader不支持oneOf{test: /\.vue$/,loader: "vue-loader", // 内部会给vue文件注入HMR功能代码options: {// 开启缓存cacheDirectory: path.resolve(__dirname,"node_modules/.cache/vue-loader"),},},],},plugins: [new ESLintWebpackPlugin({context: path.resolve(__dirname, "../src"),exclude: "node_modules",cache: true,cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"),}),new CopyPlugin({patterns: [{from: path.resolve(__dirname, "../public"),to: path.resolve(__dirname, "../dist"),toType: "dir",noErrorOnMissing: true,globOptions: {ignore: ["**/index.html"],},info: {minimized: true,},},],}),new MiniCssExtractPlugin({filename: "static/css/[name].[contenthash:10].css",chunkFilename: "static/css/[name].[contenthash:10].chunk.css",}),new VueLoaderPlugin(),new DefinePlugin({__VUE_OPTIONS_API__: "true",__VUE_PROD_DEVTOOLS__: "false",}),],optimization: {// 压缩的操作minimizer: [new CssMinimizerPlugin(),new TerserWebpackPlugin(),new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],splitChunks: {chunks: "all",},runtimeChunk: {name: (entrypoint) => `runtime~${entrypoint.name}`,},},resolve: {extensions: [".vue", ".js", ".json"],},mode: "production",devtool: "source-map",
};

三、其他配置

package.json

{"name": "vue-cli","version": "1.0.0","description": "","main": "main.js","scripts": {"start": "npm run dev","dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js","build": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"},"keywords": [],"author": "","license": "ISC","devDependencies": {"@babel/core": "^7.17.10","@babel/eslint-parser": "^7.17.0","@vue/cli-plugin-babel": "^5.0.4","babel-loader": "^8.2.5","copy-webpack-plugin": "^10.2.4","cross-env": "^7.0.3","css-loader": "^6.7.1","css-minimizer-webpack-plugin": "^3.4.1","eslint-plugin-vue": "^8.7.1","eslint-webpack-plugin": "^3.1.1","html-webpack-plugin": "^5.5.0","image-minimizer-webpack-plugin": "^3.2.3","imagemin": "^8.0.1","imagemin-gifsicle": "^7.0.0","imagemin-jpegtran": "^7.0.0","imagemin-optipng": "^8.0.0","imagemin-svgo": "^10.0.1","less-loader": "^10.2.0","mini-css-extract-plugin": "^2.6.0","postcss-preset-env": "^7.5.0","sass-loader": "^12.6.0","stylus-loader": "^6.2.0","vue-loader": "^17.0.0","vue-style-loader": "^4.1.3","vue-template-compiler": "^2.6.14","webpack": "^5.72.0","webpack-cli": "^4.9.2","webpack-dev-server": "^4.9.0"},"dependencies": {"vue": "^3.2.33","vue-router": "^4.0.15"},"browserslist": ["last 2 version", "> 1%", "not dead"]
}

.eslintrc.js

module.exports = {root: true,env: {node: true,},extends: ["plugin:vue/vue3-essential", "eslint:recommended"],parserOptions: {parser: "@babel/eslint-parser",},
};

babel.config.js

module.exports = {presets: ["@vue/cli-plugin-babel/preset"],
};

四、合并开发和生产配置

// webpack.config.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const { DefinePlugin } = require("webpack");
const CopyPlugin = require("copy-webpack-plugin");// 需要通过 cross-env 定义环境变量
const isProduction = process.env.NODE_ENV === "production";const getStyleLoaders = (preProcessor) => {return [isProduction ? MiniCssExtractPlugin.loader : "vue-style-loader","css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env"],},},},preProcessor,].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: isProduction ? path.resolve(__dirname, "../dist") : undefined,filename: isProduction? "static/js/[name].[contenthash:10].js": "static/js/[name].js",chunkFilename: isProduction? "static/js/[name].[contenthash:10].chunk.js": "static/js/[name].chunk.js",assetModuleFilename: "static/js/[hash:10][ext][query]",clean: true,},module: {rules: [{// 用来匹配 .css 结尾的文件test: /\.css$/,// use 数组里面 Loader 执行顺序是从右到左use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024, // 小于10kb的图片会被base64处理},},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",},{test: /\.(jsx|js)$/,include: path.resolve(__dirname, "../src"),loader: "babel-loader",options: {cacheDirectory: true,cacheCompression: false,plugins: [// "@babel/plugin-transform-runtime" // presets中包含了],},},// vue-loader不支持oneOf{test: /\.vue$/,loader: "vue-loader", // 内部会给vue文件注入HMR功能代码options: {// 开启缓存cacheDirectory: path.resolve(__dirname,"node_modules/.cache/vue-loader"),},},],},plugins: [new ESLintWebpackPlugin({context: path.resolve(__dirname, "../src"),exclude: "node_modules",cache: true,cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"),}),new CopyPlugin({patterns: [{from: path.resolve(__dirname, "../public"),to: path.resolve(__dirname, "../dist"),toType: "dir",noErrorOnMissing: true,globOptions: {ignore: ["**/index.html"],},info: {minimized: true,},},],}),isProduction &&new MiniCssExtractPlugin({filename: "static/css/[name].[contenthash:10].css",chunkFilename: "static/css/[name].[contenthash:10].chunk.css",}),new VueLoaderPlugin(),new DefinePlugin({__VUE_OPTIONS_API__: "true",__VUE_PROD_DEVTOOLS__: "false",}),].filter(Boolean),optimization: {minimize: isProduction,// 压缩的操作minimizer: [new CssMinimizerPlugin(),new TerserWebpackPlugin(),new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],splitChunks: {chunks: "all",},runtimeChunk: {name: (entrypoint) => `runtime~${entrypoint.name}`,},},resolve: {extensions: [".vue", ".js", ".json"],},devServer: {open: true,host: "localhost",port: 3000,hot: true,compress: true,historyApiFallback: true, // 解决vue-router刷新404问题},mode: isProduction ? "production" : "development",devtool: isProduction ? "source-map" : "cheap-module-source-map",
};

五、优化配置

const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const { DefinePlugin } = require("webpack");
const AutoImport = require("unplugin-auto-import/webpack");
const Components = require("unplugin-vue-components/webpack");
const { ElementPlusResolver } = require("unplugin-vue-components/resolvers");
// 需要通过 cross-env 定义环境变量
const isProduction = process.env.NODE_ENV === "production";const getStyleLoaders = (preProcessor) => {return [isProduction ? MiniCssExtractPlugin.loader : "vue-style-loader","css-loader",{loader: "postcss-loader",options: {postcssOptions: {plugins: ["postcss-preset-env"],},},},preProcessor && {loader: preProcessor,options:preProcessor === "sass-loader"? {// 自定义主题:自动引入我们定义的scss文件additionalData: `@use "@/styles/element/index.scss" as *;`,}: {},},].filter(Boolean);
};module.exports = {entry: "./src/main.js",output: {path: isProduction ? path.resolve(__dirname, "../dist") : undefined,filename: isProduction? "static/js/[name].[contenthash:10].js": "static/js/[name].js",chunkFilename: isProduction? "static/js/[name].[contenthash:10].chunk.js": "static/js/[name].chunk.js",assetModuleFilename: "static/js/[hash:10][ext][query]",clean: true,},module: {rules: [{test: /\.css$/,use: getStyleLoaders(),},{test: /\.less$/,use: getStyleLoaders("less-loader"),},{test: /\.s[ac]ss$/,use: getStyleLoaders("sass-loader"),},{test: /\.styl$/,use: getStyleLoaders("stylus-loader"),},{test: /\.(png|jpe?g|gif|svg)$/,type: "asset",parser: {dataUrlCondition: {maxSize: 10 * 1024,},},},{test: /\.(ttf|woff2?)$/,type: "asset/resource",},{test: /\.(jsx|js)$/,include: path.resolve(__dirname, "../src"),loader: "babel-loader",options: {cacheDirectory: true,cacheCompression: false,plugins: [// "@babel/plugin-transform-runtime" // presets中包含了],},},// vue-loader不支持oneOf{test: /\.vue$/,loader: "vue-loader", // 内部会给vue文件注入HMR功能代码options: {// 开启缓存cacheDirectory: path.resolve(__dirname,"node_modules/.cache/vue-loader"),},},],},plugins: [new ESLintWebpackPlugin({context: path.resolve(__dirname, "../src"),exclude: "node_modules",cache: true,cacheLocation: path.resolve(__dirname,"../node_modules/.cache/.eslintcache"),}),new HtmlWebpackPlugin({template: path.resolve(__dirname, "../public/index.html"),}),new CopyPlugin({patterns: [{from: path.resolve(__dirname, "../public"),to: path.resolve(__dirname, "../dist"),toType: "dir",noErrorOnMissing: true,globOptions: {ignore: ["**/index.html"],},info: {minimized: true,},},],}),isProduction &&new MiniCssExtractPlugin({filename: "static/css/[name].[contenthash:10].css",chunkFilename: "static/css/[name].[contenthash:10].chunk.css",}),new VueLoaderPlugin(),new DefinePlugin({__VUE_OPTIONS_API__: "true",__VUE_PROD_DEVTOOLS__: "false",}),// 按需加载element-plus组件样式AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver({importStyle: "sass", // 自定义主题}),],}),].filter(Boolean),optimization: {minimize: isProduction,// 压缩的操作minimizer: [new CssMinimizerPlugin(),new TerserWebpackPlugin(),new ImageMinimizerPlugin({minimizer: {implementation: ImageMinimizerPlugin.imageminGenerate,options: {plugins: [["gifsicle", { interlaced: true }],["jpegtran", { progressive: true }],["optipng", { optimizationLevel: 5 }],["svgo",{plugins: ["preset-default","prefixIds",{name: "sortAttrs",params: {xmlnsOrder: "alphabetical",},},],},],],},},}),],splitChunks: {chunks: "all",cacheGroups: {// layouts通常是admin项目的主体布局组件,所有路由组件都要使用的// 可以单独打包,从而复用// 如果项目中没有,请删除layouts: {name: "layouts",test: path.resolve(__dirname, "../src/layouts"),priority: 40,},// 如果项目中使用element-plus,此时将所有node_modules打包在一起,那么打包输出文件会比较大。// 所以我们将node_modules中比较大的模块单独打包,从而并行加载速度更好// 如果项目中没有,请删除elementUI: {name: "chunk-elementPlus",test: /[\\/]node_modules[\\/]_?element-plus(.*)/,priority: 30,},// 将vue相关的库单独打包,减少node_modules的chunk体积。vue: {name: "vue",test: /[\\/]node_modules[\\/]vue(.*)[\\/]/,chunks: "initial",priority: 20,},libs: {name: "chunk-libs",test: /[\\/]node_modules[\\/]/,priority: 10, // 权重最低,优先考虑前面内容chunks: "initial",},},},runtimeChunk: {name: (entrypoint) => `runtime~${entrypoint.name}`,},},resolve: {extensions: [".vue", ".js", ".json"],alias: {// 路径别名"@": path.resolve(__dirname, "../src"),},},devServer: {open: true,host: "localhost",port: 3000,hot: true,compress: true,historyApiFallback: true, // 解决vue-router刷新404问题},mode: isProduction ? "production" : "development",devtool: isProduction ? "source-map" : "cheap-module-source-map",performance: false,
};

-----------------------------原理篇-------------------------------

一、loader原理

二、plugin原理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_71662.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

100份简历才找一个合适的,2023,软件测试岗位饱和了吗?

各大互联网公司的接连裁员,政策限制的行业接连消失,让今年的求职雪上加霜,想躺平却没有资本,还有人说软件测试岗位饱和了,对此很多求职者深信不疑,因为投出去的简历回复的越来越少了。 另一面企业招人真的…

闪光桐人の实习日记(2023年2月20-27日)

前往闪闪の小窝以获得更好的阅读和评论体验 文章目录2023年2月20日(Vue入门)概念Vue基础Vue中的MVVMVue的体验Vue的生命周期Vue指令Vue组件VueRouter前后端路由的区别工作原理两种模式比较route跟router的区别路由属性导航守卫Vuex概述5种基本对象基本使…

Qt线程QThread详解

目录前言1.QThread介绍2.QThread示例一3.QThread示例二4.线程同步前言 在程序中使用线程可以提高程序的性能、并发性、响应性和稳定性,使得程序设计更加灵活和简单。但是,线程编程也有一些挑战,如线程安全性和死锁等问题需要格外注意。我们使…

【1】linux命令每日分享——mkdir

大家好,这里是sdust-vrlab,Linux是一种免费使用和自由传播的类UNIX操作系统,Linux的基本思想有两点:一切都是文件;每个文件都有确定的用途;linux涉及到IT行业的方方面面,在我们日常的学习中&…

【机器学习】决策树-ID3算法

1.ID3算法 ID3算法利用信息增益进行特征的选择进行树的构建。信息熵的取值范围为0~1,值越大,越不纯,相反值越小,代表集合纯度越高。信息增益反映的是给定条件后不确定性减少的程度。每一次对决策树进行分叉选取属性的时候&#x…

网络计划--时间参数的计算和优化

根据网络图的基本概念和原则绘制出网络图之后,我们可以计算网络图中有关的时间参数,主要目的是找出关键路线,为网络计划的优化、调整和执行提供明确的时间概念。如下图中从始点①到终点⑧共有4条路线,可以分别计算出每条路线所需的…

使用maven搭建父子工程项目

创建父子工程,可以通过父工程来引入jar,定义统一的版本号等。更方便对整个项目的jar包实现统一化管理,让项目的层次更加清晰。一、创建父工程第一步:file–>new–>project–>maven默认使用jdk1.8,不引入任何j…

音视频基础之视频主要概念

视频主要概念 **视频码率:**kb/s,是指视频文件在单位时间内使用的数据流量,也叫码流率。码率越大,说明单位时间内取样率越大,数据流精度就越高。 **视频帧率:**fps,通常说一个视频的25帧&…

ur3+robotiq ft sensor+robotiq 2f 140配置rviz仿真环境

ur3robotiq ft sensorrobotiq 2f 140配置rviz仿真环境 搭建环境: ubuntu: 20.04 ros: Nonetic sensor: robotiq_ft300 gripper: robotiq_2f_140_gripper UR: UR3 在安装sensor和gripper之前,先简单配置一下UR机械臂的仿真环境,可参考这篇博…

jenkins下载与简单使用

1.jenkins下载 因为我仍然使用的是jdk1.8进行开发,所以我下载的是jenkins2.332.1版本(jenkins2.346.1版本在2022年末不再支持java8,如果项目使用的是jdk11可以继续使用该jenkins版本),更多版本下载请点击jenkins下载 …

ADRC自抗扰控制总结

目录 前言 1.ADRC形式 1.1形一 1.2形二 2.被控对象 3.仿真分析 3.1仿真模型 3.2仿真结果 4.学习问题 前言 前面的3篇文章依次介绍了微分跟踪器TD、状态观测器ESO和非线性状态误差反馈NLSEF三部分内容,至此ADRC的结构已经介绍完毕,现在对分块学习…

pytorch零基础实现语义分割项目(四)——模型训练与预测

模型训练与预测项目列表前言损失函数one_hotDice LossFocal Loss模型参数与训练预测项目列表 语义分割项目(一)——数据概况及预处理 语义分割项目(二)——标签转换与数据加载 语义分割项目(三)——语义…

JVM系统优化实践(1):JVM概览

您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~这是多年之前做过的学习笔记,今天再翻出来,觉得仍然是记忆犹新。「独乐乐不如众乐乐」,就拿出来分享给「众乐乐」吧。目前大多…

用户画像——如何构建用户画像系统

为什么需要用户画像 如果你是用户,当你使用抖音、今日头条的时候,如果平台推荐给你的内容都是你感兴趣的,能够为你节省大量搜索内容的时间。 如果你是商家,当你投放广告的时候,如果平台推送的用户都是你的潜在买家,能够使你花更少的钱,带来更大的收益。 这两者背后都…

KeePass敏感信息明文传输漏洞复现 (CVE-2023-24055)

一、漏洞描述 漏洞简述 KeePass 是一款免费的开源密码管理器,可帮助您以安全的方式管理您的密码。您可以将所有密码存储在一个数据库中,该数据库由一把万能钥匙锁定。因此,您只需记住一个主密钥即可解锁整个数据库。数据库文件使用目前已知…

面试个3年自动化测试,测试水平一言难尽。。。。

公司前段缺人,也面了不少测试,结果竟然没有一个合适的。 一开始瞄准的就是中级的水准,也没指望来大牛,提供的薪资在10-20k,面试的人很多,但平均水平很让人失望。 看简历很多都是3年工作经验,但…

中间件安全—Apache常见漏洞

中间件安全—Apache常见漏洞1.Apache常见漏洞1.1.Apache介绍1.2.Apache HTTPD 换行解析漏洞(CVE-2017-15715)1.2.1.漏洞介绍1.2.2.漏洞环境1.2.2.1.运行漏洞环境1.2.2.2.访问漏洞环境1.2.3.漏洞复现1.2.3.1.拦截1.2.3.2.添加换行1.2.3.3.访问文件1.3.Apa…

steam搬砖信息差项目,新手1周拿到结果!

项目具体是什么呢? 项目简单概括通过选品软件自动分析出此商品国内外商品价格,计算出利润率,选择出有利润销量好的商品,在以最低价格上架到国内buff的平台里,既能快速的卖出,还能获利。 主要利润在于商品…

在Vue.js中应该避免的三件事

1. 尽量避免使用行内事件代码(inline script) 这是一个行内事件代码 的例子 <div click"alert(hello world); doSomething();" />这种代码虽然第一次写起来很简单&#xff0c;但是很容易出bug。这样做有两个缺点。 VS Code 无法检查行内事件代码的错误 VS …

移动WEB开发五、响应式布局

零、文章目录 文章地址 个人博客-CSDN地址&#xff1a;https://blog.csdn.net/liyou123456789个人博客-GiteePages&#xff1a;https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee&#xff1a;https://gitee.com/bluecusliyou/TechLearnGithub&#xff1a;https:…