为什么需要打包工具?
开发时,我们会使用Vue、React、ES6模块化语法、Less/Sass等css预处理器等语法进行开发, 这样的代码要想运行在浏览器中,就必须经过编译成浏览器能识别的Js、Css等语法 所以我们需要打包工具帮我们做完这些事
打包工具都有哪些?
- Grunt
- Gulp
- Parcel
- Webpack
- Rollup
- Vite
- ... 目前市面上最流行的是Webpack,所以我们主要以webpack来介绍打包工具
基本使用
Webpack是一个静态资源打包工具
它会以一个或多个文件作为打包的入口,将我们整个项目所有文件编译组合成一个或多个文件输出出去
输出的文件就是编译好的文件,就可以在浏览器运行了
我们将Webpack输出的文件叫做bundle
功能介绍
Webpack本身功能是有限的,只能处理js资源,一旦遇到Css等其他资源就会报错
- 开发模式:仅能编译Js中的ES Module语法
- 生产模式:能编译Js中的ES Module语法,还能压缩Js代码
开始使用
//1.初始化 packge.json
npm init -y
//2.下载依赖
npm i webpack-cli -D
//3.启动Webpack
开发模式 npx webpack ./src/main.js --mode=development //仅能编译Js中的ES Module语法
生产模式 npx webpack ./src/main.js --mode=production //能编译Js中的ES Module语法,还能压缩Js代码
基本配置
在使用Webpack之前,我们需要对Webpakc的配置有一定的了解
5大核心概念
- entry(入口):告诉Webpack从那个文件开始打包
- output(输出):告诉Webpack打包完的文件输出到哪里去,如何命名等
- loader(加载器):Wwebpack本身只能处理js、json等资源,其他需要借助loader才能进行解析
- plugins(插件):扩展Webpack的功能
- mode(模式):开发模式-development, 生产模式-production
准备Webpack配置文件
在项目根目录下新建文件:webpack.config.js
module.exports = {
entry: "./src/main.js",
output: {
//文件输出的路径
//__dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, 'dist'),
//文件名
filename: 'main.js'
},
//加载器
module: {
rules: [
]
},
//插件
plugins: [
],
mode: 'development'
}
开发模式介绍
开发模式主要做两件事:
- 编译代码,是浏览器能识别运行,开发时我们有样式资源,字体图标,html资源等,webpack默认都不能处理这些资源,所以我们要加载配置来编译这些资源
- 代码质量检查,树立代码规范,提前检查代码的一些隐患,让代码运行时能更加健壮。提前检查代码规范和格式,统一团队编码风格,让代码更优雅美观
处理样式资源、图片资源、字体图标资源、其他资源等
Webpack本身是不能识别样式资源的,所以我们需要借助Loader来帮助Webpack解析样式资源 我们找Loader去官方文档查找: Webpack官方文档
//webpack.config.js (根目录)
const path = require('path')
module.exports = {
entry: "./src/main.js",
output: {
//文件输出的路径
//__dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, 'dist'),
//入口文件打包的输出文件名
filename: 'static/js/main.js',
//打包前,自动清理上次打包的文件夹,并再次打包
clean: true
},
//加载器
module: {
rules: [
{
test: /\.css$/i,//只检测.css文件
use: [
//将js中css通过创建style标签添加到html文件中生效
"style-loader",
//将css资源编译成commonjs的模块到js中
"css-loader"
], //执行顺序:从右至左(从下到上)
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
'style-loader',
'css-loader',
'less-loader',
],
},
{
test: /\.s[ac]ss$/i,
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// 将 CSS 转化成 CommonJS 模块
'css-loader',
// 将 Sass 编译成 CSS
'sass-loader',
],
},
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
'stylus-loader'
], // 将 Stylus 文件编译为 CSS
},
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset', //会转成base64
parser: {
dataUrlCondition: {
//小于4kb的图片才会被打包成base64格式的图片
maxSize: 4 * 1024
}
},
generator: {
//输出图片名称
filename: 'static/images/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
{
test: /\.(ttf|woff2?|map3|mp4|avi|rmvb)$/,
type: 'asset/resource',//不会转成base64
generator: {
//输出图片名称
filename: 'static/media/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
}
],
},
//插件
plugins: [
],
mode: 'development'
}
处理js资源
Eslint
可组装的JavaScript和JSX检查工具 我们使用Eslint,关键是写配置文件,里面写上各种rules规则,将来运行Eslint时就会以写的规则对代码进行检查
1.配置文件
配置文件的写法有很多: eslintrc.*:新建文件,位于项目根目录
- eslintrc
- eslintrc.js
- eslintrc.json
- 区别在于格式不一样 packge.json中eslintConfig:不需要创建文件,在原有文件基础上写 ESlint会查找和自动读取它们,所以以上配置文件只需要存在一个即可
2.具体配置
我们以.eslintrc.js为例
module.exports={
//解析选项
parserOptions:{},
//具体检查规则
rules:{},
//继承其他规则
extends:[],
...
}
1.parserOptions解析选项
parserOptions:{
ecmaVersion:6,//ES语法版本
sourceType:"module",//ES模块化
ecmaFeatures:{// ES其他特性
jsx:true//如果是React项目,需要开启jsx语法
}
}
2.rules具体规则
- off 或 0 关闭规则
- warn 或 1 开启规则,使用警告级别的错误:warn(不会导致程序退出)
- error 或 2 开启规则,使用错误级别的错误:error(当被触发时,程序会退出)
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
]
}
3.extends继承 开发中一点点写rules规则十分麻烦,所以有更好的办法,继承现有规则 现有以下较为有名的规则:
- ESlint官方规则
- Vue Cli官方规则
- React Cli官方规则
//例如在React项目中,我们可以这样配置
module.exports={
extends:["react-app"],
rules:{
//我们的规则会覆盖掉react-app的规则
//所以我们要想修改规则直接修改就是了
}
}
Babel
npm install -D babel-loader @babel/core @babel/preset-env
JavaScript编译器 主要用于将ES6语法编写的代码转换为向后兼容的JavaScript语法,以便能够运行在当前和旧版本的浏览器或其他环境中
1.配置文件
配置文件写法有很多 babel.config.*:新建文件,位于项目根目录
- .babelrc
- .babelrc.js
- .babelrc.json
- packge.json中babel:不需要创建文件,在原有文件基础上写,Babel会查找和读取它们,以上配置只需存在一种
2.具体配置
我们以babel.config.js配置为例
module.exports={
//预设
presets:[],
}
简单理解:就是一组Babel插件,扩展Babel功能
- @babel/preset-env: 一个智能预设,允许您使用最新的JavaScript
- @babel/preset-react:一个用来编译React jsx语法的预设
- @babel/preset-typescript:一个用来编译TypeScitpt的语法预设
处理html资源
npm install --save-dev html-webpack-plugin
new HtmlWebpackPlugin({
//模板:以public/index.html文件创建新的html文件
//新的html文件特点:1.结构和原来一致,2.自动引入打包输出的资源
template: path.resolve(__dirname, 'public/index.html')
})
],
开发服务器&自动化
- 下载包:
npm i webpack-dev-server -D - 配置:
//开发服务器
devServer: {
host: 'localhost',
port: '3000',
open: true//是否自动打开浏览器
},
生产模式介绍
我们将根目录的webpack.config.js删除,然后创建config文件夹 再分别创建两种开发模式对应的config.js
开发模式config/webpack.dev.js
// config/webpack.dev.js
//开发环境
const path = require('path')
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: "./src/main.js",
output: {
//文件输出的路径
//开发模式没有输出
path: undefined,
//入口文件打包的输出文件名
filename: 'static/js/main.js',
},
//加载器
module: {
rules: [
{
test: /\.css$/i,//只检测.css文件
use: [
//将js中css通过创建style标签添加到html文件中生效
"style-loader",
//将css资源编译成commonjs的模块到js中
"css-loader"
], //执行顺序:从右至左(从下到上)
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
'style-loader',
'css-loader',
'less-loader',
],
},
{
test: /\.s[ac]ss$/i,
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// 将 CSS 转化成 CommonJS 模块
'css-loader',
// 将 Sass 编译成 CSS
'sass-loader',
],
},
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
'stylus-loader'
], // 将 Stylus 文件编译为 CSS
},
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset', //会转成base64
parser: {
dataUrlCondition: {
//小于4kb的图片才会被打包成base64格式的图片
maxSize: 4 * 1024
}
},
generator: {
//输出图片名称
filename: 'static/images/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
{
test: /\.(ttf|woff2?|map3|mp4|avi|rmvb)$/,
type: 'asset/resource',//不会转成base64
generator: {
//输出图片名称
filename: 'static/media/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
// {
// test: /\.js$/,
// exclude: /node_modules/,//排除node_modules中的js文件(这些文件不处理)
// loader: 'babel-loader',
//可以单独写babel.config.js去配置
// options: {
// presets: [
// "@babel/preset-env"
// ]
// }
// }
],
},
//插件
plugins: [
new ESLintPlugin({
//监测哪些文件
context: path.resolve(__dirname, '../src')
}),
new HtmlWebpackPlugin({
//模板:以public/index.html文件创建新的html文件
//新的html文件特点:1.结构和原来一致,2.自动引入打包输出的资源
template: path.resolve(__dirname, '../public/index.html')
})
],
//开发服务器
devServer: {
host: 'localhost',
port: '3000',
open: true//是否自动打开浏览器
},
mode: 'development'
}
生产模式config/webpack.prod.js
// config/webpack.prod.js
// 生产环境
const path = require('path')
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: "./src/main.js",
output: {
//文件输出的路径
//__dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, '../dist'),
//入口文件打包的输出文件名
filename: 'static/js/main.js',
//打包前,自动清理上次打包的文件夹,并再次打包
clean: true
},
//加载器
module: {
rules: [
{
test: /\.css$/i,//只检测.css文件
use: [
//将js中css通过创建style标签添加到html文件中生效
"style-loader",
//将css资源编译成commonjs的模块到js中
"css-loader"
], //执行顺序:从右至左(从下到上)
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
'style-loader',
'css-loader',
'less-loader',
],
},
{
test: /\.s[ac]ss$/i,
use: [
// 将 JS 字符串生成为 style 节点
'style-loader',
// 将 CSS 转化成 CommonJS 模块
'css-loader',
// 将 Sass 编译成 CSS
'sass-loader',
],
},
{
test: /\.styl$/,
use: [
'style-loader',
'css-loader',
'stylus-loader'
], // 将 Stylus 文件编译为 CSS
},
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset', //会转成base64
parser: {
dataUrlCondition: {
//小于4kb的图片才会被打包成base64格式的图片
maxSize: 4 * 1024
}
},
generator: {
//输出图片名称
filename: 'static/images/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
{
test: /\.(ttf|woff2?|map3|mp4|avi|rmvb)$/,
type: 'asset/resource',//不会转成base64
generator: {
//输出图片名称
filename: 'static/media/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
// {
// test: /\.js$/,
// exclude: /node_modules/,//排除node_modules中的js文件(这些文件不处理)
// loader: 'babel-loader',
//可以单独写babel.config.js去配置
// options: {
// presets: [
// "@babel/preset-env"
// ]
// }
// }
],
},
//插件
plugins: [
new ESLintPlugin({
//检测哪些文件
context: path.resolve(__dirname, '../src')
}),
new HtmlWebpackPlugin({
//模板:以public/index.html文件创建新的html文件
//新的html文件特点:1.结构和原来一致,2.自动引入打包输出的资源
template: path.resolve(__dirname, '../public/index.html')
})
],
//生产模式不需要devServer
// devServer: {
// host: 'localhost',
// port: '3000',
// open: true//是否自动打开浏览器
// },
mode: 'production'
}
最后,为了运行起来方便,我们稍微对packge.json做出改动
生产模式下的CSS处理
提取Css成单独文件 Css文件目前被打包到js文件中,当js文件加载时,会创建一个style标签来生成样式 这样对于网站来说,会出现闪屏现象,用户体验不好 我们应该是单独的Css文件,通过link标签加载性能才能好
1.下载包
pnpm i --save-dev mini-css-extract-plugin
2.配置
// 生产环境
const path = require('path')
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: "./src/main.js",
output: {
//文件输出的路径
//__dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, '../dist'),
//入口文件打包的输出文件名
filename: 'static/js/main.js',
//打包前,自动清理上次打包的文件夹,并再次打包
clean: true
},
//加载器
module: {
rules: [
{
test: /\.css$/i,//只检测.css文件
use: [
//将js中css通过创建style标签添加到html文件中生效
MiniCssExtractPlugin.loader,
//将css资源编译成commonjs的模块到js中
'css-loader'
], //执行顺序:从右至左(从下到上)
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
},
{
test: /\.s[ac]ss$/i,
use: [
// 将 JS 字符串生成为 style 节点
MiniCssExtractPlugin.loader,
// 将 CSS 转化成 CommonJS 模块
'css-loader',
// 将 Sass 编译成 CSS
'sass-loader',
],
},
{
test: /\.styl$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'stylus-loader'
], // 将 Stylus 文件编译为 CSS
},
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset', //会转成base64
parser: {
dataUrlCondition: {
//小于4kb的图片才会被打包成base64格式的图片
maxSize: 4 * 1024
}
},
generator: {
//输出图片名称
filename: 'static/images/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
{
test: /\.(ttf|woff2?|map3|mp4|avi|rmvb)$/,
type: 'asset/resource',//不会转成base64
generator: {
//输出图片名称
filename: 'static/media/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
// {
// test: /\.js$/,
// exclude: /node_modules/,//排除node_modules中的js文件(这些文件不处理)
// loader: 'babel-loader',
//可以单独写babel.config.js去配置
// options: {
// presets: [
// "@babel/preset-env"
// ]
// }
// }
],
},
//插件
plugins: [
new ESLintPlugin({
//检测哪些文件
context: path.resolve(__dirname, '../src')
}),
new HtmlWebpackPlugin({
//模板:以public/index.html文件创建新的html文件
//新的html文件特点:1.结构和原来一致,2.自动引入打包输出的资源
template: path.resolve(__dirname, '../public/index.html')
}),
new MiniCssExtractPlugin({
filename: 'static/css/main.css'
}),
],
//生产模式不需要devServer
// devServer: {
// host: 'localhost',
// port: '3000',
// open: true//是否自动打开浏览器
// },
mode: 'production'
}
Css兼容性处理
1.下载包npm i postcss-loader postcss postcss-preset-env -D
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env' //能解决大多数样式兼容性问题
]
}
}
},
把以上代码配置到每一个处理css的loader里(css-loader后,less-loader前) 如下:
// 生产环境
const path = require('path')
const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
entry: "./src/main.js",
output: {
//文件输出的路径
//__dirname nodejs的变量,代表当前文件的文件夹目录
path: path.resolve(__dirname, '../dist'),
//入口文件打包的输出文件名
filename: 'static/js/main.js',
//打包前,自动清理上次打包的文件夹,并再次打包
clean: true
},
//加载器
module: {
rules: [
{
test: /\.css$/i,//只检测.css文件
use: [
//将js中css通过创建style标签添加到html文件中生效
MiniCssExtractPlugin.loader,
//将css资源编译成commonjs的模块到js中
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env' //能解决大多数样式兼容性问题
]
}
}
}
], //执行顺序:从右至左(从下到上)
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env' //能解决大多数样式兼容性问题
]
}
}
},
'less-loader',
],
},
{
test: /\.s[ac]ss$/i,
use: [
// 将 JS 字符串生成为 style 节点
MiniCssExtractPlugin.loader,
// 将 CSS 转化成 CommonJS 模块
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env' //能解决大多数样式兼容性问题
]
}
}
},
// 将 Sass 编译成 CSS
'sass-loader',
],
},
{
test: /\.styl$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'postcss-preset-env' //能解决大多数样式兼容性问题
]
}
}
},
'stylus-loader'
], // 将 Stylus 文件编译为 CSS
},
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset', //会转成base64
parser: {
dataUrlCondition: {
//小于4kb的图片才会被打包成base64格式的图片
maxSize: 4 * 1024
}
},
generator: {
//输出图片名称
filename: 'static/images/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
{
test: /\.(ttf|woff2?|map3|mp4|avi|rmvb)$/,
type: 'asset/resource',//不会转成base64
generator: {
//输出图片名称
filename: 'static/media/[hash:10][ext][query]' //[hash:10]表示 hash值取前十位,[ext]后缀名,[query]参数
}
},
// {
// test: /\.js$/,
// exclude: /node_modules/,//排除node_modules中的js文件(这些文件不处理)
// loader: 'babel-loader',
//可以单独写babel.config.js去配置
// options: {
// presets: [
// "@babel/preset-env"
// ]
// }
// }
],
},
//插件
plugins: [
new ESLintPlugin({
//检测哪些文件
context: path.resolve(__dirname, '../src')
}),
new HtmlWebpackPlugin({
//模板:以public/index.html文件创建新的html文件
//新的html文件特点:1.结构和原来一致,2.自动引入打包输出的资源
template: path.resolve(__dirname, '../public/index.html')
}),
new MiniCssExtractPlugin({
filename: 'static/css/main.css'
}),
],
//生产模式不需要devServer
// devServer: {
// host: 'localhost',
// port: '3000',
// open: true//是否自动打开浏览器
// },
mode: 'production'
}
但此时打包还不能生效,因为还未告诉webpack 你的兼容性处理的要求,比如浏览器的版本要求做到什么程度? 举个例子,我们在packge.json最后添加了一个字段browserslist,意思是对>=ie8的浏览器做css兼容
{
"name": "webpack-v5",
"version": "1.0.0",
"description": "",
"main": "./src/main.js",
"scripts": {
"start": "npm run dev",
"dev": "webpack server --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^6.8.1",
"eslint-webpack-plugin": "^4.0.1",
"html-webpack-plugin": "^5.5.3",
"less": "^4.1.3",
"less-loader": "^11.1.3",
"mini-css-extract-plugin": "^2.7.6",
"postcss": "^8.4.27",
"postcss-loader": "^7.3.3",
"postcss-preset-env": "^9.1.0",
"sass": "^1.64.2",
"sass-loader": "^13.3.2",
"style-loader": "^3.3.3",
"stylus": "^0.59.0",
"stylus-loader": "^7.1.3",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
},
"browserslist":[
"ie >= 8"
]
}
然后可以去测试一下,我们在css样式中添加一个display:flex,然后打包,可以看到我们的css样式中增加了一个对display做出的兼容
//做css兼容之前
.box1{
display: flex;
width: 100px;
height: 100px;
background-image: url('../images/1.jpg');
background-size: cover;
}
//做兼容之后
.box1{
display: -ms-flexbox;
display: flex;
width: 100px;
height: 100px;
background-image: url(../../static/images/c6757f0668.jpg);
background-size: cover;
}
但是要注意,实际开发中,我们可能会对浏览器版本做以下要求
"browserslist":[
"last 2 version",
"> 1%",
"not dead"
]
Css压缩
下载包 npm i css-minimizer-webpack-plugin -D
//插件
plugins: [
new ESLintPlugin({
//检测哪些文件
context: path.resolve(__dirname, '../src')
}),
new HtmlWebpackPlugin({
//模板:以public/index.html文件创建新的html文件
//新的html文件特点:1.结构和原来一致,2.自动引入打包输出的资源
template: path.resolve(__dirname, '../public/index.html')
}),
new MiniCssExtractPlugin({
filename: 'static/css/main.css'
}),
new CssMinimizerPlugin(),
],
最后进行打包,可以看到打包出来的css文件已经被压缩。(js和html文件默认会进行压缩) 总结自尚硅谷webpack基础课程 课程链接:www.bilibili.com/video/BV14T…