webpack功能
打包:将不同类型资源按模块处理进行打包
静态:打包最后产出静态资源
模块:webpack支持不同规范的模块化开发
1、Loader
起到转换的作用,把当前代码转成webpack能识别处理的模块
webpack5中,在cli里面写loader的方式已经被废弃
CSS-Loader和style-loader
npm i css-loader style-loader -D
行内loader,单独使用不会报错,但是不会起作用,还要加上style-loader
import 'css-loader!../css/login.css'
function login() {
const oH2 = document.createElement('h2')
oH2.innerHTML = '拉勾教育前端'
oH2.className = 'title'
return oH2
}
document.body.appendChild(login())
配置文件
const path = require('path')
module.exports = {
...
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}
如果有less或者sass,则新增一个less-loader处理
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
browserslistrc和postcss
> 1%
last 2 version
not dead
使用postcss处理CSS兼容之前需要进行配置
www.caniuse.com 浏览器兼容性查询网站,node_modules里browserslist包会去这个网站匹配
>1%,兼容处理市面上占有率大于1%的浏览器
deafault,0.5%
dead 24个月没更新,没支持
last 2 version 最新的2个版本
也可以直接在package.json里配置browserslist属性
postcss
利用js来转换样式做兼容的工具,在不兼容的css属性和值前面加上前缀
如果是使用postcss-cli命令行手动的话,就是根据之前写的browserslistrc配置信息,然后使用autoprefixer来进行css修改npm i postcss-cli autoprefixer -D
npx postcss --use autoprefixer -o rest.css ./src/css/test.css
webpack里则可以通过loadernpm i postcss-loader -D
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
npm i postcss-preset-env -D
color的rgba写法或者一些新特性,部分浏览器无法兼容,使用postcss-preset-env这个预设的插件集合来进行处理,这个集合里已经有之前使用的autoprefixer
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env']
}
}
}
]
}
复用配置属性
但是,此时如果有less或者sass的时候,又需要把前面写的重新写一份,这样代码就冗余,webpack提供了一种复用的额外配置方式
在项目根目录下创建postcss.config.js
module.exports = {
plugins: [
// require('postcss-preset-env')
require('autoprefixer')
]
}
之后,就可以直接写postcss,不用多写插件配置
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
导入样式处理
之前我们处理的样式兼容,如果是通过@import进来的postcss无法处理
@import './test.css';
.title {
color: #12345678;
}
导入之后,当前css文件交给postcss处理,基于当前的筛选条件,postcss-loader发现不需要处理,交给了css-loader,而css-loader可以处理@import、@media和url等
这个时候它获取到了test.css,但是无法返回给postcss进行处理,只能进行下一步
webpack考虑到了这个情况,提供了importLoaders属性,值为数字,代表遇到了这种情况,往前找第几个loader
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'postcss-loader'
]
}
图片处理
file-loader
打包图片文件,返回webpack能识别的格式
把要处理的图片资源,用hash改名后拷贝到指定目录,默认是dist目录,然后在使用的地方返回路径npm i file-loader -D
img src
{
test: /\.(png|svg|gif|jpe?g)$/,
use: ['file-loader']
}
function packImg() {
const oEle = document.createElement('div')
const oImg = document.createElement('img')
oImg.width = 600
oImg.src = require('../img/01.wb.png').default
oEle.appendChild(oImg)
return oEle
}
document.body.appendChild(packImg())
webpack5中,file-loader返回的资源变成了一个对象,其中default属性才是真正需要的资源,要改写法
如果不想这样写,webpack需要改一下配置,esModule,含义是,是否将导出的内容转为esmodule
{
test: /\.(png|svg|gif|jpe?g)$/,
use: [
{
loader: 'file-loader',
options: {
esModule: false // 不转为 esModule
}
}
]
}
function packImg() {
const oEle = document.createElement('div')
const oImg = document.createElement('img')
oImg.width = 600
oImg.src = require('../img/01.wb.png')
oEle.appendChild(oImg)
return oEle
}
document.body.appendChild(packImg())
还有一种写法,采用esmodule的导入方法,webpack配置改回之前的写法
import oImgSrc from '../img/01.wb.png'
function packImg() {
const oEle = document.createElement('div')
const oImg = document.createElement('img')
oImg.width = 600
oImg.src = oImgSrc
oEle.appendChild(oImg)
return oEle
}
document.body.appendChild(packImg())
{
test: /\.(png|svg|gif|jpe?g)$/,
use: ['file-loader']
}
background src
.bgBox {
width: 240px;
height: 310px;
border: 1px solid #000;
background-image: url('../img/02.react.png');
}
import '../css/img.css'
function packImg() {
const oEle = document.createElement('div')
const oBgImg = document.createElement('div')
oBgImg.className = 'bgBox'
oEle.appendChild(oBgImg)
return oEle
}
document.body.appendChild(packImg())
css文件中使用了url的时候,css-loader在解析时会默认转为对象,所以需要修改配置
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
use: ['file-loader']
}
配置图片名字和输出的路径
扩展名
- [ext]: 扩展名
- [name]: 文件名
- [hash]: 根据文件内容和MD4算法生成的128位hash值
- [contentHash]:file-loader里和hash一样
- [hash:
]:固定长度的hash - [path]:不常用
输出的路径:dist目录下的文件夹名字,可以写在outputPath属性,也可以直接在名字前面加路径
{
test: /\.(png|svg|gif|jpe?g)$/,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
//outputPath: 'img'
}
}
]
}
url-loader
npm i url-loader -D
把要打包的图片资源以base64 URI的方式加载到代码里
优点:只需要请求一次,减少请求次数
缺点:当前图片如果过大,一次性请求的数据量过大,请求速度可能达不到业务要求
所以,需要限制使用url-loader时的图片大小,使用limit属性进行限制,超过限制的url-loader会自动去调用已经安装的file-loader
{
test: /\.(png|svg|gif|jpe?g)$/,
use: [
{
loader: 'url-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
limit: 25 * 1024
}
}
]
}
asset
webpack5中新增的处理图片和字体的资源类型模块,不需要再使用loader
图片
asset module type可以直接写type的值为下面3个,代表之前的loader
- asset/resource —>file-loader( 输出路径 )
- asset/inline —->url-loader(所有 data uri)
- asset/source —->raw-loader
- asset (parser )
上面这样的话,名称修改就变成全局的了,不太合适,所以要改到图片处理部分写generatoroutput: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
assetModuleFilename: "img/[name].[hash:4][ext]"
}
....
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset/resource',
}
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024
}
}
}
字体
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
}
2、plugin插件
loader对特定的文件类型进行转换,在读取某一个特定类型的资源时开始工作
plugin可以做更多事情,在打包的生命周期中任一钩子来操作clean-webpack-plugin
和之前的一样用,npm i clean-webpack-plugin -D
安装,然后导入使用const {CleanWebpackPlugin} = require('clean-webpack-plugin')
plugins:[new CleanWebpackPlugin()]
html-webpack-plugin
npm i html-webpack-plugin -D
在dist目录下创建html,并且自动引入JS文件,DefinePlugin是webpack内置,用来使用公共变量的插件 ```javascript const { DefinePlugin } = require(‘webpack’) const { CleanWebpackPlugin } = require(‘clean-webpack-plugin’) const HtmlWebpackPlugin = require(‘html-webpack-plugin’)
plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: ‘html-webpack-plugin’, template: ‘./public/index.html’ }), new DefinePlugin({ BASE_URL: ‘“./“‘ }) ]
index.html的模板
```javascript
<!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>
copy-webpack-plugin
npm i copy-webpack-plugin -D
将静态资源拷贝到dist目录下
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"'
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
})
]
babel
babel是一个JS编译器,用来转换最新的JS语法,把ES6, ES7等语法转化成ES5语法,从而能够在大部分浏览器中运行
编译过程:
1.parse分析:第一步是babel使用babylon将原始代码转换为AST抽象语法树
2.transform转化:第二步是babel通过babel-traverse对前面的抽象语法树进行遍历修改并获得新的抽象语法树
3.generator生成代码:第三步是babel使用babel-generator将抽象语法树转换为代码
这三个操作通过babel-core合成一个对外的api供外界使用
babel-loadernpm i babel-loader @babel/core @babel/preset-env -D
{
test: /\.js$/,
use: ['babel-loader']
}
根目录下创建babel.config.js
module.exports = {
presets: ['@babel/preset-env']
}
也可以直接写
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
}
polyfill
babel默认只转化新的js句法,而不会处理新的API,所以需要polyfill来处理,它会新增对应的方法
webpack5之后要优化速度,polyfill包太大,不需要安装@babel/polyfill,自己配置npm i core-js regenerator-runtime
注意的是,我们用的第三方包可能也用到了一些新的API,但是我们的代码没有,如果是usage不会填充进来,所以还是使用entry
module.exports = {
presets: [
[
'@babel/preset-env',
{
// false: 不对当前的JS处理做 polyfill 的填充
// usage: 依据用户源代码当中所使用到的新语法进行填充
// entry: 依据我们当前筛选出来的浏览器决定填充什么
useBuiltIns: 'entry',
corejs: 3
}
]
]
}