html中inline函数,自己实现一个inline-manifest-webpack-plugin

背景

项目里有用到inline-manifest-webpack-plugin,当把html-webpack-plugin升级到v4时,插件就报错了。原本想等插件适配,结果发现不维护了。所以自己动手实现一个吧!

webpack 构建流程

webpack的基本构建流程如下:

webpack 初始化: 包括校验配置文件,初始化默认插件,创建Compiler实例等

run: 执行 run 方法

compilation:创建Compilation实例,回调compilation相关钩子

emit:文件内容已经解析完毕,准备生成文件

afterEmit:文件已经写入磁盘

done:完成编译

webpack 插件实例

webpack-plugin需要实现一个apply方法,参数是Compiler实例,下面是一个简单的实例

class MyWebpackPlugin {

constructor(options) {}

apply(compiler) {

compiler.hooks.emit.tap("MyWebpackPlugin", (compilation) => {

console.log("MyWebpackPlugin");

});

}

}

module.exports = MyWebpackPlugin;

复制代码

只需要在webpack的配置文件中,添加插件就能在控制台看见输出了

module.exports = {

plugins:[

new MyWebpackPlugin(),

]

};

复制代码

要实现inline-manifest-webpack-plugin的功能,只需要在合适的hook里面实现我们的逻辑就行了

inline-manifest-webpack-plugin 的作用

将我们选择的chunk文件直接插入到html中,减少请求次数,方便做缓存等,一般用来抽离 webpack 的运行时文件(runtime.js)。它大概实现了一下三个功能:

删除index.html里的script标签

将删除标签所对应的内容直接插入index.html

删除原来的js文件

删除 index.html 里的 script 标签

inline-manifest-webpack-plugin是基于html-webpack-plugin上的插件,html-webpack-plugin为我们提供的 hook 如图所示

8f92f3c1d9f6ecdd89e4af2d7c31f076.png

在生成标签之前(beforeAssetTagGeneration)去掉指定 chunk 文件

compiler.hooks.compilation.tap("InlineManifestWebpackPlugin", (compilation) => {

// 在标签生成之前

HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(

"InlineManifestWebpackPlugin",

(htmlPluginData, cb) => {

const assets = htmlPluginData.assets;

const manifestAssetName = this.getAssetByName(compilation.chunks, this.chunkName);

if (manifestAssetName) {

const runtimeIndex = assets.js.indexOf(assets.publicPath + manifestAssetName);

// 缓存第二步里插入的内容和位子

this.chunkScript = {

tagName: "script",

closeTag: true,

attributes: {

type: "text/javascript",

},

innerHTML: sourceMappingURL.removeFrom(compilation.assets[manifestAssetName].source()),

};

this.chunkIndex = runtimeIndex;

// 从html中删除原标签

if (runtimeIndex !== -1) {

assets.js.splice(runtimeIndex, 1);

delete assets.js[this.chunkName];

}

}

cb(null, htmlPluginData);

}

);

});

复制代码

将删除标签所对应的内容直接插入 index.html

生成标签的时候把第一步缓存的标签插入

compiler.hooks.compilation.tap("InlineManifestWebpackPlugin", (compilation) => {

// 插入标签

HtmlWebpackPlugin.getHooks(compilation).alterAssetTags.tapAsync(

"InlineManifestWebpackPlugin",

(htmlPluginData, cb) => {

// 向html中注入

if (this.chunkScript && this.chunkIndex !== -1) {

htmlPluginData.assetTags.scripts.splice(this.chunkIndex, 0, this.chunkScript);

this.chunkScript = null;

this.chunkIndex = -1;

}

cb(null, htmlPluginData);

}

);

});

复制代码

删除原来的 js 文件

在 webpack 生成所以文件之后,删除原本的文件

// 在emit阶段插入钩子函数

compiler.hooks.emit.tap("InlineManifestWebpackPlugin", (compilation) => {

// 删除原文件

delete compilation.assets[this.getAssetByName(compilation.chunks, this.chunkName)];

});

复制代码

此时插件的功能基本上实现完成,一下是完整代码

const sourceMappingURL = require("source-map-url");

const HtmlWebpackPlugin = require("html-webpack-plugin");

class InlineManifestWebpackPlugin {

constructor(chunkName) {

this.chunkName = chunkName || "runtime";

this.chunkScript = null;

this.chunkIndex = -1;

}

getAssetByName = (chunks, chunkName) => {

return (chunks.find((chunk) => chunk.name === chunkName) || { files: [] }).files[0];

};

apply(compiler) {

// 注入 html-webpack-plugin 的处理过程

compiler.hooks.compilation.tap("InlineManifestWebpackPlugin", (compilation) => {

// 在标签生成之前

HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(

"InlineManifestWebpackPlugin",

(htmlPluginData, cb) => {

const assets = htmlPluginData.assets;

const manifestAssetName = this.getAssetByName(compilation.chunks, this.chunkName);

if (manifestAssetName) {

const runtimeIndex = assets.js.indexOf(assets.publicPath + manifestAssetName);

this.chunkScript = {

tagName: "script",

closeTag: true,

attributes: {

type: "text/javascript",

},

innerHTML: sourceMappingURL.removeFrom(compilation.assets[manifestAssetName].source()),

};

this.chunkIndex = runtimeIndex;

// 从html中删除原标签

if (runtimeIndex !== -1) {

assets.js.splice(runtimeIndex, 1);

delete assets.js[this.chunkName];

}

}

cb(null, htmlPluginData);

}

);

// 插入标签

HtmlWebpackPlugin.getHooks(compilation).alterAssetTags.tapAsync(

"InlineManifestWebpackPlugin",

(htmlPluginData, cb) => {

// 向html中注入

if (this.chunkScript && this.chunkIndex !== -1) {

htmlPluginData.assetTags.scripts.splice(this.chunkIndex, 0, this.chunkScript);

this.chunkScript = null;

this.chunkIndex = -1;

}

cb(null, htmlPluginData);

}

);

});

// 在emit阶段插入钩子函数

compiler.hooks.emit.tap("InlineManifestWebpackPlugin", (compilation) => {

// 删除原文件

delete compilation.assets[this.getAssetByName(compilation.chunks, this.chunkName)];

});

}

}

module.exports = InlineManifestWebpackPlugin;

复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值