自用记录文章 持续更新
为甚要用打包工具
在开发的时候,我们会用框架、es6模块化语法、less/sass等css预处理器
这样的代码必须要编译成浏览器能识别的css或者js语法,才能运行
所以我们需要打包工具
而且打包工具还能压缩代码、做兼容性处理、提升代码性能(把体积小的图片转化成base54格式减少请求,减轻服务器的压力)
简单介绍
webpack是一个静态资源打包文件
它会以一个或者几个文件作为打包入口,将整个项目所有文件编译组合成一个或者多个文件输出
输出的文件就可以在浏览器中运行了
功能介绍
webpack本身功能是有限的
++但是它可以使用loader或者plug拓展功能++
- 开发模式: 仅能编译js中的
ES Moudle语法 - 生产模式:能xxx还能压缩js代码
简单使用
npm init -y 初始化一个package.json文件
如果没有
-y那么后端会有很多选项 使用-y那么表示后续所有的选项都为yes
npm i webpack webpack-cli -D 下载webpack和weebpack-cli到生产环境
webpack、webpack-cli在实际开发过程中都是生产依赖
启动webpack
npx webpack ./src/main.js --mode=development
npx webpack ./src/main.js --mode=production
基本配置
五大核心概念
- entry(入口)
- output(输出)
- loader (加载器)
- plugins(插件)
- mode (模式)
准备webpack配置文件
在项目根目录下创建webpack.config.js
// Node.js的核心模块,专门用来处理文件路径
const path = require("path");
module.exports = {
// 入口
// 相对路径和绝对路径都行
entry: "./src/main.js",
// 输出
output: {
// path: 文件输出目录,必须是绝对路径
// path.resolve()方法返回一个绝对路径
// __dirname 当前文件的所在文件夹绝对路径
path: path.resolve(__dirname, "dist"),
// filename: 输出文件名
filename: "main.js",
clean: true, // 自动将上次打包目录资源清空
},
// 加载器
module: {
rules: [],
},
// 插件
plugins: [],
// 模式
// development和production webpack会默认配置很多模式
mode: "development", // 开发模式
};
src/math/sum.js
export default function sum(...args) {
return args.reduce((p,c) => {
return p + c
},0)
}
main.js
import sum from './math/sum.js';
console.log(123);
console.log(sum(1,2,3))
public/index.html
<body>
<div id="ly">liuyang</div>
</body>
<!-- 导入打包后的main.js文件 -->
<script src="../dist/main.js"></script>
npx webpack执行
可以看到dist文件下有一个以及打包好的main.js
处理各种资源
处理样式资源
webpack本身不能处理样式资源()比如css、less、sass或者scss、styl 但是可以使用Loader来帮助webpack解析样式资源
- 处理css资源
- 安装css-loader(将css资源编译成commonjs模块到js中)
- 安装style-loader(通过动态创建style标签 将js中的css添加到html文件中)
++加载css文件有两种方式 1.生成style标签然后把css样式放进去可以通过
css.loader2.生成.css文件然后通过link标签引入到html文件中,后续使用一个插件++
webpack.config.js
module: {
rules: [
{
test: /\.css$/, // 只检测以.css结尾的文件
use: [ // 执行顺序:下载后面的先执行
'style-loader',
'css-loader'
]
}
]
}
//一定要记得引入dist/main.js 中
<body>
<div class="box"></div>
<script src="../dist/main.js"></script>
</body>
这里对use说明一下 方便理解后续不同的写法
// 语法糖
// loader: [
// 'style-loader',
// 'css-loader'
// ]
// 如果只有一个loader 那么
// loader: 'style-loader'
// 完整版
// use: [
// {
// loader: 'style-loader',
// option: {
// }
// },
// {
// loader: 'css-loader',
// option: {
// }
// }
// ]
// 完成版简写
// use: [
// 'style-loader',
// 'css-loader'
// ]
- 处理less文件
- 安装less-loader
这个less-loader实际是用lessc(less-componier)把less文件转化成css文件
- 安装css-loader
- 安装style-loader
rules: [
{
test: /\.less$/,
use:[
"style-loader",
"css-loader",
"less-loader"
]
}
]
处理图片资源
webpack4中处理图片资源要安装file-loader和url-loader插件,webpack5已经将这两个功能内置了(webpack5中叫做资源模块类型),只用简单配置就好了
资源模块类型
- asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现;
- asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现;
- asset/source 导出资源的源代码。之前通过使用 raw-loader 实现;
- asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体 积限制实现
1.配置
webpack.config.js
mudule: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/,
type: "asset",
}
]
}
2.添加图片资源
css样式中引入img e.g. background-image: url("../images/1.jpeg");
3.可以看到dist文件下有一张图片
注意如果图片大小很小(具体多少还不知道)会被webpack5转化成dataUrl dataUrl是在dist文件下看不到的
dist文件下为什么没有样式资源呢? 因为经过 style-loader 的处理,样式资源打包到 main.js 里面去了,所以没有额外输出出来
4.优化图片
- 将小于某个大小的图片转化成 data URI 形式(Base64 格式)
- 设置打包后图片的文件名
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp)$/, //以png或jpg或jpeg或gif或webp结尾文件
type: "asset",
// 对图片进行转base64格式
parser: {
dataUrlCondition: {
// 将小于10kb的图片转为base64
// 优点:减少请求数量 减轻服务器的压力 缺点:体积会变大点(但是小图片变大一点也不大)
maxSize: 10 * 1024
}
},
generator: {
// 指定输出的图片名称
// hash: 10代表hash值只取前10位
// ext 文件后缀
// query 请求url的query
filename: 'static/images/[hash:10][ext][query]'
}
}
]
}
处理其他资源(字体、音频、视频)
包括 将打包后的文件分类
{
test: /\.(ttf|woff2?|map4|map3|avi)$/,
type: 'asset/resource', //原样输出
generator: {
filename: 'static/media/[hash:10][ext][query]'
}
}
使打包后的文件分类输出
output:{
// filename 代表入口文件输出的文件名
filename: 'xxx/xxx/main.js'
}
进一步处理js资源
webpack自己对js的处理是有限的,只能编译js中ES模块化语法,导致js不能在IE浏览器中运行 --可以使用Babel
其次在开发中,要保持团队代码一致性,我们要用专业工具来检测 --可以使用Eslint
Eslint
1.配置文件
eslint的配置文件有很多写法 注意文件要位于根目录
- eslintrc
- .eslintrc.js
- .eslintrc.json
或者 package.json中添加eslintConfig属性
2.具体配置
以.eslintrc.js文件为例
module.exports = {
// 解析选项
parserOptions: {},
// 具体检查规则
rules: {},
// 继承其他规则
extends: [],
// ...
// 其他规则详见:https://eslint.bootcss.com/docs/user-guide/configuring
};
- 解析选项说明
解析选项配置不对 那么打包的时候 控制台会对import以及export语法进行提示
"env": { "es6": true } // 启动es6全局变量 不确定啥是全局变量
<!--设置解析器选项能帮助 ESLint 确定什么是解析错误,所有语言选项默认都是 false。-->
parserOptions: {
ecmaVersion: 6, // 默认情况下,ESLint 支持 ECMAScript 5 语法 这里是启动es6语法 但是不能使用es6全局变量
sourceType: "module", // ES 模块化
ecmaFeatures: { // 可以设置额外的语言特性
jsx: true // 如果是 React 项目,就需要开启 jsx 语法
}
}
- rules规则
- "off" 或 0 - 关闭规则
- "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
- "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
e.g.
semi: "error", // 禁止使用分号
- extends继承
// 例如在React项目中,我们可以这样写配置
module.exports = {
extends: ["react-app"],
rules: {
// 我们的规则会覆盖掉react-app的规则
// 所以想要修改规则直接改就是了
eqeqeq: ["warn", "smart"],
},
};
3.使用
下载npm i eslint-webpack-plugin eslint -D
--save-dev开发依赖 等同于 -D--save运行时依赖 等同于 -S
webpack.config.js文件中引入
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src"),
// 打包的时候如果你写的源文件出现格式校验不通过,那么会自动进行fix
fix: true,
}),
],
如果有eslint插件 ..... 不用等到编译的时候才报错 会在编写代码的同时去检查你的代码 配合prettier可以自动修复不符合eslint规范的代码
忽略某些文件(这些文件下的出现的错误不报) 默认应该有
创建.eslintignore.js 文件
# 忽略dist目录下所有的文件
dist
Babel
是JavaScript 编译器。编译器包括几个阶段【解析、转换、生成阶段】
主要用于将 ES6 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中
1.配置文件
同eslint相似
2. 具体配置
- presets预设 一组babel插件 扩展babel功能
以.babelrc.js 为例 一般是用配置文件 否则就要写在webpake.config.json中 这样很难看
module.exports = {
// 预设
presets: [],
};
3.在 Webpack 中使用
npm i babel-loader @babel/core @babel/preset-env -D
.bablerc.js
module.exports = {
presets: ["@babel/preset-env"],
};
{
test: /\.js$/,
exclude: /node_modules/, // 排除node_modules代码不编译
loader: "babel-loader",
},
打开打包后的 dist/static/js/main.js 文件查看,会发现箭头函数等 ES6 语法已经转换了
处理html资源
1.下载包
npm i html-webpack-plugin -D
2.配置
在 webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
该插件会在dist文件下创建一个index.html文件, 该插件默认有一个模板。如果想要以自己的模板来创建文件 需要传入一个对象 如下:
plugins: [
new HtmlWebpackPlugin({
// 以 public/index.html 为模板创建文件
// 新的html文件(在dist下)有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源 自动生成<script defer src="js/main.js"></script>
template: path.resolve(__dirname, "public/index.html"),
}),
],
title
3. 修改 index.html
<!-- 导入打包后的main.js文件 ( 有了HtmlWebpackPlugin插件 就无需手动进行引入)-->
<!-- <script src="../dist/main.js"></script> -->
补充
模板文件中可能存在一些变量,如下面(取至vue-cli的public)的<%= BASE_URL %>favicon.ico和<%= htmlWebpackPlugin.options.title %> 可以使用插件
public/index.html文件
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
/**
* DefinePlugin
* 这里的插件用法 用来定义public/index.html 中<%= BASE_URL %> 的BASE_URL变量的值
* 当然也可以定义其他值
*/
const {DefinePlugin} = require('webpack');
new DefinePlugin({
BASE_URL:'"./"'
}),
//变量 <%= BASE_URL %>favicon.ico"> => ./favicon.ico
//至于 `<%= htmlWebpackPlugin.options.title %>` 表示的是htmlwebpackplugin 的参数 title: 'wb5'
new HtmlWebpackPlugin({
template: './public/index.html',
/**
* title 用来替代 htmlWebpackPlugin.options.title 这个变量
*/
title: 'wb5'
}),
开发服务器&自动化
每次写完代码都需要手动输入指令才能编译代码,太麻烦了,我们希望一切自动化
两种方式 webpack watch mod 和 webpack-dev-server(常用)
1. webpakck watch mod
不会自动刷新浏览器
在该模式下,webpack依赖图中的所有文件,只要有一个发生了更新,那么代码将被重新编译
1. 在webpack的配置中,添加 watch: true
2. 在启动webpack的命令中,添加 --watch的标识 webpack --watch
2. webpack-dev-server(常用)
-
下载包
npm i webpack-dev-server -D -
配置
module.exports = { // 开发服务器 devServer: { // 启动服务器的域名/ip e.g. 0.0.0.0回环地址 在同一个网段下的电脑都可以访问 host: "localhost", // 启动服务器端口号 port: "3000", // 是否自动打开浏览器 open: true, // 是否开启静态资源gzip 默认false 开启以后会对js文件压缩不会对html文件压缩,开启gzip以后content-coding=gzip compress: true }, }3.运行
npx webpack serve
开发服务器没有输出 代码编译打包是在内存中进行的,不会输出到dist目录下 e.g. 实际上是是放在了webpack-dev-serve的一个memfs 文件服务(express写的)
为啥没有输出? 因为如果有输出的到文件的话,devServer服务器会去文件中读取内容到内存这一步非常耗时间,况且开发环境不需要输出 我们可以打包以后让内容直接展示在浏览器中
open: true
devServer contentBase属性
用于配置 ++在开发阶段我们想要从额外的文件中获取资源++ 的路径, 比如: index.html中需要引用favorate.icon 这个时候开发环境又没有将资源打包进dist文件中,自然开发环境就不会出现icon,这个时候我们就可以配置contentBase属性
生产阶段我们是怎么做的呢?
生产阶段我们使用插件new CopyWebpackPlugin({from: to:}) 将指定文件复制到dist文件下,打比方 我们将favorate.icon 复制到dist文件下 打包以后 的index.html 依赖的icon就能拿到,即icon能显示
使用contentBase可以在开发环境优化打包速度
生产模式介绍
生产模式是开发完成代码后,我们需要得到代码将来部署上线。
这个模式下我们主要对代码进行优化,让其运行性能更好。
优化主要从两个角度出发:
优化代码运行性能 优化代码打包速度
生产模式准备
1. 文件目录
├── my-webpack (项目根目录)
├── config (Webpack配置文件目录)
│ ├── webpack.dev.js(开发模式配置文件)
│ └── webpack.prod.js(生产模式配置文件)
├── node_modules (下载包存放目录)
├── src (项目源码目录,除了html其他都在src里面)
│ └── 略
├── public (项目html文件)
│ └── index.html
├── .eslintrc.js(Eslint配置文件)
├── babel.config.js(Babel配置文件)
└── package.json (包的依赖管理配置文件)
2. 修改 webpack.dev.js 以及 webpack.prod.js
-
需要修改绝对路径 这里就不贴代码了
-
开发模式不需要输出 也不需要打包之前删除上次打包的文件
<!--webpack.dev.js-->
output: {
path: undefined, // 开发模式没有输出,不需要指定输出目录
filename: "static/js/main.js", // 将 js 文件输出到 static/js 目录中
// clean: true, // 开发模式没有输出,不需要清空输出结果
},
mode: "development"
<!--webpack.prod.js-->
output: {
path: path.resolve(__dirname,'../dist'),
filename: 'js/main.js',
clean: true
},
mode: "production"
- 命令
npx webpack serve --config ./config/webpack.dev.jsnpx webpack --config ./config/webpack.prod.js
有点复杂 稍后我去package.json中配置
"start": "npm run dev",
"dev": "npx webpack serve --config ./config/webpack.dev.js",
"build": "npx webpack --config ./config/webpack.pro.js",
进一步处理css资源
1. 提取css成单独文件
使用style-loader会使得出现闪屏现象
浏览器首先 解析html代码将结构渲染上去,然后解析js,样式是js解析完以后再加载的
我们应该通过link标签加载
style-loader是把 动态创建一个style标签把js中的css样式添加到html文件中 会出现闪屏现象 原因:浏览器先构建html js 然后加载样式
我们可以使用插件MiniCssExtractPlugin
1. 下载
npm i mini-css-extract-plugin -D
2. 配置
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 替换
`style-loader` -> `MiniCssExtractPlugin.loader`
// 提取css成单独文件
new MiniCssExtractPlugin({
// 定义输出文件名和目录
filename: "static/css/main.css",
}),
2. 兼容性处理
像flex 动画 等css样式在低版本的浏览器不能使用 用这个插件可以解决大多数样式问题
postcss是一个通过js来转化样式的工具 可以帮助我们进行css的转换和适配 实现这些功能也同样需要postcss对应的插件 比如
postcss-preset-env
postcss-preset-env可以帮助我们将一些现代的css特性转化成以前浏览器能认识的css语法和加浏览器前缀
- 比如 用16进制代表颜色 绝大多数浏览器支持#123456 但是现在css新语法可以支持8位后面两位代表透明度 如果想要所有浏览器适配的话就用可以用这个插件 e.g. 我们写color:#12345678-> color: rgba(18,52,86,0.47059);
1. 下载
npm i postcss-loader postcss postcss-preset-env -D
2. 配置
use: [
MiniCssExtractPlugin.loader,
'css-loader',
<!-- 注意放的位置-->
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
'sass-loader'
]
3. 控制兼容性
我们可以在 package.json 文件中添加 browserslist 来控制样式的兼容性做到什么程度。
{ // 其他省略 "browserslist": ["ie >= 8"] }
实际开发中我们一般不考虑旧版本浏览器了,所以我们可以这样设置:
{ // 其他省略 "browserslist": ["last 2 version", "> 1%", "not dead"] }
4. 合并配置
const getStyleLoader = (loader) => {
return [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解决大多数样式兼容性问题
],
},
},
},
loader
];
};
{
test: /\.s[ac]ss$/,
use: getStyleLoader('sass-loader')
},
3. css压缩
1. 压缩包
npm i css-minimizer-webpack-plugin -D
2.配置
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// css压缩
new CssMinimizerPlugin(),
html压缩
默认生产模式已经开启了:html 压缩和 js 压缩
不需要额外进行配置