webpack
https://xxpromise.gitee.io/webpack5-docs/
基本开发配置
含义:
webpack是一个静态资源打包工具
-- 以一个或多个文件为打包入口,将我们整个项目的文件编译组合成一个或者多个文件发布出去
我们将webpack打包好的文件叫bundel
功能介绍
webpack本身功能有限,开发模式只能编译js的ES Module语法, 生产模式也只是能压缩下
基本配置
五大核心模块
- entry --- 指示入口文件
- 单个入口文件 entry: './src/main.js'
- 多入口文件 entry:
- output-- 指示出口命名以及地址
- 单个出口 output:
- 多个出口 output:
- loader --- 加载机 使webpack具有解析其他资源的能力
- module:
- plugins --- 插件 拓展webpack功能
- plugins: [] -------- 数组
- mode ---模式 development production
开始使用
- 安装
- npm i webpack webpack-cli -g 全局装全局用
- 局部装 npm i webpack webpack-cli -D 局部装 局部使用指令
- npx webpack 入口文件地址 --mode=development (npx 作用就是找node_modules下的bin目录下的指令去操作)
- npm init -y 生成package.json文件
- 根目录下创建webpack.config.js 去进行基础的配置
- 配置完毕 执行指令 全局执行 webpack -w 局部 npx webpack
- 处理css资源 less资源 scss资源 npm i css-loader less-loader sass-loader sass node-sass(被sass-loader依赖) -D
- 处理图片资源 webpack5版本已经内置 file-loader url-loader 直接配置无需安装
- 处理字体图标 视频音频文件
- 处理js资源文件
- Eslint处理代码规范
- ----下
- babel处理兼容性
- Eslint处理代码规范
const path = require('path')
const EslintPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
'main':'./src/main.js'
},
output: {
path: path.join(process.cwd(), './myDist'),
// 所有文件打包之后的目录
filename: 'static/js/main.js' , // 只能分离出来入口文件打包的js
// 我们想让入口文件打包的js资源单独出来 ‘js/main.js’
// filename: 'main.js'
// 自动清空上次打包内容
clean: true
},
module: {
// loader配置
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
},
{
test: /\.less$/,
use: ['style-loader','css-loader','less-loader']
},
{
test: /\.s[ca]ss$/,
use: ['style-loader','css-loader','sass-loader']
},
// 图片加载机
{
test: /\.(png|jpg|jpeg|gif|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 // 4kb 小于4kb的base64编码 注意图片打包后不会把之前打包的删除 所以要手动删除
}
},
generator: {
// 定义图片资源打包输出目录 相对于output的path hash:6 取前六位
filename: 'static/img/[hash: 6][ext][query]'
}
},
// 字体图标 音视频
{
test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
generator: {
// 定义图片资源打包输出目录 相对于output的path hash:6 取前六位
filename: 'static/media/[hash: 6][ext][query]'
}
},
//// babel 使用
{
test: /\.m?js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
// exclude: [], // 排除多个
presets: ['@babel/preset-env']
}
}
]
},
plugins: [
// 插件配置
// eslint插件
new EslintPlugin({
context: path.join(process.cwd(), './src') // 表示从哪里开始检查 ,一般是入口文件
}),
new HtmlWebpackPlugin({
// 以那个文件为模板进行打包
// 新的html文件结构与原来一致 还会自动引入打包输出的文件
template: path.join(process.cwd(), './public/index.html')
})
],
mode: 'development'
}Eslint
配置文件
- .eslintrc.* 创建文件进行配置 1. .eslintrc 2. .eslintrc.js 3. .eslintrc.json
区别在于配置格式不同
- package.json中 eslintConfig 不需要创建文件进行配置
具体配置
.eslintrc.js 方式
module.exports = {
extends: ["react-app"], // 直接继承react官网规则
parserOptions: {
ecmaVersion: 6, //es语法版本
sourceType: "module", // es模块化
ecmaFeatures: { // es其他特性
jsx: true // react项目要开启jsx语法
}
},
rules: {
"no-var": 2 // 有var直接error
},
// https://eslint.bootcss.com/docs/user-guide/configuring
// 规则文档 *
// https://eslint.bootcss.com/docs/rules/
}- parseOptions 解析选项
- rules 配置规则 需要的时候直接去配置规则中寻找
- "off"或者 0 关闭规则
- "warn" 或者 1 开启规则 --- 使用警告级别错误
- "error"或者 2 开启规则 ---- 出现错误 中断运行
- https://eslint.bootcss.com/docs/rules/
- extends 继承 --- 开发中写规则太费劲 可以直接继承现有的规则
- Eslint官方规则 eslint:recommended
- Vue cli 官方规则 plugin: vue/essential
- React cli 官方规则 react-app
- **** 注意我们写的规则会覆盖继承的规则
webpack使用eslint
- 根目录下创建 .eslintrc.js 配置eslint (可以安装个eslint插件看)
- npm i eslint-webpack-plugin eslint -S
- 在webpack.config.js中
- 引入插件 const EslintPlugin = require('eslint-webpack-plugin')
- 在webpack配置项plugins: []中 实例化插件 plugins: [new EslintPlugin(option),
- 如果安装了eslint插件,打包之后,会发现打包后的文件会报错,而我们不希望检查打包后的文件 所以
- 新建 .eslintignore
- 直接写 dist 表示这个目录不检查
Babel
配置文件 搞一个就行
- babel.config.*
- babel.config.js
- babel.config.json
- .babelrc.*
- .babelrc
- .babelrc.js
- .babelrc.json
- package.json中的babel
具体配置 babel.config.js例子
module.export = {
// 预设
presets: ['@babel/preset-env', ]
}- presets 预设
- @babel/preset-env 允许使用最新js
- @babel/preset-react 编译jsx
- @babel/preset-typescript 编译ts
在webpack使用
https://webpack.docschina.org/loaders/babel-loader/
- -D babel-loader @babel/core @babel/preset-env
- loader方式添加
//// babel 使用
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
// options可以写babel.config.js中
options: {
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
// exclude: [], // 排除多个
presets: ['@babel/preset-env']
}
}
}- options配置可以写在外边
- 新建babel.config.js
- 在这个文件中配置就可以 方便之后修改
处理html资源
我们现在js css资源是手动引入的 将来可能会发生变化 所以我们需要自动
- npm i html-webpack-plugin -D
- webpack.config.js中
- const HtmlWebpackPlugin = require('html-webpack-plugin')
- 在plugin中new HtmlWebpackPlugin ({})下就行 具体看配置对象 有 template等属性
开发服务器&自动化
目的: 每次开发完都要手动输入指令才能编译,我们需要自动化
使用: webapck -w 应该也行
- npm i webpack-dev-server -D
- webpack.config.js中进行配置
- 启动指令webpack serve 局部 npx webpack serve
devServer: {
host: 'localhost',
port: '3000',
open: true
}基础配置
const path = require('path')
const EslintPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
'main':'./src/main.js'
},
output: {
path: path.join(process.cwd(), './myDist'),
// 所有文件打包之后的目录
filename: 'static/js/main.js' , // 只能分离出来入口文件打包的js
// 我们想让入口文件打包的js资源单独出来 ‘js/main.js’
// filename: 'main.js'
// 自动清空上次打包内容
clean: true
},
module: {
// loader配置
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
},
{
test: /\.less$/,
use: ['style-loader','css-loader','less-loader']
},
{
test: /\.s[ca]ss$/,
use: ['style-loader','css-loader','sass-loader']
},
// 图片加载机
{
test: /\.(png|jpg|jpeg|gif|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 // 4kb 小于4kb的base64编码 注意图片打包后不会把之前打包的删除 所以要手动删除
}
},
generator: {
// 定义图片资源打包输出目录 相对于output的path hash:6 取前六位
filename: 'static/img/[hash: 6][ext][query]'
}
},
// 字体图标 音视频
{
test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
generator: {
// 定义图片资源打包输出目录 相对于output的path hash:6 取前六位
filename: 'static/media/[hash: 6][ext][query]'
}
},
//// babel 使用
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
options: {
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
// exclude: [], // 排除多个
presets: ['@babel/preset-env']
}
}
]
},
plugins: [
// 插件配置
// eslint插件
new EslintPlugin({
context: path.join(process.cwd(), './src') // 表示从哪里开始检查 ,一般是入口文件
}),
new HtmlWebpackPlugin({
// 以那个文件为模板进行打包
// 新的html文件结构与原来一致 还会自动引入打包输出的文件
template: path.join(process.cwd(), './public/index.html')
})
],
// 配置开发服务器
devServer: {
host: 'localhost',
port: '3000',
open: true
},
mode: 'development'
}基本生产配置
把开发生产模式分开
- 根目录创建config目录
- config目录包含webpack.dev.js webpack.prod.js
- 把之前的开发配置复制到dev,并进行修改
// webpack.dev.js
// 我们运行指令还是在根目录运行
const path = require('path')
const EslintPlugin = require('eslint-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
'main':'./src/main.js'
},
output: {
// path: path.join(process.cwd(), './myDist'),
// 因为开发不需要输出
path: undefined,
// 所有文件打包之后的目录
filename: 'static/js/main.js' , // 只能分离出来入口文件打包的js
// 我们想让入口文件打包的js资源单独出来 ‘js/main.js’
// filename: 'main.js'
// 自动清空上次打包内容
//clean: true
},
module: {
// loader配置
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader']
},
{
test: /\.less$/,
use: ['style-loader','css-loader','less-loader']
},
{
test: /\.s[ca]ss$/,
use: ['style-loader','css-loader','sass-loader']
},
// 图片加载机
{
test: /\.(png|jpg|jpeg|gif|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 // 4kb 小于4kb的base64编码 注意图片打包后不会把之前打包的删除 所以要手动删除
}
},
generator: {
// 定义图片资源打包输出目录 相对于output的path hash:6 取前六位
filename: 'static/img/[hash: 6][ext][query]'
}
},
// 字体图标 音视频
{
test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
generator: {
// 定义图片资源打包输出目录 相对于output的path hash:6 取前六位
filename: 'static/media/[hash: 6][ext][query]'
}
},
//// babel 使用
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
//exclude: [], // 排除多个
presets: ['@babel/preset-env']
}
]
},
plugins: [
// 插件配置
// eslint插件
new EslintPlugin({
context: path.join(process.cwd(), './src') // 表示从哪里开始检查 ,一般是入口文件
}),
new HtmlWebpackPlugin({
// 以那个文件为模板进行打包
// 新的html文件结构与原来一致 还会自动引入打包输出的文件
template: path.join(process.cwd(), './public/index.html')
})
],
// 配置开发服务器
devServer: {
host: 'localhost',
port: '3000',
open: true
},
mode: 'development'
}- 修改之后,想运行全局开发指令 webpack serve --config ./config/webpack.dev.js,局部 npx webpack serve --config ./config/webpack.dev.js
- 配置生产模式下的配置
// webpack.prod.js
// 我们运行指令还是在根目录运行
const path = require('path')
const EslintPlugin = require('eslint-webpack-plugin')
// 处理html 自动引入
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 提取css为单独文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 压缩css
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
entry: {
'main':'./src/main.js'
},
output: {
path: path.join(process.cwd(), './myDist'),
// 所有文件打包之后的目录
filename: 'static/js/main.js' , // 只能分离出来入口文件打包的js
// 我们想让入口文件打包的js资源单独出来 ‘js/main.js’
// filename: 'main.js'
// 自动清空上次打包内容
clean: true
},
module: {
// loader配置
rules: [
{ // MiniCssExtractPlugin.loader提取css为单独的文件
test: /\.css$/,
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[ca]ss$/,
use: [MiniCssExtractPlugin.loader,'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", //可以解决大多说样式问题
]
}
}
},
'sass-loader']
},
// 图片加载机
{
test: /\.(png|jpg|jpeg|gif|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 // 4kb 小于4kb的base64编码 注意图片打包后不会把之前打包的删除 所以要手动删除
}
},
generator: {
// 定义图片资源打包输出目录 相对于output的path hash:6 取前六位
filename: 'static/img/[hash: 6][ext][query]'
}
},
// 字体图标 音视频
{
test: /\.(ttf|woff2?|mp3|mp4|avi)$/,
type: "asset/resource",
generator: {
// 定义图片资源打包输出目录 相对于output的path hash:6 取前六位
filename: 'static/media/[hash: 6][ext][query]'
}
},
//// babel 使用
{
test: /\.m?js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
//exclude: [], // 排除多个
presets: ['@babel/preset-env']
}
}
]
},
plugins: [
// 插件配置
// eslint插件
new EslintPlugin({
context: path.join(process.cwd(), './src') // 表示从哪里开始检查 ,一般是入口文件
}),
new HtmlWebpackPlugin({
// 以那个文件为模板进行打包
// 新的html文件结构与原来一致 还会自动引入打包输出的文件
template: path.join(process.cwd(), './public/index.html')
}),
// 拆分css
new MiniCssExtractPlugin({
filename: 'static/css/main.css' // 打包到的位置
}),
// 压缩css
new new CssMinimizerPlugin()
],
// 配置开发服务器
devServer: {
host: 'localhost',
port: '3000',
open: true
},
mode: 'production'
}- 生产指令与开发指令类似
- 因为指令长,我们可以在package.json进行配置
”script“: {
"start": "npm run dev",
"dev": "webpack serve --config ./config/webpack.dev.js",
"prod": "webpack serve --config ./config/webpack.prod.js"
}css文件处理
提取css文件为单独文件
因为目前css被打包到js中,js加载时,会创建一个style标签生成样式,对于网站来说,会有闪屏现象,我们倾向于link引入单独文件
- npm i mini-css-extract-plugin -D
- 在生产配置中配置
- 引入const MiniCssExtractPlugin = require('mini-css-extract-plugin')
- plugins: [new MiniCssExtractPlugin()]
- 每个样式loader中,把之前的style-loader修改成 use: [MiniCssExtractPlugin.loader, "css-loader"]
样式兼容性处理
- npm i postcss-loader postcss postcss-preset-env -D
- 在生产配置中配置
- 在每个css-loader后边进行配置 --- 位置不能变 必须在less或者scssloader之前
- 加上
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", //可以解决大多说样式问题
]
}
}
}- package.json中增加配置,告诉这个loader要做兼容性做到什么程度
- "browserslist": [ "last 2 version", "> 1%", "not dead" ] 实际开发这么写 /////// ["ie >= 8"] 不常用 除非有特殊实际需求
所有浏览器 覆盖99% 不是一些已经挂了的版本 --- 交集
css压缩
https://webpack.docschina.org/plugins/css-minimizer-webpack-plugin/
- npm i css-minimizer-webpack-plugin -D
- 在生产配置中配置
- 引入 const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
- plugins:[new CssMinimizerPlugin ()]
html与js文件压缩
mode改成生产模式就默认压缩了
高级优化
介绍
- 提升开发体验(SourceMap HMR )
- 提升打包构建速度(oneOf Include/Exclude Eslint Babel的缓存 cache 多进程优化 )
- 减少代码体积(Tree Shaking Babel辅助文件引用 图片压缩image Minimizer)
- 优化代码运行性能
SourceMap -- 优化报错
为什么?
开发时,运行的代码是编译后的,如果出现错误,不知道哪里出错,所以出错时需要告诉我们在源代码哪里出错,哪一行那一列
是什么?
SourceMap -- 源代码映射 是一个用来生成源代码与构建后代码一一映射的文件的方案
它会生成一个***.map文件, 里面包含源代码和构建后代码每一行,每一列的映射关系,当代码构建出错了,会通过这个map文件,从构建后代码出错的位置找到映射后源代码出错的位置,从而让浏览器提示源代码出错位置,帮助我们找到错误根源
怎么用?https://webpack.docschina.org/configuration/devtool/
开发模式配置文件中 devtool: "cheap-module-source-map" 只包含行
生产模式配置文件中 devtool: "source-map" 包含行和列 因此打包编译慢一点
提升代码构建速度
HMR -- HotModuleReplacement
为什么?
开发中,修改一点就需要重新打包,如果项目很大,那么修改一次就要打包好几分钟,所以我们需要提升打包构建速度
是什么?
HotModuleReplacement -- 热模块替换 就是程序运行中,替换 添加 删除模块, 无需重新加载整个页面
怎么用?
只需要在开发模式使用热模块替换
devServer: {
hot: true // 就行 默认就是开的
}
注意 热模块替换开启后,只是说除了js之外的能实现,如果是js变化,还是会重新加载整个页面
开启js热模块需要我们在入口文件最下边写代码
if(module.hot) {
module.hot.accept('./js/count.js') // 让这个js模块开启
module.hot.accept('./js/sum.js', () => {
热加载时执行这个回调
}) // 让这个js模块开启
}oneOf
--- 就类似遍历的时候你已经得到了结果,不需要继续遍历 这里 oneOf就是这样的效果,如果这个文件已经被一个loader处理了,就不需要继续看下边的了
使用:开发 生产模式都可以
把所有loader拿出来,改造rules
rules: [
{
// 每个文件只能被一个loader处理 提升性能
oneOf: [
loader.... 那些loader
]
}
]Include/Exclude --- 只针对js文件
我们需要使用一些三方库或者插件,所有的文件都在node_modeuls 这些文件不需要编译打包,所以有这俩去处理, js文件只有babel和eslint在干活 所以只处理babel eslint
include: 包含 只处理xxx文件
exclude: 排除 除了xxx之外的都处理
实例: ------
在babel-loader时, 还有语法检查new EslintPlugin({})时也要写 --- 看最后配置(开发 生产都需要)
Eslint Babel的缓存 cache
因为每次打包js文件时,都要经过Eslint检查和babel编译,所以速度比较慢
我们可以缓存之前的Eslint检查和babel编译结果,这样二次打包速度就快了
babel 处理
在babel-loader配置项options: {}中增加属性 (开发模式,生产模式都可以配置),打包后缓存生成在node_modules下的.cache中
options: {
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
//exclude: [], // 排除多个
presets: ['@babel/preset-env'],
cacheDirectory: true, // 开启babel缓存
cacheCompression: false // 关闭缓存压缩, 因为我们为了速度,压缩缓存也需要时间
}eslint处理
new EslintPlugin({
context: path.join(process.cwd(), './src'), // 表示从哪里开始检查 ,一般是入口文件
exclude: 'node_modules',
cache: true, // 开启缓存
cacheLocation: path.join(process.cwd(), './node_modules/.cache/eslint_cache')// 定义缓存存储路径
}),多进程打包
当项目越来越庞大,打包速度越来越慢,虽然有了一些优化,但是随着项目的增大,还是越来越慢,所以我们还需要提升js打包速度,因为一个项目中大部分都是js,js的话主要就是eslint babel terser三个工具,所以我们要提升他们的运行速度,所以我们可以多进程打包,比单进程块
多进程打包:就是指的电脑多个进程同时干一件事,
使用:
- 引入os模块,获取cpu核心数
- const os = require('os')
- const threads = os.cpus().length // threads cpu核心数
- 下载包 npm i thread-loader -D
- 改造babel-loader, 我们一定要把thread-loader放在要处理的babel-loader之前
- 改造eslint 直接写threads就可以
- 引入插件terser-webpack-plugin (内置的无需下载) const TerserWebpackPlugin = require('terser-webpack-plugin')
// 处理bable-loader 多进程编译
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'thread-loader',
options: {
works: threads // 进程数
}
},
{
loader: 'babel-loader',
options: {
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
//exclude: [], // 排除多个
presets: ['@babel/preset-env']
}
}
]
}
// 处理eslint 多进程语法校验
new EslintPlugin({
...
threads // 开启多进程
})
// 处理压缩 --- 多进程压缩
// 在插件部分
new TerserWebpackPlugin({
parallel: threads
})减少代码体积
Tree Shaking
-- 移除js中没有使用上的代码
webpack已经默认开启,不需要任何操作
Babel
文件因为babel为编译的都插入了辅助代码,使得代码体积过大,我们能将这些辅助代码作为一个独立模块,避免重复引入
@babel/plugin-transform-runtime: 禁用babel自动对每个文件的runtime注入,改成引入, 并且使所有辅助代码从这里引用
使用:
- npm i @babel/plugin-transform-runtime -D
- 配置 开发 生产都可以
{
loader: 'babel-loader',
options: {
// 表示排除编译的文件 如果只有一个 直接是字符串
// exclude: /node_modules/
//exclude: [], // 排除多个
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime'] // 减少代码体积
}
}图片压缩 Image Minimizer
如果项目中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢,我们可以对图片进行压缩,减少图片体积
注意:如果项目中图片都是在线连接,不需要进行处理,本地项目静态图片才需要进行压缩
image-minimizer-webpack-plugin: 压缩图片的插件
使用:---- 生产模式下配置就行
- npm i image-minimizer-webpack-plugin imagemin -D
- 下载剩余包,有两种模式
- 无损压缩npm i imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
- 有损压缩npm i imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D
- 以无损为例 配置 有损配置不同
// 压缩图片 放在压缩的位置
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",
},
},
],
},
],
],
},
},
}),- 打包发现抱错,可能会有两个程序没有下载下载来,所以我们需要手动把程序放在指定位置
- jpegtran.exe 把它放在node_modules/jpegtran-bin/ventor目录下 http://jpegclub.org/jpegtran/
- optipng.exe 把他放在node_modules/optipng-bin/vendor目录下 http://optipng.sourceforge.net/
- 之后进行打包就行了
优化代码运行性能
Code Split 生产模式
打包js时会将所有js文件打包到一个文件中,体积太大,如果我们只需要渲染首页,就应该只加载首页js文件,其他文件不应该加载,所以我们把打包生成的代码进行分割,生成多个js文件,渲染那个页面就只加载某个js文件,这样加载资源就少,速度就快
代码分割:
- 分割文件: 将打包后的文件进分割,生成多个js文件
- 按需加载: 需要那个文件就加载那个
使用方式1 --- 多入口
使用方式2 --- 单入口
npm install eslint-plugin-import -D ----- 让eslint支持import
- 直接把配置放在optimization中, 如果我们将来用到了node_modules中的会把它单独打包成一个文件
如果用到了动态导入语法 也会单独打包成一个文件
// 代码分割配置
splitChunks: {
chunks: "all", // 对所有模块都进行分割
},
// 如果我们将来用到了node_modules中的会把它单独打包成一个文件
// 如果用到了动态导入语法 也会单独打包成一个文件 import(/* webpackChunkName: "count" */'./count.js').then().catch()
// 动态导入语法默认eslint不能识别 所以需要配置 npm install eslint-plugin-import -D ----- 让eslint支持import- .eslintrc.js中新增 plugins: ["import"] 支持动态导入
- 动态导入 - 单独打包出的文件配置名字
- webpack魔法命名 直接在动态导入的地方通过注释的方式命名 import(/* webpackChunkName: "count" */'./count.js').then().catch()
- 在output配置项中添加配置 chunkFilename: 'static/js/[name].chunk.js'
- 统一命名配置
- 把output中主文件名用[name].js兼容
- 把output中其他文件名[name].chunk.js 进行区分
- 把用asset处理的资源统一命名
- 把图片和字体图标等用asset处理的资源的filename注释
- output中新增assetModuleFilename: "static/media/[hash:10][ext][query]"
- 把拆分css插件配置filename导出文件名进行适配
newMiniCssExtractPlugin({
// 适配将来的多入口
filename: 'static/css/[name].css',
// 动态导入了css
chunkFilename: 'static/css/[name].chunk.css'
}),preload/prefetch
当我们使用动态导入时,比如我们点击之后才加载这个资源,如果此时加载的资源很大,那么会有明显的卡顿,所以我们想在浏览器空闲时间加载后续需要使用的资源,我们就需要preload prefetch技术
preload: 告诉浏览器立即加载资源
prefetch: 告诉浏览器在空闲时才开始加载资源
共同点: 都只是加载资源,并不执行,都有缓存
不同:preload: 加载优先级高,并且只能加载当前页面用到的资源
prefetch:加载优先级低,但是可以预加载下个页面资源 兼容性较差
https://caniuse.com/ 查询兼容性问题网站
使用:
npm i @vue/preload-webpack-plugin -D
资源预加载 防止按需引入卡顿const PreloadWebpackPlugin=require('@vue/preload-webpack-plugin')
js 按需引入资源预加载 在plugins:[]配置
newPreloadWebpackPlugin({ rel: 'preload', as: 'script' // rel: 'prefetch'
{)
缓存优化 runtime
代码分割可能会导致缓存失效,所以要runtime,因为我们的main.js文件依赖count.js文件,所以当我们按需引入的count文件变化时,main也会变化,不会使用缓存,原因是count的hash值的变化,所以我们想到一个办法,把这个hash值存在另一个文件,main中每次去另一个文件runtime拿hash,这样main就可以使用缓存
-- 会把文件之间依赖的hash值提取到一个文件保管 --
使用:在optimization中配置
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}.js`
}Core-js
- 我们之前使用babel的时候,使用智能预设对js进行兼容性处理,但是它只能将es6的一些语法进行编译转换,但是像async promise 数组的一些方法includes等 没办法处理
- core-js 是专门用来做ES6以及以上的API的polyfill(补丁)的
- 使用:
- npm i core-js
- 引入方式
- 入口文件 --- 全部引入 在入口文件中 import 'core-js' 就完事
- 入口文件 --- 按需引入 如 import 'core-js/es/promise' 只引入一个语法
- wenpack配置babel ----我们也可以通过配置解决 --- 常用
// babel配置中找
presets: [
["@babel/preset-env", {useBuiltIns: 'usage',corejs: 3// corejs版本}]
]PWA技术
开发web App项目,项目一旦处于网络离线情况,就没办法访问了,我们希望可以离线访问
PWA 一种渐进式网络应用程序:一种可以提供类似一native app 原生应用程序体验的web app 技术
通过 Service Workers实现的
使用:
- npm i workbox-webpack-plugin -D
- webpack配置 const WorkboxPlugin = require('workbox-webpack-plugin')
- plugins:[] 配置 new WorkboxPlugin.GenerateSW ({clientsCliaim: true, skipWaiting: true})
- 入口文件中配置
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);
});
});
}注意:我们想使用这个技术 那么必须把打包出的项目部署在服务器环境下,可利用serve
高级优化总结
- Source Map
- HotModuleReplacement
- OneOf
- Include/Exclude
- Cache
- Thead
- Tree Shaking
- @babel/plugin-transform-runtime
- Image Minimizer
- Code Split
- Preload / Prefetch
- Network Cache
- Core-js
- PWA
react-cli
开发模式
npm i eslint eslint-webpack-plugin html-webpack-plugin style-loader css-loader less-loader sass-loader sass postcss-preset-env postcss-loader stylus-loader babel-loader @babel/core babel-preset-react-app eslint-config-react-app webpack-dev-server webpack webpack-cli -D
npm i react react-dom 不要添加到开发依赖
. 报错
运行报错
解决:
- npm i cross-env -D
- 在package.json启动webpack前边加上 cross-env NODE_ENV=devlopment
"scripts": {
"start": "npm run dev",
"dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
},js修改,HMR热更新不起作用
解决:https://www.npmjs.com/package/@pmmmwh/react-refresh-webpack-plugin
- npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh
- webpack.dev.js中
- const ReactRefreshWebpackPlugin=require('@pmmmwh/react-refresh-webpack-plugin');
- babel-loader的options中添加plugins: ['react-refresh/babel']
- 在webpack配置的plugins中 new ReactRefreshWebpackPlugin()
当我们使用BrowserRouter路由的时候,在路由页面刷新,会找不到404, HashRouter没有这个问题
解决:https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback
devServer: { // 直接配置
historyApiFallback: true,
},问题4. 我们发现现在路由和其他js文件打包到了一个js中,我们开发想让路由单独打包,--- 路由懒加载
解决:导入路由用懒加载 可以webpack魔法命名 只要设置了splitChunk 就行
const Home = lazy(() => import(/* webpackChunkName: "home_chunk" */ './pages/Home/home'));
const About = lazy(() => import(/* webpackChunkName: "about_chunk" */ './pages/About/about'))问题5. I
解决: import 上移就行
生产模式
npm i mini-css-extract-plugin css-minimizer-webpack-plugin image-minimizer-webpack-plugin imagemin imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
问题1. 我们配置完发现,public下面的文件除了index.html都不能被打包,我们会在public中放icon, 以及其他的一些静态资源,他们也需要打包,但是需要排除index.html
解决:
const CopyPlugin = require("copy-webpack-plugin");
在plugins:[]中配置
plugins: [
new CopyPlugin({
patterns: [
{
from: path.join(process.cwd(), './pubilc'),
to: path.join(process.cwd(), './dist') ,
globOptions: {
ignore: ["**/index.html"],
},
}
],
}),
],合并开发生产 复用代码
process.env.NODE_ENV ===' production'
优化
问题1. node_modules单独打包出的文件比较大
思路: 将react相关的打包在一起 将antd打包在一起 将其他打包在一起
配置:
splitChunks: {
chunks: 'all',
// ndoe_modules文件打包出来的比较大
cacheGroups: {
// 根据项目实际情况 进行打包
// react react-dom react-router-dom 打包
react: {
test: /[\\/]node_modules[\\/]react(.*)?/,
name: 'chunk_react',
// 打包的权重肯定要比node_modules高 要不就没用了
priority: 40
},
// antd单独打包
antd: {
test: /[\\/]node_modules[\\/]antd?/,
name: 'chunk_antd',
// 打包的权重肯定要比node_modules高 要不就没用了
priority: 30 // 权重
},
// 剩余单独打包
libs: {
test: /[\\/]node_modules[\\/]/,
name: 'chunk_libs',
priority: 20
}
}
},
开发模式
npm i vue-loader vue-template-compiler vue-style-loader eslint-webpack-plugin eslint-plugin-vue html-webpack-plugin css-loader postcss-loader postcss-preset-env less-loader sass-loader sass stylus-loader babel-loader @vue/cli-plugin-babel webpack webpack-cli webpack-dev-server eslint @babel/eslint-parser -D
npm i vue
vue-loader的使用 --- 处理.vue文件
- npm i vue-loader vue-template-compiler -D
- 添加规则
{
test: /\.vue$/,
loader: 'vue-loader'
}- 引入插件,并调用
const {VueLoaderPlugin} = require('vue-loader')
plugins:[new VueLoaderPlugin()]- 拓展名extensions:['.vue', '.js', '.json']
- style-loader 修改成vue-style-loader
问题1. Feature flags are not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle. 运行起来浏览器报警告,
解决:
- const {DefinePlugin} from "webpack"
- 在plugins:[] 配置
new DefinePlugin({
__VUE_OPTIONS_API__: true,
__VUE_PROD_DEVTOOLS__: false
})生产模式
npm i mini-css-extract-plugin css-minimizer-webpack-plugin image-minimizer-webpack-plugin imagemin imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
图片压缩比较难下载, -f 当下载报错采用强制下载
合并
process.env.NODE_ENV
优化
使用element-plus
- 全部引入
- npm i element-plus
- 入口文件 import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css- app.use(ElementPlus )
- 在项目中直接使用,无需其他配置
- 按需引入 需要配置 --npm i element-plus
- npm i -D unplugin-vue-components unplugin-auto-import
- 配置webpack
- const AutoImport = require('unplugin-auto-import/webpack')
- const Components = require('unplugin-vue-components/webpack')
- const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
- plugins: [] 配置
- AutoImport({ resolvers: [ElementPlusResolver()], }),
- Components({ resolvers: [ElementPlusResolver()], }),
- 之后在组件中引入并注册组件才能使用
- import{ElButton} from 'element-plus'
- 使用
修改elementplus的主题配置 用到的时候再来看
https://www.bilibili.com/video/BV14T4y1z7sw?p=63&vd_source=4d085ef79ab7d7c6bec8ce5f945651da
优化打包
配置 在optimization的splitChunks中
splitChunks: {
chunks: 'all',
// ndoe_modules文件打包出来的比较大
cacheGroups: {
// 根据项目实际情况 进行打包
// react react-dom react-router-dom 打包
vue: {
test: /[\\/]node_modules[\\/]vue(.*)?/,
name: 'chunk_vue',
// 打包的权重肯定要比node_modules高 要不就没用了
priority: 40
},
// antd单独打包
antd: {
test: /[\\/]node_modules[\\/]element-(.*)?/,
name: 'chunk_element',
// 打包的权重肯定要比node_modules高 要不就没用了
priority: 30 // 权重
},
// 剩余单独打包
libs: {
test: /[\\/]node_modules[\\/]/,
name: 'chunk_libs',
priority: 20
}
}
},关闭性能分析
performance: false, //不展示文件大小性能 提升打包速度 关闭性能分析vue-loader做缓存
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
cacheDirectory: path.join(process.cwd(), './node_modules/.cache/vueLoaderCache')
}
},