webpack
2024-03-10 22:07:24
浏览量: 1003
概述
- 官网 https://www.webpackjs.com/concepts/
- 安装
- npm i webpack webpack-cli -D
- webpack-cli(此工具用于在命令行中运行 webpack):
- 编译
- npx webpack 入口文件 --mode-编译模式(开发/生产)
- npx webpack --config 指定配置文件
- webpack本身只识别js资源,需要通过loader对css、图片等资源进行预处理
配置
- entry
- 入口
- output 出口
- path 路径
- filename 文件名
- loader 加载器
- 对文件进行预处理
- 用于识别webpack不能识别的文件
- plugins 插件
- 用于扩展webpack的功能
- node 模式
- 'none' | 'development' | 'production'
import path = require('path')
module.exports = {
entry: '',
// entry: { app: '', main: '' } //多入口打包
output: {
path: path.resolve(__dirname, 'dest'),
filename: 'main.js',
// filename: '[name].js',//根据入口文件的名字生成打包后的文件名
clean: true//清空上次打包内容
// filename: 'js/main.js'//指定js文件目录
//chunkFilename:'js/[name].js'//输出的其他文件的命名规则
//chunkFilename:'js/[name][contenthash:10].js'//contenthash根据文件内容生成hash值
// assetModuleFilename: 'static/[hash:10][ext][query]'//静态资源(type: 'asset/*')统一的命名规则
},
module: {//loader
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],//执行顺序从右到左,从下到上
},
{
test: /\.less$/i,
use: ['style-loader', 'css-loader', 'less-loader'],//执行顺序从右到左,从下到上
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
// generator: {
// filename: 'static/[hash][ext][query]'//自定义输出文件路径和名字
// }
//生成Base64格式
// type: 'asset',
// parser: {
// dataUrlCondition: {
// maxSize: 4 * 1024 // 4kb
// }
// }
},
{
test: /\.(woff|woff2|eot|ttf|otf|mp3|mp4)$/i,
type: 'asset/resource',
},
],
},
plugins: [
// new HtmlWebpackPlugin()
],
mode: 'development',
resolve: {//设置模块如何被解析
// alias: {},//路径别名配置
extensions: ['.js', '.json', '.wasm'],
},
// performance:false//关闭打包时的性能分析
}
- 打包编译时启用eslint检测
- npm install eslint-webpack-plugin eslint --save-dev
- .eslintrc.js 配置文件
- .eslintignore 忽略检查配置
- 配合vscode插件ESlint可在编写代码时进行错误提示
const ESLintPlugin = require('eslint-webpack-plugin');
module.exports = {
// ...
plugins: [
new ESLintPlugin({
context: path.resolve(__dirname, 'src')//指定检测文件目录
})
],
// ...
};
- 在webpack中使用Babel
- 将es6+的高级语法向后做兼容处理
- npm install -D babel-loader @babel/core @babel/preset-env
- babel-config.js 配置文件
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
// options: {
// presets: ['@babel/preset-env']//智能预设处理es6新语法
// plugins: ['@babel/plugin-transform-runtime']//Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。你可以引入 Babel runtime 作为一个独立模块,来避免重复引入。
// }
}
}
]
}
- 使用html插件
- npm install --save-dev html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html'),//使用模板文件,需要自定义生成后的html时必须使用模板
})
],
};
- 使用devServer
- 开启本地服务器,实时监听文件变化,实时编译
- 安装:npm i webpack-dev-server -D
- 启动命令:npx webpack serve
- 开发模式下会将打包内容输出到内存,不会生成具体文件
const path = require('path');
module.exports = {
//...
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,//启用 gzip 压缩
port: 9000,
open:true,//是否自动打开
historyApiFallback: true,//解决history路径刷新404的问题
},
};
- 开发模式和生产模式分离
import path = require('path')
//开发模式
module.exports = {
entry: './src/main.js',
output: {
path: undefined,//devServer开发模式下无具体文件输出所以不需要路径
filename: 'main.js',
},
module: {//loader
rules: [],
},
plugins: [],
mode: 'development',
}
//生产模式
module.exports = {
entry: '',
output: {
path: path.resolve(__dirname, '../dest'),//如果配置文件不在主目录下,拼接绝对路径时需要回退到主目录再拼接
filename: 'main.js',
},
module: {//loader
rules: [],
},
plugins: [],
mode: 'production',
}
sourceMap
- SourceMap是一种源文件映射技术。当项目运行后,如果出现错误,错误信息只能定位到打包后文件中错误的位置。如果想查看在源文件中错误的位置,则需要使用映射关系,找到对应的位置。
- 它会生成一个xxx.map映射文件,保存的是编译后文件和源文件的一一映射关系
- devtool
- 控制是否生成,以及如何生成 source map。
- 默认:none
- 使用 SourceMapDevToolPlugin 可以进行更细粒度的配置。
- 使用 source-map-loader 可以对已有的 source map进行特殊处理。
// sourceMap配置
module.exports = {
// ...
mode: 'development',
devtool: 'cheap-module-source-map'//开发常用模式
// mode: 'production',
// devtool: 'source-map'//生成常用模式
}
loader 配置
- Rule.oneOf
- 规则数组,当规则匹配时,只使用第一个匹配到规则。
module.exports = {
//...
module: {
rules: [
{
oneOf: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.less$/i,
use: ['style-loader', 'css-loader', 'less-loader'],
},
],
},
],
},
};
- Rule.exclude
- 排除所有符合条件的模块
- Rule.include
- 引入指定的模块
- cache
- 缓存生成的 webpack 模块和 chunk,来改善二次构建速度
//loader中使用cache
{
module: {
rules: [
{
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false//默认值为 true。当设置此值时,会使用 Gzip 压缩每个 Babel transform 输出。如果你想要退出缓存压缩,将它设置为 false -- 如果你的项目中有数千个文件需要压缩转译,那么设置此选项可能会从中收益。
}
}
}
]
}
}
//plugin中使用缓存
module.exports = {
// ...
plugins: [
new ESLintPlugin({
cache: true,
cacheLocation: path.resolve(__dirname, '.test_cache'),//指定缓存位置/文件
})
],
// ...
};
热模块更新
- 可以使用插件:HotModuleReplacementPlugin
- 热模块替换,提高打包效率
- 修改一个模块的代码时,不再重新打包所有文件,只打包被修改的文件,其他的走缓存
- 不能被用在生产环境
- 也可以在devServer中直接配置
// webpack.config.js
module.exports = {
//...
devServer: {
hot: true,//也可以使用devServer的hot配置开启热模块更新,但是这个默认不支持js的热更新,需要主动在js中使用module.hot.accept函数处理
},
};
//入口文件,使用module.hot.accept函数处理js热更新,实际开发中应使用loader来解决js热更新问题,如:vue-loader、react-hot-loader
if (module.hot) {
module.hot.accept('./library.js', function() {
// 对更新过的 library 模块做些事情...
});
}
多进程多文件打包
- webpack 可以利用电脑多cpu的特点,开启多进程进行打包
- 每个 进行worker 都是一个独立的 node.js 进程,其开销大约为 600ms 左右,同时会限制跨进程的数据交换,文件较少时反而降低打包速度,请仅在耗时的操作中使用
- 在很多loader或plugin中都有多进程配置选项,如:
- thread-loader
- TerserWebpackPlugin
- ESLintPlugin
tree shaking
- 移除js中没有使用的代码
- 使用es module模块化的代码才有效
- webpack5生产环境下已经开启了tree shaking
代码分隔 code split
- 多入口多输出
- 使用Optimization.splitChunks配置,会根据你选择的 mode 来执行不同的优化
- 公共模块单独打包
- 源代码一般称Chunks,打包后的代码一般称bundle
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',//all async initial
//其他都用默认配置就行
//使用all后,所有的modules中的第三方插件和使用了动态引入语法的js都会被打包成单独文件
// cacheGroups:{}//自定义打包分隔规则
},
// runtimeChunk: {//把文件之间的依赖关系,生成单独文件进行管理,某个文件变化时只更新当前文件和依赖关系中的文件名
// name: (entrypoint) => `runtime~${entrypoint.name}`,
// },
// minimize:true//是否需要压缩
// minimizer:[]//自定义压缩覆盖默认压缩工具(minimizer)。
},
};
按需加载
- 动态导入
document.body.onclick = function(){
//webpack遇到动态导入的语法时,会按需加载,比如这里会把count.js单独打包,且只有在点击时才加载到页面。vue中路由的按需加载就是基于这个原理做的
import(/* webpackChunkName: 'count' */'./count.js').then(res=>{
console.log(res.default)
// /* webpackChunkName: 'count' */ webpack魔法命名,需要配合webpack配置中output.chunkFilename才有效
}).catch(err=>{
console.log(err)
})
}
//eslint默认不支持import动态引入语法,需要在.eslintrc.js配置文件中添加以下配置
{
// ...
plugins: ['import ']
//...
}
预加载
- Preload 是一种浏览器机制,告诉浏览器立即加载资源,只加载当前页面修养的资源,兼容性较好
- Prefetch 是一种浏览器机制,告诉浏览器在空闲时才开始加载资源,可以加载其他页面需要的资源,兼容性较差
- webpack常用插件preload-webpack-plugin
{
plugins: [
new HtmlWebpackPlugin(),
new PreloadWebpackPlugin({
rel: 'preload',
as: 'script'
})// 打包后的加载标签 <link rel="preload" as="script" href="chunk.31132ae6680e598f8879.js">
// new PreloadWebpackPlugin({
// rel: 'prefetch',
// })// 打包后的加载标签 <link rel="prefetch" href="chunk.31132ae6680e598f8879.js">
]
}
core.js
- core-js 它是JavaScript标准库的 polyfill(垫片/补丁),将新功能的es'api'转换为大部分现代浏览器都可以支持 运行的一个'api'补丁包集合。
// babel.config.json
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",//按需加载,自动引入补丁
"corejs": "3.22"//指定corejs版本
}
]
// 'react-app' //babel-reset-react-app官方预设处理
]
}
loader推荐
- postcss-loader
- 预处理css,解决css兼容问题
- thread-loader
- 每个 进行worker 都是一个独立的 node.js 进程,其开销大约为 600ms 左右。同时会限制跨进程的数据交换。请仅在耗时的操作中使用此 loader!
- vue-loader
- 将解析vue文件,提取每个语言块,如有必要,将它们通过其他加载器进行管道传输,最后将它们组装回ES 模块,其默认导出为 Vue.js 组件选项对象
- vue-template-compiler
- 编译vue模板的包,传入模板返回AST抽象语法树。
- vue-style-loader
- 处理vue文件中的样式
插件推荐
- MiniCssExtractPlugin
- 本插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。提高加载效率
- CssMinimizerWebpackPlugin
- 这个插件使用 cssnano 优化和压缩 CSS。
- 就像 optimize-css-assets-webpack-plugin 一样,但在 source maps 和 assets 中使用查询字符串会更加准确,支持缓存和并发模式下运行
- html和js在生产模式下默认就已经压缩了,不再需要额外配置
- TerserWebpackPlugin
- 该插件使用 terser 来压缩 JavaScript。
- ImageMinimizerWebpackPlugin
- 图片压缩插件
- CopyWebpackPlugin
- 静态资源复制
- DefinePlugin
- webpack内置插件
- 允许在 编译时 将你代码中的变量替换为其他值或表达式。就是定义全局常量
PWA
- PWA 全称Progressive Web Apps(渐进式Web应用程序),旨在使用现有的web技术提供用户更优的使用体验,即使在离线的情况下也能正常访问页面。PWA背后不是一种新的技术,而是集合当前多种web技术的一种集合。分别利用各自的功能来完成渐进式的整体需求。
- webpack中的渐进式网络应用程序
- 添加 Workbox:npm install workbox-webpack-plugin --save-dev
//webpack.config.js添加配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js',
},
plugins: [
new HtmlWebpackPlugin({
title: 'Output Management',
title: 'Progressive Web Application',
}),
new WorkboxPlugin.GenerateSW({
// 这些选项帮助快速启用 ServiceWorkers
// 不允许遗留任何“旧的” ServiceWorkers
clientsClaim: true,
skipWaiting: true,
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
//入口文件 注册 Service Worker
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);
});
});
}
loader
- loader就是一个函数,webpack在解析时,会调用配置相关的loader,并把文件内容作为参数传给loader函数,loader函数对内容处理后返回新内容,用于处理webpack默认不能识别的文件类型(如:css)
- loader参数
- function(content, map, meta){}
- content
- 文件内容
- map
- source map
- meta
- 其他loader传递的参数
- 种类
- pre前置,normal普通,inline内联,post后置
- 所有的loader的执行顺序都有两个阶段:pitching和normal阶段,类似于js中的事件冒泡、捕获阶段
- Pitching阶段: post > inline > normal > pre
- Normal阶段:pre > normal > inline > post
- 同一阶段,相同的loader执行顺序
- 从右到左,从下到上
// 使用inline内联loader,多个loader用!分隔
import Styles from 'style-loader!css-loader?modules!./styles.css'
//内联loader三种前缀的使用
//! 跳过normal
import Styles from '!style-loader!css-loader?modules!./styles.css'
//-! 跳过 pre 和 normal loader
import Styles from '-!style-loader!css-loader?modules!./styles.css'
//!! 跳过 pre、 normal 和 post loader
import Styles from '!!style-loader!css-loader?modules!./styles.css'
- 定义loader的方式
//同步方式
module.exports = function(content, map, meta){
return content;//直接返回
}
module.exports = function(content, map, meta){
this.callback(null, content, map, meta);//调用callback交给给下一个loader继续处理;第一个参数可以传错误信息
}
//异步方式
module.exports = function(content, map, meta){
const callback = this.async();
settimeout(()=>{
callback(null, content, map, meta)
},1000)
}
//Raw loader 处理文件、图片、图标等数据
module.exports = function(content, map, meta){
return content;//得到的content是Buffer数据
}
module.exports.raw = true
//Pitching loader
module.exports = function(content, map, meta){
return content;
}
module.exports.pitch = function(){
console.log('pitch')//pitch函数会优先执行,所有loader等pich函数执行完后才执行普通loader函数
// return 'res'//如果有返回值,则后续loader函数包括自身的普通函数将都不再执行,称为熔断机制
}
loader 常用api
- this.async()
- 告诉 loader-runner 这个 loader 将会异步地回调。返回 this.callback。
- this.callback()
- 交给给下一个loader继续处理
- this.getOptions(schema)
- 提取给定的 loader 选项,接受一个可选的 JSON schema 验证规则作为参数
- JSON Schema指的是数据交换中的一种虚拟的“合同”。
- 约束JSON对象的规则,如:定义可以使用哪些关键字,类型是什么,是否可以自定义内容
- this.emitFile()
- 向外发送一个文件
- this.utils
- this.utils.contextify(): 返回一个相对路径
- this.utils.absolutify(): 返回一个绝对路径
plugins 插件
- 用于扩展webpack,加入自定义的构建行为
- webpack在构建时,会触发一系列的钩子函数,我们可以在这些钩子函数中注册自定义事件,实现特定的功能
- 生命周期
/**
* webpack
* compiler
* compiler.run()
* compiler.compilation()
* compiler.make() -> compilation
* compilation.buildModule()
* compilation.seal()
* compilation.optimize()
* compilation.reviveChunks()
* compilation.unseal()
* compiler.afterCompile()
* compiler.emit()
* compiler.emitAssets()//最后将资源输出出去
*/
- 在 webpack 中的许多钩子对象都扩展自 Tapable 类。
- tapable 是一个类似于 Node.js 中的 EventEmitter的库,但更专注于自定义事件的触发和处理。webpack 通过 tapable 将实现与流程解耦,所有具体实现通过插件的形式存在。
- tapable 暴露了三个方法用于注册不同类型的行为
- tap
- tapAsync
- tapPromise
- compiler 钩子
- Compiler 模块是 webpack 的主要引擎,保存了webpack所有的环境配置,compilation 实例。 它扩展(extends)自 Tapable 类,用来注册和调用插件。 大多数面向用户的插件会首先在 Compiler 上注册。
- 当webpack打包时,首先会创建Compiler实例对象,而且仅创建一个
- compilation 钩子
- Compilation 模块会被 Compiler 用来创建新的 compilation 对象(或新的 build 对象)。 compilation 实例能够访问所有的模块和它们的依赖(大部分是循环依赖)。
- 一个compilation对象代表一个资源的构建,它会对应用程序的依赖图中所有模块, 进行字面上的编译(literal compilation)。 在编译阶段,模块会被加载(load)、封存(seal)、优化(optimize)、 分块(chunk)、哈希(hash)和重新创建(restore)。
/**
* webpack加载配置时,在使用插件时通常会实例化对象,如:new TestPlugin()
* webpack编译前会先创建Compiler实例对象
* 在遍历插件配置时,会调用插件的apply方法
* 接着触发钩子函数
*/
class TestPlugin {
constructor(){}
apply(compiler){
// cnosole.log(compiler)
compiler.hooks.entryOption.tap('MyPlugin', (context, entry) => {
console.log(entry)
});
}
}
module.exports = TestPlugin
- nodejs调试webpack
- node:通过node启动
- inspect:调试模式
- brk:在首行打一个断点,调试代码会在首行停住
- ./node_modules/webpack/bin/webpack.js:调试对象及其路径
// 在xxx.js中设置 debugger,当点击下一步,会在这里停住
class TestPlugin {
constructor(){}
apply(compiler){
debugger;
cnosole.log(compiler)
}
}
// packge.json
{
"script": {
"debug": "node --inspect-brk ./node_modules/webpack/bin/webpack.js"
}
}
// 运行指令:npm debug
// 此时在浏览器(如:webpack官网)F12可以查看到控制台多个node绿色按钮
// 点击绿色按钮,进入到调试工具,前面指令设置了brk,这里代码停留在第一行
// 点击下一步,代码会停留在debugger处
推荐文章
-
2024-02-16 00:13:32 浏览量: 1002
-
2024-02-16 03:22:29 浏览量: 1002
-
2024-02-16 03:49:05 浏览量: 1002
-
2024-02-16 00:04:14 浏览量: 1002
-
2024-02-16 04:02:23 浏览量: 1002
-
2024-02-16 03:09:12 浏览量: 1002
-
2024-02-15 21:59:48 浏览量: 1007
-
2024-02-16 02:42:35 浏览量: 1002
-
2024-02-16 04:15:41 浏览量: 1002
-
2024-02-15 23:36:06 浏览量: 1009
-
2024-02-15 22:58:39 浏览量: 1008
-
2024-02-16 00:15:03 浏览量: 1009
-
2024-03-06 13:20:00 浏览量: 1023
-
2024-03-06 06:21:13 浏览量: 1019
-
2024-02-18 04:56:39 浏览量: 1014
-
2024-03-06 15:35:55 浏览量: 1103
-
2024-02-07 03:15:48 浏览量: 1007
-
2024-02-08 17:17:29 浏览量: 1006
-
2024-02-16 14:02:15 浏览量: 1007
-
2024-02-23 17:39:12 浏览量: 1011