- 现代Web开发“问题”
- 采用模块化
- 使用新特性提高效率保证安全性
- 实时监听开发过程使用热更新
- 项目结果打包压缩优化
- 为现代JavaScript应用提供静态模块打包
webPack功能
1.打包:将不同类型资源按模块处理进行打包
2.静态:打包后最终产出静态资源
3.模块: Webpack支持不同规范的模块开发
配置文件
webpack的配置文件写在webpack.config.js
const path = require('path')
module.exports = {
// 入口
entry: './src/index.js',
// 出口
output: {
filename: 'build.js',
// path.resolve() 将相对路径转换为绝对路径,__dirname 获取当前文件所属目录的绝对路径
path: path.resolve(__dirname, 'dist')
}
}
运行配置 在package.json配置build,之后运行npm run build就会运行webpack配置
"scripts": {
"build": "webpack"
},
loader
webpack不是所有类型的文件都能当模块进行使用,因此我们需要使用loader对它进行转换
CSS-loader使用
默认情况下webpack是不能处理css文件的,因为它并不是一个js模块,因此我们需要用CSS-loader对css文件进行一个转换,然后webpack拿到转换后的文件进行打包(此时样式并没有生效我们还需要用到style-loader
安装 npm i css-loader -D
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
// 方法1
{
test: /\.css$/, // 一般就是一个正则表达式,用于匹配我们需要处理的文件类型
use: [
{
loader: 'css-loader'
}
]
},
// 方法2
{
test: /\.css$/,
loader: 'css-loader'
},
// 方法3
{
test: /\.css$/,
use: ['css-loader']
}
]
}
}
style-loader
作用:在我们当前界面生成一个style标签,然后把处理好的样式添加进来
安装 npm i style-loader -D
...
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] // 执行的顺序是从后往前
}
]
}
...
less-loader
安装 npm i less-loader -D
...
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
...
browserslistrc
- 1 工程化:
- 2 兼容性: CSS JS
- 3 如何实现兼容:
- 4 到底要兼容哪些平台
- caniuse.com
可以建一个.browserslistrc文件配置browserslistrc
> 0.01%
last 2 version
not dead
postcss工作流程
postcss是JavaScript转换样式的工具,通过browserslistrc告诉它需要兼容的平台
安装 npm i postcss -D相当于解析器,到时候再安装些插件完成对样式的转换
可以去做一些兼容性的工作,比如给css添加不同的浏览器前缀
安装 npm i postcss-cli -D可以在命令行终端使用命令
npm i autoprefixer -D添加前缀
使用: npx postcss --use autoprefixer -o ret.css ./src/css/test.css
postcss-loader处理兼容
除了使用命令行使用autoprefixer添加前缀,我们可以使用postcss-loader
安装 npm i postcss-loader -D
postcss在css-loader之前进行工作
...
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('autoprefixer')
]
}
}
}
]
}
]
}
上面这种方式如果还要兼容别的就要再引用相应的插件,比较麻烦,因此我们可以使用下面的方式
postcss-preset-env 预设 -- 插件集合(集合了很多现代css转换时需要用到的插件)
安装 npm i postcss-preset-env -D
...
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('postcss-preset-env')
]
}
}
}
]
},
// 但是还有less的之后就得再配置一次,代码就会显得冗余
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('postcss-preset-env')
]
}
}
},
'less-loader'
]
}
]
}
我们可以单独使用一个配置文件postcss.config.js
module.exports = {
plugins: [
// require('postcss-preset-env')
require('autoprefixer')
]
}
写了这个之后webpack的配置文件就可以简写
...
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
...
importLoaders属性
若有使用@import引入css的文件,根据上面的配置打包会发现test.css中的代码没有被解析
@import './test.css';
.title {
color: #12345678;
}
此时我们需要使用如下的配置
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'postcss-loader'
]
}
]
}
importLoaders: n表示遇到有使用@import方法引入文件时,我们需要往前使用n个loader来解析文件,这里的importLoaders: 1就表示往前使用一个也就是postcss-loader来处理文件
file-loader处理文件
安装 npm i file-loader -D
/**
* 打包图片:
* - img src
* + 使用 require 导入图片,此时如果不配置 esModule: false ,则需.default 导出
* + 也可以在配置当中设置 esModule: false
* + 采用 import xxx from 图片资源,此时可以直接使用 xxxx
* - background url
*/
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
},
{
test: /\.(png|svg|gif|jpe?g)$/,
// use: [
// {
// loader: 'file-loader',
// options: {
// esModule: false // 不转为 esModule
// }
// }
// ]
use: ['file-loader']
}
]
}
***********img.css****************
.bgBox {
width: 240px;
height: 310px;
border: 1px solid #000;
background-image: url('../img/02.react.png'); // css-loader可以识别url的,并将此替换为require语法,而require语法更新后默认导出的是esModule,我们可以在css-loader的配置中
}
***********Image.js***************
import oImgSrc from '../img/01.wb.png'
import '../css/img.css' // 被依赖进来后将来会被我们配置的css的相关loader解析
function packImg() {
// 01 创建一个容器元素
const oEle = document.createElement('div')
// 02 创建 img 标签,设置 src 属性
const oImg = document.createElement('img')
oImg.width = 600
// file-loader更新后为了适配webpack5,此处返回的是一个对象的结构{ default: xxx }
// oImg.src = require('../img/01.wb.png').default
// 通过file-loader的配置esModule: false(是否将我们导出的内容转化成esModule)可以不使用.default
// oImg.src = require('../img/01.wb.png')
// 采用esModel的规范,此时oImgSrc就相当于defaule之后的结果
oImg.src = oImgSrc
oEle.appendChild(oImg)
// 03 设置背景图片
const oBgImg = document.createElement('div')
oBgImg.className = 'bgBox'
oEle.appendChild(oBgImg)
return oEle
}
document.body.appendChild(packImg())
设置图片名称与输出
...
module: {
rules: [
{
test: /\.(png|svg|gif|jpe?g)$/,
use: [
{
loader: 'file-loader',
options: {
// name: '[name].[hash:6].[ext]', // 设置图片名称
// outputPath: 'img' // 设置路径
name: 'img/[name].[hash:6].[ext]', // 设置路径与此路径下的图片名称
}
}
]
}
]
}
/**
* [ext]: 扩展名
* [name]: 文件名
* [hash]: 文件内容 + MD4的算法 生成128位的hash值
* [contentHash]:
* [hash:<length>]:length是当前需要保存的长度
* [path]:
*/
...
url-loader处理图片
安装 npm i url-loader -D
...
module: {
rules: [
{
test: /\.(png|svg|gif|jpe?g)$/,
use: [
{
loader: 'url-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
limit: 25 * 1024
}
}
]
}
]
}
/**
* 01 url-loader base64 uri 文件当中,减少请求次数
* 02 file-loader 将资源拷贝至指定的目录,分开请求
* 03 url-loader 内部其实也可以调用 file-loader
* 04 limit 如果图片的大小超过了limit的值,就会使用file-loader
*
*/
asset处理图片(webpack5内置)
我们可以使用aasset替换file-loader url-loader raw-loader
- asset module type
- 01 asset/resource -->file-loader( 输出路径 )
- 02 asset/inline --->url-loader(所有 data uri)base64的
- 03 asset/source --->raw-loader
- 04 asset (parser )
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]" // 相当于asset的全局的路径和名称的配置 不推荐
},
module: {
rules: [
*****asset/resource*****
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset/resource',
generator: {
filename: "img/[name].[hash:4][ext]"
}
},
*****asset/inline*****
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset/inline'
}
*****asset/resource*****
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset',
generator: {
filename: "img/[name].[hash:4][ext]"
},
parser: {
dataUrlCondition: {
maxSize: 30 * 1024 // 如果图片的大小超过了maxSize的值,就会像是file-loader
}
}
}
]
}
}
asset处理图标字体
module: {
...
rules: [
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
generator: {
filename: 'font/[name].[hash:3][ext]'
}
}
]
}
...
webpack插件使用
- 插件与loader
- loader: 对特定的模块类型进行转换(如以上几种),工作的时机:当我们要去识别或去读取这个文件内容的时候就进行工作
- plugin: 可以做更多的事情,webpack存在生命周期,工作的时机:插件可以在webpack生命周期的任一时机被插进来,插件其实也是一个类
举例常用插件clean-webpack-plugin
安装:npm i clean-webpack-plugin -D
const path = require('path')
// 第三方插件需要自己安装并引入
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
module: {...},
plugins: [
new CleanWebpackPlugin()
]
}
html-webpack-plugin插件使用
作用: 默认帮我们创建一个index.html,它会有一个自己内置的模板,也可以自己提供模板
安装:npm i html-webpack-plugin -D
const path = require('path')
// 第三方插件需要自己安装并引入
const path = require('path')
const { DefinePlugin } = require('webpack') // 自带的
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
module: {...},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-webpack-plugin',
template: './public/index.html' // 自己提供的模板,模板里使用到ejs语法写占位的内容
}),
new DefinePlugin({
BASE_URL: '"./"' // 定义下方常量BASE_URL的值
})
]
}
***********./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>
copy-webpack-plugin插件使用
作用:复制一些我们不需要打包的资源
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html'] // 需要忽略的 **/是从from定义的开始
}
}
]
})
babel
作用:将一下新的语法如es6的语法等等转换为浏览器可以识别的语法
npm i babel-loader -D
babel也有预设,设置了许多要转换的语法
// babel.config.js
module.exports = {
presets: ['@babel/preset-env']
}
babel
module: {
rules: [
// 方式一 多个插件
{
test:/\.js$/,
use: {
loader: 'babel-loader',
options: {
plugins: {
// 这里放对应插件
'xxx',
'xxx'
}
}
}
},
// 方式二 多个预设
{
test:/\.js$/,
use: {
loader: 'babel-loader',
options: {
// 这里放预设
presets: ['xxx','xxx']
}
}
}
// 方式三
{
test: /\.js$/,
use: ['babel-loader'] // 配置在babel.config.js
}
},
// babel.config.js
module.exports = {
presets: ['@babel/preset-env']
}
babel也是根据文件.browserslistrc里写的兼容范围去控制兼容性的
polyfill
webpack4默认有polyfill导致webpack4打包特别大,webpaack5基于优化打包的效率就移除了, ES6在Array对象上新增了Array.from方法,Promise等等更新的语法。Babel预设转换的语法有限,Babel就不会转码这个方法。如果想让这个方法运行,必须使用babel-polyfill,为当前环境提供一个填充。
// babel.config.js
module.exports = {
// presets: ['@babel/preset-env']
presets: [
[
'@babel/preset-env',
{
// false: 不对当前的JS处理做 polyfill 的填充
// usage: 依据用户源代码当中所使用到的新语法进行填充
// entry: 依据我们当前筛选出来的浏览器决定填充什么 也就是.browserslistrc此文件中筛选的
useBuiltIns: 'entry',
corejs: 3 // 使用的时候默认用的是版本2的,这里安装的是3版面,需要自己指定一下
}
]
]
}
// webpack.config.js
{
test:/\.js$/,
exclude: /node_modules/, // 使用这个的时候usage 可以加这行剔除node_modules,不用被我们的loader处理
use: ['babel-loader']
}
使用entry的时候需要在入口文件加import "core-js/stable";
webpack-dev-server
作用:开启一个热更新的本地服务,代码会运行到内存中
在package.json中的scripts配置使用
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"serve": "webpack serve"
},
HMR
作用:热模块替换
// indes.js
import './title.js'
if (module.hot) {
module.hot.accept(['./title.js'], () => {
console.log('title.js模块更新')
})
}
// title.js
console.log('title.js');
// webpack.config.js
...
devServer: {
hot: true
},
target: 'web', // 开发时可以配置这个,不用.browserslistrc
...