why webpack
现代web开发的问题
- 采用模块化开发
- 使用新特性提供效率
- 开发过程中实时监听,热更新
- 编码完成打包压缩优化
webpack 功能
安装webpack和webpack-cli
- 打包:将不同类型资源按模块处理进行打包
- 静态:打包后最终产出静态资源
- 模块:webpack支持不同规范的模块化开发
- webpack会默认去项目下的src找index.js,然后对index.js里面的资源进行查找,进行统一打包(ESModule或者commonJS规范的模块都会统一处理),并在项目下产出一个dist目录,生成一个打包好的js文件
- “build”: “webpack --config lg.webpack.js” 通过–config可以自定义修改webpack.config.js
- webpack默认只会读取src/index.js中已经引用了的文件或模块,进行打包
为什么需要loader
- 处理非js文件就需要loader
loader是什么
- loader也是一个模块里面是js代码逻辑,能够将对应类型的文件转换成webpack能够处理的文件
css-loader
- css-loader只是将css代码处理为当前js可以识别的模块类型,里面可能存在@import/background/url等操作,css-loader更多的是处理这些,并不能将样式放在页面上使用想要在页面看到样式效果要么使用style属性,或style标签,要么通过link链接,而css-loader无法做到这些,需要另外的loader(style-loader)
-1.安装css-loader npm i css-loader -D
-2.方式一:通过行内使用(在前面加上css-loader前缀) import ‘css-loader!../css/login.css’
-3.方式二:通过配置文件配置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: [
// {
// loader: 'css-loader'
// }
// ]
// },
// {
// test: /\.css$/,
// loader: 'css-loader'
// },
{
test: /\.css$/,
use: ['css-loader']
}
]
}
}
style-loader
- npm i style-loader -D
- 注意在配置文件中loader的使用顺序; 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
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']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
}
}
browserslistrc
如何实现兼容
- 1.js ⇒ babel
- 2.css ⇒Autoprefixer/postcss-preset-env
兼容哪些平台
- 3.从caniuse.com获取
配置方法一:
- 在package.json里添加配置
"browserslist:[">1%,"last 2 version","not dead"]"
配置方法二:
- 新建.browserslist文件
> 1%
last 2 version
not dead
postcss
1.postcss: js转换样式的工具
2.postcss在css-loader之前进行工作(进行兼容性处理)
module.exports = {
plugins: [
require('postcss-preset-env')
]
}
importLoaders
- css-loader里面的options添加importLoaders属性,1表示css-loader工作时,又找到了css需要进行处理,就往前找一个;0表示和没写一样
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',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
}
}
file-loader处理图片
import oImgSrc from '../img/01.wb.png'
import '../css/img.css'
function packImg() {
// 01 创建一个容器元素
const oEle = document.createElement('div')
// 02 创建 img 标签,设置 src 属性
const oImg = document.createElement('img')
oImg.width = 600
// oImg.src = require('../img/01.wb.png').default
// oImg.src = require('../img/01.wb.png') require不用.default时需要配置options:{esModule:false}
oImg.src = oImgSrc
oEle.appendChild(oImg)
// 03 设置背景图片
const oBgImg = document.createElement('div')
oBgImg.className = 'bgBox'
oEle.appendChild(oBgImg)
return oEle
}
document.body.appendChild(packImg())
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',
{
loader: 'css-loader',
options: {
importLoaders: 1,
esModule: false
// css-loader处理url('xxxxxx')时,会自动替换为require语
//法,require语法会导出一个esModule,require语法要配合.default,
//但是css中无法使用.default,所以要配置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: {
name: 'img/[name].[hash:6].[ext]',
// outputPath: 'img' (可简写为上面img/xxx的写法)
}
}
]
}
]
}
}
/**
* [ext]: 扩展名(原扩展名输出)
* [name]: 文件名(原文件名输出)
* [hash]: 文件内容
* [contentHash]:等同[hash]
* [hash:<length>]
* [path]:
*/
url-loader处理图片
1.file-loader
- 把图片的名称/路径返回,把要打包的图片资源拷贝到指定的目录,分开请求
2.url-loader
- 默认会把要打包的图片资源以base64 uri的方式加载到代码当中
3.优缺点
- url-loader相比file-loader能和减少请求次数,因为所有图片都放在一个js文件中
- url-loader内部其实也可以调用file-loader
- limit属性
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',
{
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: 'url-loader',
options: {
name: 'img/[name].[hash:6].[ext]',
limit: 25 * 1024 //大于25kb就拷贝,小于25就转成base64
}
}
]
}
]
}
}
/**
* 01 url-loader base64 uri 文件当中,减少请求次数
* 02 file-loader 将资源拷贝至指定的目录,分开请求
* 03 url-loader 内部其实也可以调用 file-loader
* 04 limit
*
*/
asset处理图片
webpack5 内置的资源处理模块
- asset/resource 拷贝目标资源到指定目录 等同于 file-loader
- asset/inline 把资源添加到代码中 等同于 url-loader
- asset/source
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// 全局只要是通过asset处理的资源都放到此目录下
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
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)$/,
// 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
}
}
}
]
}
}
/**
* 01 url-loader base64 uri 文件当中,减少请求次数
* 02 file-loader 将资源拷贝至指定的目录,分开请求
* 03 url-loader 内部其实也可以调用 file-loader
* 04 limit
*
*/
asset处理字体图标
webpack5之前在处理字体图标时,同样时按照静态资源处理,使用file-loader或url-loader处理,webpack5可以直接使用asset处理
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
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)$/,
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]'
}
}
]
}
}
/**
* 01 url-loader base64 uri 文件当中,减少请求次数
* 02 file-loader 将资源拷贝至指定的目录,分开请求
* 03 url-loader 内部其实也可以调用 file-loader
* 04 limit
*
*/
插件的使用
- loader :
1.功能:针对特定的文件类型的转换
2.当识别或读取文件内容时,就开始进行工作- plugin
1.可以实现更多东西
//和module并列
//每一个插件就是一个类,使用的时候new这个类
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins: [
new CleanWebpackPlugin()
]
html-webpack-plugin使用
自动在打包之后的目录中生成html文件
const path = require('path')
//用于识别html模板中的变量,如BASE_URL
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'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
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)$/,
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]'
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'html-webpack-plugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"' //注意写法
})
]
}
/**
* class MyPlugin{
* constructor(){}
* apply()
* }
*/
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
对于项目中不需要处理的文件需要copy到自定义的目录下(.ico文件,说明文档等等)
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist'),
// assetModuleFilename: "img/[name].[hash:4][ext]"
},
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)$/,
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]'
}
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"'
}),
new CopyWebpackPlugin({
patterns: [
{
//表示从public拷贝,to可以简写,简写了就和output输出目录一致
from: 'public',
//忽略public下的html文件,html文件不做拷贝,
// 上面HtmlWebpackPlugin已经拿html文件做模板了
globOptions: {
ignore: ['**/index.html']
}
}
]
})
]
}
babel
- 为什么需要babel
1.jsx语法 TS ES6等等 需要在浏览器平台直接使用,就需要转换;功能与postcss类似;处理js的兼容
注意
- 直接使用babel是不会转换的,无法进行兼容性处理,需要针对不同的内容安装特定的Babel插件
- 例如处理箭头函数需要安装plugin-transform-arrow-functions
npm i @babel/plugin-transform-arrow-functions -D- 处理const let需要安装plugin-transform-block-scoping
npm i @babel/plugin-transform-block-scoping -D- 为了防止每次都要安装对应的依赖插件,@babel/preset-env集成了一些常用的插件用于处理新语法, 不需要每次对应的去安装 npm i @babel/preset -env -D
- babel同样会根据.browserslistrc里面的内容进行符合条件的处理
babel-loader
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist')
},
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)$/,
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]'
}
},
//babel-loader不会对文件进行兼容处理,需要用到preset-env插件集合,在babel.config.js导出
{
test: /\.js$/,
use: ['babel-loader']
}
]
},
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.config.js
module.exports = {
presets: ['@babel/preset-env']
}
polyfill
- -----------为什么需要polyfill?-------
- webpack5之前是集成的,webpack5为了优化打包速度单独出去了
- 上面提到了preset-env预设插件,但是需要注意的是,预设插件只是预设的一些常见的(箭头函数、const关键字)用于处理兼容的插件;
如果遇到了像promise,generator生成器函数,symbol等更新的语法的时候preset-env就无法进行转换,这时就需要用到poyfill
module.exports = {
presets: [
[
'@babel/preset-env',
{
// false: 不对当前的JS处理做 polyfill 的填充(默认)
// usage: 依据用户源代码当中所使用到的新语法进行填充
// entry: 依据我们当前筛选出来的浏览器决定填充什么
useBuiltIns: 'entry',
corejs: 3
}
]
]
}
webpack-dev-server
- 启本地服务器
1.通过live server插件配合 --watch或者在配置文件通过命令watch:true
不足:
1.所有源代码都会重新编译
2.每次编译成功之后都需要进行文件读写
3. 依赖live server
4.不能局部刷新
"serve": "webpack serve --config lg.webpack.js"
webpack-dev-middleware
在本地开启一个服务,将webpack打包后的文件交给这个服务,然后浏览器直接访问
// server.js 开启服务
const express = require('express')
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
const app = express()
// 获取webpack配置文件
const config = require('./webpack.config.js')
//调用webpack函数得到compiler,webpack会执行配置文件进行打包
const compiler = webpack(config)
app.use(webpackDevMiddleware(compiler))
// 开启端口上的服务
app.listen(3000, () => {
console.log('服务运行在 3000端口上')
})
HMR hot module replacement模块热替换
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//会和.browserslistrc冲突,
//target:'web' 表示开发阶段忽略browserslistrc
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist')
},
target: 'web',
// 所有跟webpack-dev-server相关的配置卸载devServer里
devServer: {
hot: true //开启热更新
},
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)$/,
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]'
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ['babel-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"'
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
})
]
}
打包的入口文件src/index.js
import './title'
//表示给当前模块开启热更新
if (module.hot) {
//第一个参数表示要开启热更新的模块
module.hot.accept(['./title.js'], () => {
console.log('title.js模块更新')
})
}
react组件热更新
- 1.webpack识别react组件 用到babel-loader @babel/preset-env @babel/preset-react
- 2.支持js的热更新 hot:true 且target:‘web’
- 3 react组件热更新用到的插件
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist')
},
target: 'web',
devServer: {
hot: true
},
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)$/,
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]'
}
},
{
test: /\.jsx?$/,
use: ['babel-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"'
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
}),
new ReactRefreshWebpackPlugin()
]
}
module.exports = {
presets: [
['@babel/preset-env'],
['@babel/preset-react'],
],
plugins: [
['react-refresh/babel']
]
}
path、devServer
- output
output中的path是打包后的文件路径,publicPath是告诉index.html以什么样的规则去查找filename
域名端口+publicPath(默认为’‘)+path路径- devServer
publicPath:指定本地服务所在的目录
contentBase:我们打包之后的资源如果说依赖了其他的资源(没有被打包的),此时就告知去哪找
watchContentBase:没有被打包的内容发生修复也能监控到
hotOnly:当前界面存在多个状态,其中某一个状态在执行过程中发生改变(发生语法错误),如果不配置hotOnly的情况下根据报错位置修改好了以后,默认会对整个页面执行一次刷新操作
compress:开启服务端的gzip压缩
historyApiFallback:处理history模式 后端没有对应的资源情况- output里面的publicPath和devServer里的publicPath保持一致
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist')
},
target: 'web',
devServer: {
hot: true,
hotOnly: true,
port: 4000,
open: false,
compress: true,
historyApiFallback: true
},
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { DefinePlugin } = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
mode: 'development',
devtool: false,
entry: './src/index.js',
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/lg'
},
target: 'web',
devServer: {
hot: true,
publicPath: '/lg',
contentBase: path.resolve(__dirname, 'public'),
watchContentBase: true
},
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)$/,
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]'
}
},
{
test: /\.jsx?$/,
use: ['babel-loader']
},
{
test: /\.vue$/,
use: ['vue-loader']
}
]
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'copyWebpackPlugin',
template: './public/index.html'
}),
new DefinePlugin({
BASE_URL: '"./"'
}),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
}),
new VueLoaderPlugin()
]
}
proxy
devServer: {
hot: true,
hotOnly: true,
port: 4000,
open: false,
compress: true,
historyApiFallback: true,
proxy: {
// /api/users
// http://localhost:4000/api/users
// https://api.github.com/info/users
// /api/users---> 返回
//https://api.github.com/users 后端接口直接就是/users所以需要重写
//如果是https://api.github.com/info/users就需要重写为pathRewrite: { "^/api": "info" }
'/api': {
target: 'https://api.github.com',
pathRewrite: { "^/api": "" },
changeOrigin: true
}
}
},
/api 开头的接口请求都走代理设置
通过http://localhost:4000转发请求
代码懒加载
- 预获取
会在浏览器空闲的时候加载
const oBtn = document.createElement('button')
oBtn.innerHTML = '点击加载元素'
document.body.appendChild(oBtn)
// 按需加载
oBtn.addEventListener('click', () => {
import(
/*webpackChunkName:'utils' */
/*webpackPreFetch:true */
'./utils').then(({ default: element }) => {
console.log(element)
document.body.appendChild(element)
})
})
- 预加载
preLoad会在当前引用的页面加载的时候并行加载
const oBtn = document.createElement('button')
oBtn.innerHTML = '点击加载元素'
document.body.appendChild(oBtn)
// 按需加载
oBtn.addEventListener('click', () => {
import(
/*webpackChunkName:'utils' */
/*webpackPreFetch:true */
'./utils').then(({ default: element }) => {
console.log(element)
document.body.appendChild(element)
})
})
- splitChunks
optimization: {
minimizer: [
new TerserPlugin({
extractComments: false,
}),
],
splitChunks: {
chunks: 'initial', // async initial all
minSize: 20000, //达不到minSize就不拆
maxSize: 20000, //拆体积大于maxSize的 且拆出来的不能小于minSize
minChunks: 1, //至少被引用了一次才会被拆,跟minSize和maxSize不一起出现
cacheGroups: { //第三方的引用有很多的时候,某一个拆包好了放入缓存等其他的也好了再打包到一起
syVendors: {
test: /[\\/]node_modules[\\/]/,
filename: 'js/[id]_vendor.js', //写入的目录,打包的名称
priority: -10, // 谁大就走谁
},
default: {
minChunks: 2,
filename: 'js/syy_[id].js',
priority: -20,// 谁大就走谁
}
}
}
},
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require("terser-webpack-plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin') //css文件单独抽离
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") //css文件压缩插件
const PurgeCSSPlugin = require('purgecss-webpack-plugin')
const CompressionPlugin = require("compression-webpack-plugin")
const InlineChunkHtmlPlugin = require('inline-chunk-html-plugin')
const resolveApp = require('./paths')
const glob = require('glob')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'production',
optimization: {
usedExports: true, // 标记不被使用的函数
minimize: true,
minimizer: [
new TerserPlugin({
extractComments: false,
}),
new CssMinimizerPlugin()
]
},
plugins: [
new CleanWebpackPlugin(),
new CopyWebpackPlugin({
patterns: [
{
from: 'public',
globOptions: {
ignore: ['**/index.html']
}
}
]
}),
new MiniCssExtractPlugin({
filename: 'css/[name].css'
}),
// css tree-shaking
new PurgeCSSPlugin({
paths: glob.sync(`${resolveApp('./src')}/**/*`, { nodir: true }),
safelist: function () {
return {
standard: ['body', 'html', 'ef']
}
}
}),
//文件压缩
new CompressionPlugin({
test: /\.(css|js)$/, //只对css和js压缩
minRatio: 0.8,
threshold: 0,
algorithm: 'gzip'
}),
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime.*\.js/])
]
}