【webpack 插件開發】webpack-perload-parallel-plugin

開發緣由:
因爲項目是vue的頁面級組件有差很少100個,舉個case:
因爲用了 webpack-preftech-pulgin 插件, 頁面裏會插入 100個css

<link href="xxx" as="script" rel="preftech">

標籤.
經過在chrome 裏面調試發現 當頁面裏有過多preftech 會阻塞主文件的下載速度,也就是會搶佔重要資源的下載速度。因此我採用另外一種實現方式,限制最大同時預下載(未來須要)的 資源的數量,減小由於資源下載過多,形成首頁渲染過慢。
包地址
插件主代碼html

// 插件地址
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.jsvue

(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*/)
相關文章
相關標籤/搜索