webpack:为现代JavaScript应用提供静态模块打包的工具
打包: 将不同类型资源按模块处理进行打包
静态:打包后最终产出静态资源
模块:webpack支持不同规范的模块化开发
有关模块化
es模块化
//导出 utils.js
const sum = (m, n) => m + n
const square = m => m * m
export { sum, square }
//导入 index.js
import { sum, square } from './js/utils.js'
在写有es模块化的js引入中,可以设置type="module",可以解决es模块化报错的问题
Cannot use import statement outside a module
<script src="./src/index.js" type="module"></script>
CommonJS
//导出 api.js
const getInfo = () => {
return {
name: 'kylin',
age: 20
}
}
module.exports = getInfo
//导入 index.js
const getInfo = require('./js/api.js')
在当前项目目录下执行
webpack命令对项目进行打包, 会在项目下生产一个dist目录,目录下main.js即为src下index.js(入口文件)打包后的结果。不再引入src下的index.js, 改为引入dist下的main.js,不会再报require的错:require is not defined。
package.json中修改webpack的入口和出口文件: webpack --entry ./src/main.js --output-path ./bulid
指定webpack的配置文件: webpack --config lg.webpack.js
webpack配置项
//webpack.config.js
const path = require('path')
module.exports = {
entry: './src/index.js', //相对路径
output: {
filename: 'build.js',
path: path.resolve(__dirname, 'dist') //绝对路径
}
}
css-loader: 只是让css文件能被处理,css语法能被识别
行内配置
//多个loader之间用!分割
import 'css-loader!../css/login.css'
配置文件中webpack.config.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
// {
// test: /\.css$/, //一般就是一个正则表达式,用来匹配我们需要处理的文件类型
// use: [
// {
// loader: 'css-loader'
// // options:
// }
// ]
// },
// 简写1
// {
// test: /\.css$/,
// loader: 'css-loader'
// },
// 简写2
{
test: /\.css$/,
use: ['css-loader']
}
]
}
}
style-loader: 解析css
loader处理的顺序是从右往左或 从下往上
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
}
less-loader
使用less-loader需要安装less才能编译less文件
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
browserslist package.json
到底兼容哪些平台 caniuse.com
"browserslist": [
">1%",
"last 2 version",
"not dead"
]
在src下新建.browserslistrc文件:
> 1%
last 2 version
not dead
postcss
autoprefixer.github.io: 自动做前缀兼容性的处理
postcss是JavaScript转换样式的工具
post-cli: 可以在命令行终端使用postcss
postcss-loader
//webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('autoprefixer')
]
}
}
}
]
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
postcss-preset-env
集合现代css转换时需要的插件
...
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
// plugins: [
// require('autoprefixer'), //已经包含在postcss-preset-env了
// require('postcss-preset-env') //可以直接写‘postcss-preset-env’
// ]
plugins: ['postcss-preset-env']
}
}
}
]
},
...
postcss.config.js 配置后在webpack.config.js中可以直接写'postcss-loader'
module.exports = {
plugins: [
require('postcss-preset-env')
]
}
importLoaders属性
代表当发现用import引入了新的css文件时,可以倒回执行loader的数量, 默认为0
module.exports = {
module: {
rules: [
{
test: /.css$/i,
use: [
"style-loader",
{
loader: "css-loader",
options: {
importLoaders: 2,
// 0 => no loaders (default);
// 1 => postcss-loader;
// 2 => postcss-loader, sass-loader
},
},
"postcss-loader",
"sass-loader",
],
},
],
},
};
file-loader
打包图片
- img src
- 使用require导入图片,此时如果不配置esModule: false, 则需
.default导出 - 也可以在配置中设置
esModule: false - 采用 import xxx from 图片资源,此时可以直接使用 xxx
- 使用require导入图片,此时如果不配置esModule: false, 则需
- background url
- 在css-loader中配置esMOdule: false
/* img.css */
.bgBox{
width: 400px;
height: 400px;
border: 1px solid slateblue;
background: url('../img/01.png'); /* 在css-loader中配置esMOdule: false */
}
//Image.js
import oImgSrc from '../img/pkq.png'
import '../css/img.css'
function packImg() {
const oEle = document.createElement('div')
const oImg = document.createElement('img')
oImg.width = 200
// oImg.src = require('../img/pkq.png').default //方法1
// oImg.src = require('../img/pkq.png') // 方法2 设置esModule
oImg.src = oImgSrc //方法3
oEle.appendChild(oImg)
const oBgImg = document.createElement('div')
oBgImg.className = 'bgBox'
oEle.appendChild(oBgImg)
return oEle
}
document.body.appendChild(packImg())
//webpack.config.js
...
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'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 //不转为es module
// }
// }
'file-loader'
]
}
]
}
...
file-loader配置文件名称和路径
// [ext]: 扩展名
// [name]: 文件名
// [hash]: 文件内容
// [contentHash]:
// [hash:<length>]
// [path]
...
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
// outputpath: 'img'
}
}
...
url-loader
- url-loader以base64 uri的形式展示图片,不会复制图片到dist下。可以减少请求次数。
- file-loader是将资源拷贝到指定的目录,分开请求。
- url-loader内部也可以调用file-loader
- limit:25 * 1024 (设置在url-loader的options中)
asset
处理图片
- asset/resource => file-loader: 拷贝图片
- asset/inline => url-loader:base64 uri
- asset/source =>row-loader
- asset asset/resource在output中配置:统一配置
// ext: 自带.
assetModuleFilename: "img/[name].[hash:4][ext]"
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset/resource',
generator: {
filename: "img/[name].[hash:4][ext]"
}
}
{
test: /\.(png|svg|gif|jpe?g)$/,
type: 'asset/inline',
}
{
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]"
}
}
webpack插件
clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
...
module: {},
plugins: [
new CleanWebpackPlugin()
]
...
html-webpack-plugin
自动生成html
- title
- template: 自定义模板
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({
title: 'HtmlWebpackPlugin',
template: './public/index.html'
})
]
DefinePlugin
webpack自带的插件:配置常量
const { DefinePlugin } = require('webpack')
plugins: [
new DefinePlugin({
BASE_URL: '"./"'
})
]
babel
需要在webpack.config.js中设置:mode: 'development'看效果
- @babel/core
- @babel/cli
- @babel/plugin-transform-arrow-functions
- @babel/plugin-transform-block-scoping
- @babel/preset-env 集合
babel-loader
相关的配置文件
- babel.config.js(json cjs mjs)
- babelrc.json(js)
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
]
}
module.exports = {
presets: ['@babel/preset-env']
}
@babel/polyfill
不建议直接安装这个库
- core-js
- regenerator-runtime
module.exports = {
presets: [
[
'@babel/preset-env',
{
//false: 不对当前js处理做polyfill的填充
//usage: 依据用户源代码当中所使用的新语法进行填充
//entry: 根据筛选出的浏览器决定填充什么
useBuiltIns: 'usage',
corejs: 3
}
]
]
}
copy-webpack-plugin
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
})
webpack-dev-server
实现热更新
//webpack.config.js
target: 'web',
devServer: {
hot: true
}
//index.js
if (module.hot) {
module.hot.accept(['./title.js'], () => {
console.log('title.js模块更新')
})
}
react实现热更新
- @pmmmwh/react-refresh-webpack-plugin
- react-refresh
//webpack.config.js
const reactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
target: 'web',
devServer: {
hot: true
},
module: {
rules: [
{
test: /\.jsx?$/,
use: ['babel-loader']
}
]
},
plugins: [
new htmlWebpackPlugin({
title: 'Hello webpack',
template: './index.html'
}),
new reactRefreshWebpackPlugin()
],
//babel.config.js
module.exports = {
presets: [
["@babel/preset-env"],
["@babel/preset-react"],
],
plugins: [
['react-refresh/babel']
]
}
vue实现热更新
- vue-template-compiler 解析vue文件
- vue-loader
vue-loader@14
//webpack.config.js vue-loader@14
{
test: /\.vue$/,
use: ['vue-loader']
}
vue-loader@15
该版本需要手动加载插件
const VueLoaderPlugin = require('vue-loader/lib/plugin')
...
{
test: /\.vue$/,
use: ['vue-loader']
}
...
plugins: [
new htmlWebpackPlugin({
title: 'Hello webpack',
template: './index.html'
}),//在build目录下自动生成index.html,指定其title
new reactRefreshWebpackPlugin(),
new VueLoaderPlugin()
],
vue-loader@16 -> vue3
output
+filename:输出的文件名
- path:输出文件路径
- publicPath: index.html内部的引用路径
- 域名 + publicPath + filename
- 本地用'./', 服务用 '/'
devServer
- publicPath:指定本地服务所在的目录
contenBase:打包后的资源引用的其他资源的路径(新版本已废弃)- hot: true/only
- port: 4000
- open: false 每次更新后是否自动打开浏览器
- historyApiFallback: true 解决路由刷新报错问题
proxy
//webpack.config.js
// http://localhost:4000/api/users
//https://api.github.com/users才有数据
devServer: {
proxy: {
'/api': {
target: 'https://api.github.com',
pathRewrite: {"^api": ""},
changeOrigin: true
}
}
}
resolve 模块解析规则
- extensions: ['.js', '.json'] 引入时默认可以省略的后缀名
- mainFiles: ['index'] 引入时默认可以省略的文件名
- modules: ['node-modules']
- alias
resolve: {
extensions: ['.js', '.json', '.ts', '.jsx', '.vue'],
alias: {
'@': path.resolve(ROOT_PATH, 'app')
}
},
devtool
- eval mode为development时devtool的默认值
- false
- source-map:打包时会对应生成.map文件,跟源代码做对应关系,方便错误调试 vue
- cheap-module-source-map: react
source-map 映射
- 在调试时可以定位到源代码中的信息
ts-loader
- 全局安装ts
- tsc --init : 在当前目录生成tsconfig.json文件
- babel-loader编译ts
- @babel/preset-typescript
//webpack.config.js
{
test: /\.ts$/,
use: ['babel-loader']
}
module.exports = {
//babel.config.js
presets: [
["@babel/preset-env", {
useBuiltIns: 'usage',
corejs: 3
}],
["@babel/preset-typescript"]
],
}
合并生成环境配置
- process.cwd : 获取项目根目录
- webpack --env prodction 传参
- const isProduction = env.production 判断环境
- const config = isProduction ? prodConfig : devConfig
- const mergeConfig = merge(commonConfig, config)