1
webpack | plugin 机制详解
发布于: 2021 年 01 月 29 日
webpack 插件是一个具有 apply
方法的 JavaScript 对象。apply
方法会被 webpack compiler 调用,并且在整个编译生命周期都可以访问 compiler 对象。
compiler hook 的 tap 方法的第一个参数,应该是驼峰式命名的插件名称。建议为此使用一个常量,以便它可以在所有 hook 中重复使用。
1. 基本结构
class Plugin{ // 插件名称
constructor(options){
// 通过构造函数获取传递参数
this.options = options;
}
apply(compiler){ // 插件上的apply方法
// 监听钩子触发事件
compiler.hooks.done.tap('Plugin',(stats)=>{ // done钩子触发,stats作为参数
// 插件处理逻辑
})
}
}
module.exports = Plugin;
复制代码
2. 配置文件的声明
// webpack配置文件中的配置
module.exports = {
entry:'./index.js',
output:{
path:'./dist',
filename:'[name].js'
},
plugins:[
new Plugin({配置参数})
]
}
复制代码
3. 模拟器 webpack 调用 plugin
// webpack编译器中的调用
//tapable使用:模拟webpack的Compiler.js
const {SyncHook} = require("tapable");
module.exports = class Compiler{
constructor(){
this.hooks = {
// 1. 注册同步钩子
init:new SyncHook(['start']),
}
}
run(){
// 3. 触发钩子函数
this.hooks.init.call()
}
}
//模拟 plugin.js
class Plugin{
constructor(){}
apply(compiler){
// 2. 插件内监听钩子函数
compiler.hooks.init.tap('start',()=>{
console.log('compiler start')
})
}
}
// 模拟webpack.js
const options = {
plugins:[new Plugin()]
}
const compiler = new Compiler()
for(const plugin of options.plugins){
if(typeof plugin==='function'){
plugin.call(compiler,compiler)
}else{
plugin.apply(compiler)
}
}
compiler.run()
复制代码
4. CorsPlugin 跨域插件
用于处理网页域名和要载入的静态文件存放的站点域名不一致时候的跨域问题。
module.exports = class CorsPlugin {
constructor ({ publicPath, crossorigin, integrity }) {
/*
* crossorigin:跨域属性
* 值:anonymous || '' :对此元素的CORS请求将不设置凭据标志
* 值:use-credentials :对此元素的CORS请求将设置凭证标志;这意味着请求将提供凭据
*/
this.crossorigin = crossorigin
// 请求完整性,供CDN 的静态文件使用,检查文身否为原版,防止使用的CDN资源被劫持篡改
this.integrity = integrity
this.publicPath = publicPath
}
apply (compiler) {
const ID = `vue-cli-cors-plugin`
compiler.hooks.compilation.tap(ID, compilation => {
/*
* Standard Subresource Integrity缩写
* 用于解析,操作,序列化,生成和验证Subresource Integrity哈希值。
*/
const ssri = require('ssri')
// 计算文件的integrity值
const computeHash = url => {
const filename = url.replace(this.publicPath, '')
const asset = compilation.assets[filename]
if (asset) {
const src = asset.source()
const integrity = ssri.fromData(src, {
algorithms: ['sha384']
})
return integrity.toString()
}
}
compilation.hooks.htmlWebpackPluginAlterAssetTags.tap(ID, data => {
const tags = [...data.head, ...data.body]
if (this.crossorigin != null) {
// script || link 标签设置允许跨域
tags.forEach(tag => {
if (tag.tagName === 'script' || tag.tagName === 'link') {
tag.attributes.crossorigin = this.crossorigin
}
})
}
if (this.integrity) {
// 校验文件是否为原版
tags.forEach(tag => {
if (tag.tagName === 'script') {
const hash = computeHash(tag.attributes.src)
if (hash) {
tag.attributes.integrity = hash
}
} else if (tag.tagName === 'link' && tag.attributes.rel === 'stylesheet') {
const hash = computeHash(tag.attributes.href)
if (hash) {
tag.attributes.integrity = hash
}
}
})
// when using SRI, Chrome somehow cannot reuse
// the preloaded resource, and causes the files to be downloaded twice.
// this is a Chrome bug (https://bugs.chromium.org/p/chromium/issues/detail?id=677022)
// for now we disable preload if SRI is used.
data.head = data.head.filter(tag => {
return !(
tag.tagName === 'link' &&
tag.attributes.rel === 'preload'
)
})
}
})
compilation.hooks.htmlWebpackPluginAfterHtmlProcessing.tap(ID, data => {
data.html = data.html.replace(/\scrossorigin=""/g, ' crossorigin')
})
})
}
}
复制代码
划线
评论
复制
发布于: 2021 年 01 月 29 日阅读数: 32
版权声明: 本文为 InfoQ 作者【梁龙先森】的原创文章。
原文链接:【http://xie.infoq.cn/article/2c989ed17e26db1d48043a592】。文章转载请联系作者。
梁龙先森
关注
脚踏V8引擎的无情写作机器 2018.03.17 加入
还未添加个人简介
评论