开发起因:
因为我的项目是 vue 的页面级组件有差不多 100 个,举个 case:
因为用了 webpack-preftech-pulgin 插件,页面里会插入 100 个
<link href="xxx" as="script" rel="preftech">
标签.
通过在 chrome 外面调试发现 当页面里有过多 preftech 会阻塞主文件的下载速度,也就是会抢占重要资源的下载速度。所以我采纳另一种实现形式,限度最大同时预下载(未来须要)的 资源的数量,缩小因为资源下载过多,造成首页渲染过慢。
包地址
插件主代码
// 插件地址
const fs = require('fs')
const path = require('path');
const Entrypoint = require('webpack/lib/Entrypoint.js');
const assert = require('assert');
class HtmlWebpackPreFetch {constructor(options = { paralleMax: 3}) {this.options = options}
apply(complier) {
complier.hooks.compilation.tap(this.constructor.name, compilation => {
// hook HtmlWebpackPlugin 输入 index.html 之前的钩子,为了革新输入 index.html 文件内容(来自某个 webpack 插件)let hook = compilation.hooks.htmlWebpackPluginAfterHtmlProcessing;
if (!hook) {const [HtmlWebpackPlugin] = complier.options.plugins.filter((plugin) => plugin.constructor.name === 'HtmlWebpackPlugin');
assert(HtmlWebpackPlugin, 'Unable to find an instance of' +
'HtmlWebpackPlugin in the current compilation.');
hook = HtmlWebpackPlugin.constructor.getHooks(compilation).beforeEmit;
}
// 对对应的钩子 注册事件,我是这个认为????
hook.tapAsync(
this.constructor.name,
(htmlPluginData, callback) => {
try {const { outputOptions: { publicPath = ''} , chunks, chunkGroups} = compilation;
// split chunks 找到非入口文件
const sourceList = chunkGroups.filter(chunkGroup => {return !(chunkGroup instanceof Entrypoint) && !chunkGroup.isInitial()}).map(chunkGroup => chunkGroup.chunks.reduce((p, q) => {
return {files: [...p.files, q.files]
}
, {files: []} })).reduce((p, n) => {
return {files: [...p.files, ...n.files]
}
} , {files: []}).files.map(path => (`${publicPath}${path}`))
// loadTemplate.js 实现并发加载模版文件
let loadJsTemplate = fs.readFileSync(path.join(__dirname, './loadTemplate.js'), {encoding: 'utf-8'})
// 获取要下载的文件 和 配置参数
const templateOptions = JSON.stringify({
sourceList,
...this.options
})
loadJsTemplate = loadJsTemplate.replace('/*injectOptions*/', templateOptions)
let htmlTemplate = htmlPluginData.html
const htmlTemplateList = htmlTemplate.split('</body>')
htmlTemplateList.splice(1, 0, `<script>${loadJsTemplate}</script></body>`)
htmlTemplate = htmlTemplateList.join('');
htmlPluginData.html = htmlTemplate
callback(null, htmlPluginData);
} catch (error) {callback(error);
}
}
);
})}}module.exports = HtmlWebpackPreFetch
并行下载文件 loadTemplate.js
(function(options) {
var win = window;
var doc = win.document;
var sourceList = options.sourceList;
var paralleMax = options.paralleMax;
var attr = options.attr;
var jsReg = /\.js/;
var cssReg = /\.css/;
// 结构队列下载类
function QueueLoadPlugin (sourceList, paralleMax, attr) {
this.sourceList = sourceList;
this.paralleMax = paralleMax || 3;
this.sourceIndex = 0;
this.queueList = [];
this.attr = attr || {
rel: 'preload'
//rel: 'prefetch'
};
this.handleLoadLogic();};
QueueLoadPlugin.prototype.loadSingleSouce = function(href, attr, cb) {
var head = doc.head;
var link = doc.createElement('link');
link.href = href;
for(var key in attr) {if (attr.hasOwnProperty(key)) {link.setAttribute(key, attr[key]);
}
}
if(jsReg.test(href)) {link.setAttribute('as','script');
}
if(cssReg.test(href)) {link.setAttribute('as','style');
}
link.onload = function() {cb(true)
};
link.onerror = function() {cb(false)
};
head.appendChild(link);
};
QueueLoadPlugin.prototype.handleLoadLogic = function() {
var paralleMax = this.paralleMax;
var sourceList = this.sourceList;
var sourceLg = sourceList.length;
var sourceIndex = this.sourceIndex;
if (sourceIndex === 0) {for(; sourceIndex < sourceLg && sourceIndex < paralleMax; sourceIndex ++) {var href = sourceList[sourceIndex];
this.queueList.push(sourceIndex);
this.loadPartLogic(sourceIndex, href, this);
}
} else {if (sourceIndex < sourceLg) {const href = sourceList[sourceIndex];
this.loadPartLogic(sourceIndex, href, this);
sourceIndex ++;
}
}
this.sourceIndex = sourceIndex;
};
//
QueueLoadPlugin.prototype.loadPartLogic = function(idx, href, context) {var href = this.sourceList[idx];
this.loadSingleSouce(href, context.attr, function() {
var sourceLg = sourceList.length;
var curIndex = context.queueList.findIndex(d => d === idx);
context.queueList.splice(curIndex, 1, 0);
if(idx < sourceLg - 1)
context.handleLoadLogic()})
};
new QueueLoadPlugin(sourceList, paralleMax, attr);
})(/*injectOptions*/)