plugin
原理
webpack插件是一个具有apply方法的JS对象,apply方法会被webpack compiler调用,并且在整个编译生命周期都可以访问compiler对象。
原理:通过在生命周期的钩子中挂载函数,来实现功能扩展。
生命周期就是整个生命过程中的关键节点。
自定义plugin
钩子是提前在可能增加功能的地方,埋好(预设)一个函数,生命周期的函数。
官网钩子40-50个,以下是几个关键钩子
钩子 | 描述 | 类型 |
---|---|---|
environment | 环境准备好 | SyncHook |
compile | 编译开始 | SyncHook |
compilation | 编译结束 | SyncHook |
emit | 打包资源到output之前 | SyncSeriesHook |
afterEmit | 打包资源到output之后 | SyncSeriesHook |
done | 打包完成 | SyncHook |
const pluginName = 'MyWebpackPlugin';
class MyWebpackPlugin {
// compiler 对象是 webpack 编译器对象
apply(compiler){
// hook中的tap函数的第一个参数是插件名称
compiler.hooks.run.tap(pluginName, compilation => {
// webpack配置应用了插件后就会执行到此处
console.log("webpack 构建过程开始!")
})
}
}
复制代码
plugin的特性
- 是一个独立的模块
- 模块对外暴露一个 js 函数
- 函数的原型 (prototype) 上定义了一个注入 compiler 对象的 apply 方法
- apply 函数中需要有通过 compiler 对象挂载的 webpack 事件钩子,钩子的回调中能拿到当前编译的 compilation 对象,如果是异步编译插件的话可以拿到回调 callback
- 完成自定义子编译流程并处理 complition 对象的内部数据
- 如果异步编译插件的话,数据处理完成后执行 callback 回调
loader
loader的特性
-
loader本质上是一个ESM模块,它导出一个函数,在函数中对打包资源进行转换
-
loader必须返回一段js代码,这样eval才能执行,否则灰报错
,多个loader,保证最后面一个返回js代码就行。
loader执行顺序
当配置多个loader时,loader的执行顺序时从右往左
,右边的执行结果作为参数传到左边。
less-loader把less转化成css,传给css-loader,css-loader把结果给style-loader,style-loader返回javascript代码字符串。
{
test:/\.less$/,
use:[
'style-loader','css-loader','less-loader'
]
}
复制代码
数组对象形式,这么写也可以说是默认从下到上
module: {
rules: [
{
test: /\.less$/,
use: 'style-loader'
},
{
test: /\.less$/,
use: 'css-loader'
},
{
test: /\.less$/,
use: 'less-loader'
}
]
}
复制代码
如何保证各个loader按照预想方式工作?
可以使用 enforce 强制执行 loader 的作用顺序
,pre 代表在所有正常 loader 之前执行,post 是所有 loader 之后执行。(inline 官方不推荐使用)
可以通过一个 enforce 属性,默认有以下几个值
- pre 优先处理
- normal 正常处理(默认)
- inline 其次处理
- post 最后处理
enforce的使用方法:
module: {
rules: [
{
test: /\.less$/,
use: 'less-loader',
enforce: 'pre'
},
{
test: /\.less$/,
use: 'css-loader'
},
{
test: /\.less$/,
use: 'style-loader',
enforce: 'post'
}
]
},
复制代码
自定义loader
loader-utils 可以获取loader的配置项option
比如:申明一个读取markdown文件内容的loader。
cosnt {getOptions} = require('loader-utils')
const marked = require('marked')
module.exports = function(source){
//获取loader配置选项
const options = getOptions(this)
//对输入内容进行处理
const html = marked(source)
//返回给下一个loader处理,如果不用给下一个loader处理需要保证返回js代码
return html
}
复制代码
比如 :css-loader 把css解析成webpack识别的模块
module.exports = function(source) {
return `module.exports=${source}`;
}
复制代码
- 当定义一个loader时,这个loader函数只有一个参数,参数是包含文件内容的字符串。
- 同步loader可以返回一个代表模块转化后的简单的值
- loader的返回值是javascript代码字符串或者是Buffer
小结
配置
- 条件匹配: 通过test、include、exclude三个配置来命中Loader要应用的规则文件。
- 应用规则: 对选中后的文件通过use配置项来应用loader,可以应用一个loader或者按照从后往前的顺序应用一组loader。同时还可以分别给loader传入参数。
- 重置顺序: 一组loader的执行顺序默认是从从右往左执行,通过exforce选项可以让其中一个loader的执行顺序放到最前或者是最后。
编写原则
- 单一职责。每个loader只负责一件事情。
- 使用链式调用,确保loader的依赖关系的正确。
- 无状态性,确保loader在不同模块转换之间不保存状态。每次运行都应该独立于其他编译模块以及相同模块之前的编译结果。
loader和plugin的区别
- 功能及特点
-
loader:对文件进行转换成webpack能够识别的文件,从右往左执行
-
plugin:插件,监听文件,在webpack打包的时候对文件进行处理,目的在于解决loader无法实现的其他事
-
loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。因为
Webpack 只认识JavaScript
,所以Loader就成了翻译官,对其他类型的资源进行转译的预处理工作。
loader描述了webpack如何处理非javascript模块,并且在build中引入这些依赖。loader可以将文件从不同的语言(如TypeScript)转换为JavaScript,或者将内联图像转换为data URL。比如说:CSS-Loader,Style-Loader等。
- plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,
在 Webpack 运行的生命周期中会广播出许多事件,plugin可以监听这些事件
,在合适的时机通过 Webpack 提供的 API 改变输出结果。
- 配置
- loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
- plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。
常用loader和plugin
打包CSS
- style-loader :把写入js的样式代码插入到 index.html文件中
- css-loader :仅仅只是把样式代码写入 js
- postcss-loader 前缀 postcss.config.js require(‘autoprefixer’)
- less-loader
- stylelint-webpack-plugin : css代码格式校验 还得安装stylelint stylelint-config-standard【大厂基本是使用的规范】
- optimize-css-assets-webpack-plugin /css-minimizer-webpack-plugin css压缩,不同组件中重复的css可以快速去重
- sass-loader:加载SASS / SCSS 文件并将其编译为 CSS
- mini-css-extract-plugin:打包css文件。之前打包的css是把css样式打包到js文件里面,使用这个插件可以把样式抽离成一个css文件;这个插件要同时使用他的loader和plugin才行
require('./index.css')
// 这个插件要同时使用他的loader和plugin才行
plugins:[
new MiniCssExtractPlugin({
filename: 'css/main.css'
}),
],
module: {
rules: [
{
test: /\.css$/,
loader: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
}
踩坑在于,把style-loader和miniCss.loader一起使用了,导致一直打包不成功,报错document is not found
复制代码
打包JS
- babel-loader babel-core @babel/preset-env
@babel/preset-env只能转译基本语法(promise就不能转换)
@babel/polyfill 转换所有js新语法 引入文件太大,增加垃圾代码
core-js 按需转译JS新语法,这个最好
- eslint-webpack-plugin JS代码格式校验 eslint eslint-config-airbnb-base eslint-webpack-import
打包图片
- file-loader :将要加载的文件复制到指定目录;生成请求文件资源URL,
是按需导入,文件中没有引入不会被处理
。它是将用到的图片复制到输出目录,过滤掉不用的图片。
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'file-loader',
query: {
name: '[name].[ext]',
outputPath: 'static/img/',
publicPath: '/dist/static/img/'
}
}
name指定文件名
outputPath指定文件的输出目录,此处应为相对于webpack输出目录的相对路径。
publicPath指定请求文件时的URL。如上面的配置,生成的URL为/dist/static/img/${name}
注意
outputPath, publicPath一定要以/结尾
复制代码
注:mini-css-extract-plugin 需要额外处理css中的图片路径(例如:背景图片加载失败),不然会加载失败,具体看MiniCssExtractPlugin.loader
-
url-loader:
file-loader的升级版
,如果图片小于配置大小,会转成base64字符串,减少图片的请求次数,如设置设置限制大小8192,也就是8M【使用url-loader,不能删除file-loader, file-loader是基本功能,url-loader是加强功能,这句话要进一步验证】 -
html-loader:将html导出为字符串。在导出的过程中,它可以把html中所有
跟资源有关的内容做处理,所以可以解决图片引入的问题
。 它会把图片转化成require或者import的形式。音频、视频等资源都可以被处理。
处理html中图片加载的问题,如果在html中直接引入图片,不使用html-loader不会对图片路径做任何处理,可以使用html-loader处理。
尽管配置了html-loader,html中的路径图片得到了处理,但是不能正常读出图片,是因为
url-loader默认采用ES Modules规范解析
,但是html-loader引入图片使的是CommonJS规范。
解决:关闭url-loader默认的ES Modules规范,强制url-loader使用CommonJS规范进行打包
esModule:false
复制代码
webpack4中只需要url-loader配置esModule:false
webpack5需要html-loader和url-loader都配置esModule:false
html-loader和html-webpack-plugin的冲突:
原因:htmlWebpackPlugin会检测目标文件是否已经有loader处理,如果有
其他loader处理,htmlWebpackPlugin不再使用lodash.template去处理ejs语法。
html-loader 如果是普通的html,就按照html-loader处理,否则html都改成ejs语法。
html-loader 可以处理静态的模板,而且模板里没有使用ejs语法语法,可以处理图片加载问题。如果使用了ejs语法,可以使用html-webpack-
- image-webpack-loader:使用图片压缩能极大的减少包的大小
打包字体
- file-loader 打包字体
不满足url-loader的条件就使用file-loader
其他
- DefinePlugin(webpack内置插件):定义环境变量,这对开发模式和发布模式的构建允许不同的行为非常有用。
- copy-webpack-plugin :不需要处理的其他文件,可以直接复制到输出目录, 拷贝资源插件
- clean-webpack-plugin:清理指定目录下指定的文件 每次打包之前,先删除历史文件
- html-webpack-plugin : 自动生成html (filename,title,template)
- compression-webpack-plugin:压缩文件,生产环境可采用gzip压缩JS和CSS
new CompressionPlugin({ //打包文件为giz格式
filename: '[path].gz[query]', //目标资源名称。[file] 会被替换成原资源。[path] 会被替换成原资源路径,[query] 替换成原查询字符串
algorithm: 'gzip',//算法
test: new RegExp('\\.(js|css)$'),
threshold: 10240,//只处理比这个值大的资源。按字节计算
minRatio: 0.8//只有压缩率比这个值小的资源才会被处理
})
复制代码
-
vconsole-webpack-plugin:调试
-
extract-text-webpack-plugin:将js文件中引用的样式单独抽离成css文件,防止将样式打包在js中引起页面样式加载错乱的现象
-
ProvidePlugin: 自动加载模块,而不必到处 import 或 require
要自动加载 jquery,我们可以将两个变量都指向对应的 node 模块
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
复制代码
- SplitChunksPlugin: 拆包
splitChunks: {
chunks: 'async',
minSize: 20000,
minRemainingSize: 0,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
automaticNameDelimiter: '~',
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
复制代码
- uglifyjs-webpack-plugin:基于UglifyJS压缩代码
new UglifyJsPlugin()
复制代码
var webpackConfig = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index_bundle.js'
},
plugins: [new HtmlWebpackPlugin()]
};
复制代码
-
CommonsChunkPlugin(webpack内置插件):提高打包效率,将第三方库和业务代码分开打包。
-
babel-plugin-transform-runtime:减少冗余代码
注意:
为什么不用脚手架:如果你直接使用了vue-cli,wepback从3升级到4,会碰到extract-text-webpack-plugin还没有webpack4版本,会报错,使用npm install extract-text-webpack-plugin@next解决
避免不了将生态相关的插件更新到新版,这个过程中可能会遇到很多坑:比如CommonsChunkPlugin 已经在webpack4中移除
url-loader 和 file-loader的区别
url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。
file-loader 默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。
import img from ‘./file.png’
生成文件 file.png,输出到输出目录并返回 public URL。
“/public/path/0dcbbaa7013869e351f.png”
test: /\.(jpe?g|png|gif)$/,
use: [
{
loader: 'url-loader',
options: {
esModule: false, // 不加的话会有这种情况 img属性src="https://juejin.cn/post/[object Module]"
limit: 1024 * 100, // 当大于100kb时候,将文件打包到publicPath中
outputPath: 'images', // 将文件打包到哪里
publicPath: 'images/',
name: '[name].[hash:8].[ext]'
}
}
]
options中的publicPath+name=html和css引用路径的全写,那也就意味着,如果publicPath是../images/ 会导致页面上的引用路径出错,
而如果publicPath改成了images/,那么css引用的路径会出错,(好像不对?)
我的项目中设置:
outputPath: 'img',
publicPath: '/img',
复制代码
这种打包方式会存在一定的缺陷:打包之后,每个图片在加载时,都将会发送一个http请求,当页面图片过多,会严重拖慢网页加载速度。这种情况下,我们可以选择用url-loader进行打包,通过配置规则,让较小的图片打包成base64的形式存放在打包后的js中,不再需要单独发送http请求加载图片。