初始化
-1 yarn init 生成包描述文件(可跳过yarn init -y)
{
"name": "demo02",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
-2 创建入口文件 (项目根目录root/src/main.js)
// main.js
console.log('webpack')
-3 下载webpack包
yarn add webpack webpack-cli -D
-4 配置打包文件
root/config/webpack.dev.js(开发模式)
首次打包js
// webpack.dev.js
// 路径处理 (node路径模块)
const { resolve } = require('path');
/**
* @type{import('webpack').Configuration}
*/
module.exports = {
// 单文件入口配置
entry: resolve(__dirname, '../src/main.js'),
// 打包出口
output: {
// 打包文件名(带文件路径)
filename: 'js/[name].[contenthash:7].js',
// 打包输出位置
path: resolve(__dirname, './dist'),
// 是否清空打包文件
clean: true,
// 打包后基础路径
publicPath:'/'
},
// 打包模式
mode: 'development'
}
配置打包命令
// package.json
{...
"scripts":{
"build:dev":"npx webpack --config ./config/webpack.dev.js" // 开发模式打包
}
...
}
结果
root/dist/js/xxx.js
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./src/main.js":
/*!*********************!*\
!*** ./src/main.js ***!
\*********************/
/***/ (() => {
eval("console.log('webpack');\n\n//# sourceURL=webpack://demo02/./src/main.js?");
/***/ })
/******/ });
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module can't be inlined because the eval devtool is used.
/******/ var __webpack_exports__ = {};
/******/ __webpack_modules__["./src/main.js"]();
/******/
/******/ })()
;
// node mainxxxxx.js
D:\项目\前端案例\webpack高级\01-基础使用\demo02\dist\js>node main.af7ca76.js
webpack
-5 引入babel-loader处理代码兼容
yarn add babel-loader @babel/preset-env @babel/core -D
babel-loader 可以向下兼容js代码
@babel/preset-env 预设包
@babel/core 转码包
// config/webpack.dev.js 新的更改
module.exports = {
....
module: {
rules: [
// js 处理
{
test: /\.js$/,
includes: resolve(__dirname, '../src'),
use: [{
loader: 'babel-loader' // js代码兼容
}]
}
]
},
......
}
根目录创建babel配置文件
root/babel.config.js
// root/babel.config.js
module.exports = {
// 创建预设
presets:['@babel/preset-env']
}
eslint
用于校验代码可行性和格式,可以自定义
yarn add eslint eslint-webpack-plugin -D
-1 创建配置文件
// root/.eslintrc.js
module.exports = {
extends: ['eslint:recommended'], // 继承规则 有官方标准(此处的) , 还有react, vue的
env: {
node: true, // 启用node全局环境
browser: true, // 启用浏览器全局环境
es6: true // 允许Es6
},
// 解析配置
parserOptions: {
ecmaVersion: 6, // ES 语法版本
sourceType: "module", // ES 模块化
ecmaFeatures: { // ES 其他特性
jsx: true // 如果是 React 项目,就需要开启 jsx 语法
}
},
// 自定规则 可覆盖继承规则
rules: {
// 是否必须分号
semi: ['warn']
}
}
打包配置
// root/config/webpack.dev.js
// 路径处理 (node路径模块)
const { resolve } = require('path');
// eslintPlugin
const EslintWebPackPlugin = require('eslint-webpack-plugin')
/**
* @type{import('webpack').Configuration}
*/
module.exports = {
// 单文件入口配置
entry: resolve(__dirname, '../src/main.js'),
// 打包出口
output: {
// 打包文件名(带文件路径)
filename: 'js/[name].[contenthash:7].js',
// 打包输出位置
path: resolve(__dirname, '../dist'),
// 是否清空打包文件
clean: true
},
module: {
rules: [
// js 处理
{
test: /\.js$/,
include: resolve(__dirname, '../src'),
use: [{
loader: 'babel-loader'
}]
}
]
},
plugins: [
new EslintWebPackPlugin({
// 配置校验的文件
context: resolve(__dirname, '../src')
})
],
// 打包模式
mode: 'development'
}
处理非js资源
-1 html
yarn add html-webpack-plugin -D
html文件
<!DOCTYPE html>
<html lang="en">
<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">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
这是第一个文件
</body>
</html>
main.js
import demo1 from './utils/demo1';
demo1().then(res => {
console.log(res);
}).catch((err) => {
console.log(err);
});
console.log('webpack');
配置文件
// root/config/webpack...dev
// 路径处理 (node路径模块)
const { resolve } = require('path');
// eslintPlugin
const EslintWebPackPlugin = require('eslint-webpack-plugin')
// 处理html资源
const HtmlWebpackPlugin = require('html-webpack-plugin')
....
plugins: [
new EslintWebPackPlugin({
// 配置校验的文件
context: resolve(__dirname, '../src')
}),
new HtmlWebpackPlugin({
filename: 'index.html', //文件打包后名称
title: 'index', // htmltilte
inject: 'body', // js插入的位置
// favicon: 图标文件地址
// minify :true // 压缩html? 默认development不压缩, production压缩
// chunks: [resolve(__dirname, '../src/utils/demo1.js')], // html自身的chunk包(多文件)
template: resolve(__dirname, '../src/view/demo01.html') // 模板
})
],
......
}
打包后
// index.html
<!DOCTYPE html>
<html lang="en">
<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">
<title>index</title>
</head>
<body>
这是第一个文件
<script defer src="js/main.fc7cc0e.js"></script></body>
</html>
-2 css + less
yarn add css-loader -D -> 处理css文件
yarn add style-loader -D --> 将css文件转换为style文件写入js中
yarn add less -D --> less
yarn add less-loader -D -> 用于处理less文件
yarn add mini-css-extract-plugin -D --用于单独打包css文件
yarn add css-minimizer-webpack-plugin -D --压缩css资源
yarn add postcss -D --用于将css向下兼容
yarn add postcss-loader -D --处理postcss
yarn add postcss-preset-env -D --postcss预设
使用mini-css-extract-plugin时无需将css打包进js中所以无需使用style-loader
// 单独打包css --> 此时无需加载styleloader
const MiniCssExtarctPlugin = require('mini-css-extract-plugin')
// 压缩 css资源
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
········
{
test: /\.css$/,
use: [MiniCssExtarctPlugin.loader, 'css-loader', { // 压缩css中自带loader
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env'] // 解决大多数样式兼容问题
}
}
}]
},
// 解析less文件
{
test: /\.less$/,
use: [MiniCssExtarctPlugin.loader, "css-loader", {
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env']
}
}
}, "less-loader"],
},
······
plugin:[
····
new MiniCssExtarctPlugin({
// 文件位置及路径
filename: 'static/css/[name].[contenthash:6].css' // 独立css文件
}),
new CssMinimizerWebpackPlugin()// 压缩css
]
控制兼容性
package.json
"browserslist": [
"> 1%",
"last 2 version",
"not dead"
]
-3 图片&字体
webpack5之前需要下载url-loader\file-loader,而在5版本中内置了两个loader
····
// 处理图片资源(5版本以上)
{
test: /\.(jpg|jpeg|png)/,
type: 'asset', // 相当于url-loader
// 解析器
parser: {
dataUrlCondition: {
maxSize: 50 * 1024 // 大小小于50k则打包为base64
}
},
// 发生器
generator: {
filename: 'img/[name][hash:4][ext][query]'
}
},
// 处理字体图标
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',// file-loader
// 发生器
generator: {
filename: 'font/[name][hash:6][ext]'
}
}
// 5版本下
// {
// test: /\.(jpg|png|jpeg|gif|svg)/,
// use: ['url-loader'],
// options: {
// // 300 kb 以上会被打包base64编码
// limit: 300 * 1024,
// name: '/img/[name].[hash:5].[ext]'
// }
// },
// // 处理字体文件
// {
// test: /\.(ttf|woff2?)$/,
// use: ['file-loader']
// }
,
// 打包其他资源 如音视频
{
test: /\.(mp[34]|avi)$/,
type: 'asset/resource',
generator: {
filename: '/media/[name][hash:6][ext]'
}
},
····
问题: 在打包过程中发现html图片无法正常显示
yarn add html-loader -D --解析html中的url
{
test: /\.html$/,
use: {
loader: 'html-loader'
}
}
-4 ts资源
yarn add typescript ts-loader
···
// 打包ts资源
{
test: /\.ts$/,
exclude: /node_modules/,
include: resolve(__dirname, '../src'),
use: ['ts-loader']
},
resolve: {
alias: {
'@src': resolve(__dirname,'../src') // 路径别名
},
extensions: ['.ts', '.js', '.json] //因为路径上包含.ts, webpack是无法识别的,配置后可解决
},
···
webpack服务启动
yarn add webpack-dev-server -D --开启一个web服务 -- 可以搭建开发环境服务
yarn add serve -D --开启一个运行生产环境文件的服务
配置指令
package.json
"scripts":{
"build:dev": "npx webpack --config ./config/webpack.dev.js",
"dev": "npx webpack serve --config ./config/webpack.dev.js"
}
打包配置文件
module.exports ={
···
// 自启动热服务
devServer: {
host: 'localhost',
open: false,
port: 2000,
···
},
···
}
优化策略
-1 映射优化开发体验(报错后可以找到报错位置)
-
开发模式:
cheap-module-source-map- 优点:打包编译速度快,只包含行映射
- 缺点:没有列映射
// prodction
module.exports = {
····
devtool: "source-map"
····
}
-
生产模式:
source-map- 优点:包含行/列映射
- 缺点:打包编译速度更慢
// development
module.exports = {
····
devtool: "cheap-module-source-map"
····
}
-2 开启热模块更新
····
// 自启动热服务
devServer: {
host: 'localhost',
open: false,
port: 2000,
hot: true, // 热模块替换, 代码更新不用更新整个页面 ,但js文件变化不能更新 --- vue热替换可以解决
},
····
-3 oneOf匹配
使用oneOf后匹配loader不用循环每次匹配loadertest正则了
···
module: {
rules: [{
oneOf: [
// 解析css文件
{
test: /\.css$/,
use: ['style-loader', 'css-loader', {
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env']
}
}
}]
// css-loader --> 处理css资源
// style-loader --> 打包css资源到js中以<style>方式展示
},
// 解析less文件
{
test: /\.less$/,
use: ["style-loader", "css-loader", {
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['postcss-preset-env']
}
}
}, "less-loader"],
},
// 处理图片资源(5版本以上)
{
test: /\.(jpg|jpeg|png)/,
type: 'asset', // 相当于url-loader
// 解析器
parser: {
dataUrlCondition: {
maxSize: 50 * 1024
}
},
// 发生器
generator: {
filename: 'img/[name][contenthash:4][ext][query]'
}
},
// 处理字体图标
{
test: /\.(ttf|woff2?)$/,
type: 'asset/resource',
// 发生器
generator: {
filename: 'font/[name][contenthash:6][ext]'
}
}
// 5版本下
// {
// test: /\.(jpg|png|jpeg|gif|svg)/,
// use: ['url-loader'],
// options: {
// // 300 kb 以上会被打包base64编码
// limit: 300 * 1024,
// name: '/img/[name].[hash:5].[ext]'
// }
// },
// // 处理字体文件
// {
// test: /\.(ttf|woff2?)$/,
// use: ['file-loader']
// }
,
// 打包其他资源 如音视频
{
test: /\.(mp[34]|avi)$/,
type: 'asset/resource',
generator: {
filename: '/media/[name].[contenthash:6][ext]'
}
},
// 打包js资源
{
test: /\.js$/,
exclude: /node_modules/, // 排除包文件,不需要打包
include: resolve(__dirname, '../src'),
use: [{
loader: 'babel-loader'
}]
},
// 打包ts资源
{
test: /\.ts$/,
exclude: /node_modules/,
include: resolve(__dirname, '../src'),
use: ['ts-loader']
},
// 打包html图片资源
{
test: /\.(html|xml)/,
use: [{
loader: 'html-loader'
}]
}
]
}]
},
···
-4 eslint cache
缓存之前eslint编译结果
···
module:{
oneOf:{
rules:[
···
// 打包js资源
{
test: /\.js$/,
exclude: /node_modules/, // 排除包文件,不需要打包
include: resolve(__dirname, '../src'),
use: [{
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启babel编译缓存
cacheCompression: false, // 缓存文件不要压缩
}
}]
},
···
]
}
},
···
plugins:[
···
// 指定文件检查根目录
context: resolve(__dirname, '../src'),
cache: true, // 开启缓存
// 缓存目录
cacheLocation: resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
)
}),
···
]
···
-5 减少babel插入的辅助代码(减少打包文件体积)
yarn add @babel/plugin-transform-runtime -D
···
module:{
oneOf:{
rules:[
···
// 打包js资源
{
test: /\.js$/,
exclude: /node_modules/, // 排除包文件,不需要打包
include: resolve(__dirname, '../src'),
use: [{
loader: 'babel-loader',
options: {
plugins: ['@babel/plugin-transform-runtime'], //减少代码体积
cacheDirectory: true, // 开启babel编译缓存
cacheCompression: false, // 缓存文件不要压缩
}
}]
},
···
]
}
},
···
-6 更多
图片压缩
yarn add image-minimizer-webpack-plugin --有损压缩和无损压缩
代码分割(单)
optimization: {
splitChunks: {
chunks: 'all'
},
},
按需加载 js中导入需要动态加载的js 如 点击事件后的文件 操作后加载的文件
使用 import('../')导入方式
main.js
dom.addventLisetener('click', ()=>{
import('.../ks.js').then(()=>{
// 成功
}).catch(()=>{
// 失败
})
})
动态导入 Preload / Prefetch
yarn add @vue/preload-webpack-plugin -D
const PreloadWebpackPlugin = require("@vue/preload-webpack-plugin");
···
plugins:[
```
new PreloadWebpackPlugin({
rel: "preload", // preload兼容性更好
as: "script",
// rel: 'prefetch' // prefetch兼容性更差
}),
···
]
网络缓存
// 输出打包后的文件
output: {
//contenthash 用于解决网络缓存优化
// 我们希望静态资源缓存(文件名不变化可以触发),
// 而浏览器识别文件名称变化时不会读取缓存
// 为避免优化后导致文件内容更新触发浏览器读取缓存导致用户无法更新内容
// 这里就可以使用 contenthash -> 文件内容变化时文件名变化
filename: 'static/[name].[contenthash:6].js',
path: resolve(__dirname, '../build'),
// 自动清除上次打包文件
clean: true,
// 基础路径
publicPath: '/'
},
兼容js新属性
yarn add core-js
PWA 离线访问项目
yarn add workbox-webpack-plugin -D
const WorkboxPlugin = require("workbox-webpack-plugin");
···
plugins:[
···
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
···
]
main.js
if ("serviceWorker" in navigator) { //
window.addEventListener("load", () => {
navigator.serviceWorker
.register("/service-worker.js")
.then((registration) => {
console.log("SW registered: ", registration);
})
.catch((registrationError) => {
console.log("SW registration failed: ", registrationError);
});
});
}
配置环境变量
yarn add cross-env -D 提供控制设置获取生产、开发环境的Node
配置指令
main.js
console.log(process.env.NODE_ENV)
"scripts":{
"build:pro": "cross-env NODE_ENV=production npx webpack --config ./config/webpack.pro.js",
"build:dev": "cross-env NODE_ENV=development npx webpack --config ./config/webpack.dev.js",
"dev": "cross-env NODE_ENV=development npx webpack serve --config ./config/webpack.dev.js",
"distTest":'npx serve build'
}