YYDS:Webpackプラグインの開発

YYDS:Webpackプラグインの開発
  長い間フロントエンドにいなかった小さな開発者として、私はそれについて話さなければなりませんwebpack。私が最初にウェブパックに連絡し始めたとき、最初の反応は何でしたか(⊙_⊙)?なぜそれはとても複雑で、とても難しいと感じます、それを忘れてください!時間は良いことです。正しい前端工程化実践と理解が徐々に深まり、ウェブパックとの接触が増えるにつれて、彼はついに彼に納得し、「webpack yyds(永远滴神)!」と叫ばざるを得なくなりました

  去年の半ばにウェブパックについての記事を書きたかったのですが、いろいろな理由で遅れました(主にウェブパックを十分に理解していないと感じ、傲慢に書くことを敢えてしませんでした)。ウェブパックに触れて、いくつかの「新年の商品」を整理し、困っているxdmと共有してください!xdm···の監督の下、フォローアップでいくつかの[Webpack]記事を書き続けます。

ガイド

  この記事では、主にプラグインの実装によるcdn优化プラグイン開発の特定のプロセスCdnPluginInject紹介します。真ん中はプラグインの使用、プロジェクトでのWebpackプラグインの構成、およびWebpack関連のナレッジポイントの説明を含みます。全文は約2800語以上で、5〜10分かかると予想されます。xdmが読んだ後、学び、考え、出力できることを願っています。webpackpluginhtml-webpack-pluginvue/cli3+

注:この記事例は、vue/cli3+プロジェクトの展開に基づいています。

1.CDNの定期的な使用

index.html:

<head>
  ···
</head>
<body>
  <div id="app"></div>
  <script src="https://cdn.bootcss.com/vuex/3.1.0/vuex.min.js"></script>
  <script src="https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js"></script>
  ···
</body>

vue.config.js:

module.exports = {
  ···
  configureWebpack: {
    ···
    externals: {
      'vuex': 'Vuex',
      'vue-router': 'VueRouter',
      ···
    }
  },

2.Webpackプラグインを開発します

webpackの公式ウェブサイトではこれを紹介しています。プラグインはサードパーティの開発者にwebpackエンジンの完全な機能を提供します。段階的なビルドコールバックを使用して、開発者は独自の動作をWebpackビルドプロセスに導入できます。プラグインの作成は、ローダーの作成よりも高度です。対応するフックを実装するには、webpackの基本的な内部機能のいくつかを理解する必要があるためです。

プラグインは次のもので構成されています。

  • 名前付きJavaScript関数。
  • プロトタイプで適用メソッドを定義します。
  • タッチウェブパック独自のイベントフックを指定します
  • Webpack内のインスタンス固有のデータを操作します。
  • 関数の実装後に、webpackによって提供されるコールバックを呼び出します。
    // 一个 JavaScript class
    class MyExampleWebpackPlugin {
    // 将 `apply` 定义为其原型方法,此方法以 compiler 作为参数
    apply(compiler) {
    // 指定要附加到的事件钩子函数
     compiler.hooks.emit.tapAsync(
       'MyExampleWebpackPlugin',
       (compilation, callback) =&gt; {
         console.log('This is an example plugin!');
         console.log('Here’s the `compilation` object which represents a single build of assets:', compilation);
         // 使用 webpack 提供的 plugin API 操作构建结果
         compilation.addModule(/* ... */);
         callback();
       }
     );
    }
    }

3、CDN最適化プラグインの実装

アイデア:

  • 1、名前の作成JavaScript機能(利用ES6class実現を)。
  • 2、それはプロトタイプapplyメソッドで定義されています。
  • 3. Webpack自体にアクセスするイベントフックを指定します(compilationフックはここタッチされます:コンパイルが作成された後、プラグインが実行されます)。
  • 図4に示すように、フックの動作の場合にはindex.html(TOに挿入中央)。cdnscript标签index.html
  • 5.applyメソッドが実行される前にcdn的参数それwebpack入れてください外部扩展externals;
  • 6.関数の実装後にwebpack提供される呼び出しcallback

実装手順:

1、名前付きを作成しますJavaScript実現の機能ES6使用してclass

  クラスを作成しcdnPluginInject、渡されたパラメーターを受け取るクラスのコンストラクターを追加します。ここでは、受け取ったパラメーターの形式を次のように定義します。

modules:[
  {
    name: "xxx",    //cdn包的名字
    var: "xxx", //cdn引入库在项目中使用时的变量名
    path: "http://cdn.url/xxx.js" //cdn的url链接地址
  },
  ···
]

定義クラスの変数は、modules渡されたcdn参数処理結果を受け取ります。

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
 ···
}
module.exports = CdnPluginInject;

2、それはプロトタイプapplyメソッドで定義されています

プラグインは、applyインスタンス化されたコンストラクターです(プロトタイプオブジェクトにはこのコンストラクターメソッドがあります)。applyプラグをインストールするこの方法は、一度webpackコンパイラと呼ばれます。applyこのメソッドは、webpackコンパイラオブジェクトへの参照を受け取ることができるため、コールバック関数でコンパイラオブジェクトにアクセスできます。

cdnPluginInject.jsコードは次のように表示されます。

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
  //webpack plugin开发的执行入口apply方法
  apply(compiler) {
    ···
  }

module.exports = CdnPluginInject;

3.Webpack自体に触れるイベントフックを指定します

  ここでcompilationフックに触れます。コンパイルが作成された後、プラグインが実行されます。

YYDS:Webpackプラグインの開発

  compilationはいcompiler、フック関数です。コンパイルにより、コンパイルプロセスの新しいインスタンスが作成されます。コンパイルインスタンスは問題访问所有模块和它们的依赖ありません。これらのモジュールを取得したら、必要に応じて操作できます。

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
  //webpack plugin开发的执行入口apply方法
  apply(compiler) {
    //获取webpack的输出配置对象
    const { output } = compiler.options;
    //处理output.publicPath, 决定最终资源相对于引用它的html文件的相对位置
    output.publicPath = output.publicPath || "/";
    if (output.publicPath.slice(-1) !== "/") {
      output.publicPath += "/";
    }
    //触发compilation钩子函数
    compiler.hooks.compilation.tap("CdnPluginInject", compilation => { 
     ···
  }
}

module.exports = CdnPluginInject;

4.フックイベントで操作しますindex.html

  このステップでは、主に達成すべきである挿入してcdnscript标签index.html、それを達成するためにどのように?vueプロジェクトでは、webpackは実際にhtml-webpack-pluginを.html使用してパッケージ化時にファイル生成するため、ここでhtml-webpack-pluginhtmlファイルを操作してcdnスクリプトタグを挿入することもできます

// 4.1 引入html-webpack-plugin依赖
const HtmlWebpackPlugin = require("html-webpack-plugin");

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
  //webpack plugin开发的执行入口apply方法
  apply(compiler) {
    //获取webpack的输出配置对象
    const { output } = compiler.options;
    //处理output.publicPath, 决定最终资源相对于引用它的html文件的相对位置
    output.publicPath = output.publicPath || "/";
    if (output.publicPath.slice(-1) !== "/") {
      output.publicPath += "/";
    }
    //触发compilation钩子函数
    compiler.hooks.compilation.tap("CdnPluginInject", compilation => { 
      // 4.2 html-webpack-plugin中的hooks函数,当在资源生成之前异步执行
      HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration
       .tapAsync("CdnPluginInject", (data, callback) => {   // 注册异步钩子
            //获取插件中的cdnModule属性(此处为undefined,因为没有cdnModule属性)
          const moduleId = data.plugin.options.cdnModule;  
          // 只要不是false(禁止)就行
          if (moduleId !== false) {    
             // 4.3得到所有的cdn配置项
            let modules = this.modules[                    
                moduleId || Reflect.ownKeys(this.modules)[0] 
            ];
            if (modules) {
              // 4.4 整合已有的js引用和cdn引用
              data.assets.js = modules
                .filter(m => !!m.path)
                .map(m => {
                  return m.path;
                })
                .concat(data.assets.js);
              // 4.5 整合已有的css引用和cdn引用
              data.assets.css = modules
                .filter(m => !!m.style)
                .map(m => {
                  return m.style;
                })
                .concat(data.assets.css); 
            }
          }
            // 4.6 返回callback函数
          callback(null, data);
        });
  }
}

module.exports = CdnPluginInject;

次に、上記の実現を段階的に分析します。

  • 4.1.html-webpack-pluginの依存関係を導入します。言うまでもありません。
  • 4.2。呼び出しhtml-webpack-pluginhooks関数は、html-webpack-pluginリソースが生成される前に非同期実行されます。これはhtml-webpack-plugin、開発html-webpack-plugin者がプラグインを呼び出すさまざまな段階でさまざまな操作を埋め込むために、開発にプラグイン多くのフック関数を構築したことを心から自慢している著者です。ので、ここでは使用することができます動作するHTMLのペアを。html-webpack-pluginbeforeAssetTagGeneration
  • 4.3。beforeAssetTagGeneration必要性がでCDNによってインポートされることをすべての構成データを取得します。
  • 4.4。既存のjs参照とcdn参照を統合します。ステージで生成された(そして最終的にindex.htmlに挿入された)すべてのリンク/パスをdata.assets.js取得compilationjs资源、構成が必要なものをcdn的path数据(cdn的url)マージできます
  • 4.5。既存のcss参照とcdn参照を統合します。ステージで生成された(そして最終的にindex.htmlに挿入された)すべてのリンク/パスをdata.assets.css取得compilationcss资源、構成が必要なcssタイプをcdn的path数据(cdn的url)マージできます
  • 4.6。コールバック関数を返します。目的はwebpack、操作が完了し、次のステップに進むことができることを通知することです。

5、設定webpack外部扩展externals

applyメソッドを実行  する前に、完了する必要のあるもう1つのステップがあります。それをcdn的参数構成外部扩展externalsしますcompiler.options.externals。webpackでexternals属性を直接取得し、操作後にcdn構成でデータを構成できます。

6 、callback

  コールバックを返して、WebpackCdnPluginInjectプラグインが完了したことを通知します。

// 4.1 引入html-webpack-plugin依赖
const HtmlWebpackPlugin = require("html-webpack-plugin");

class CdnPluginInject {
  constructor({
    modules,
  }) {
    // 如果是数组,将this.modules变换成对象形式
    this.modules = Array.isArray(modules) ? { ["defaultCdnModuleKey"]: modules } : modules; 
  }
  //webpack plugin开发的执行入口apply方法
  apply(compiler) {
    //获取webpack的输出配置对象
    const { output } = compiler.options;
    //处理output.publicPath, 决定最终资源相对于引用它的html文件的相对位置
    output.publicPath = output.publicPath || "/";
    if (output.publicPath.slice(-1) !== "/") {
      output.publicPath += "/";
    }
    //触发compilation钩子函数
    compiler.hooks.compilation.tap("CdnPluginInject", compilation => { 
      // 4.2 html-webpack-plugin中的hooks函数,当在资源生成之前异步执行
      HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration
       .tapAsync("CdnPluginInject", (data, callback) => {   // 注册异步钩子
            //获取插件中的cdnModule属性(此处为undefined,因为没有cdnModule属性)
          const moduleId = data.plugin.options.cdnModule;  
          // 只要不是false(禁止)就行
          if (moduleId !== false) {    
             // 4.3得到所有的cdn配置项
            let modules = this.modules[                    
                moduleId || Reflect.ownKeys(this.modules)[0] 
            ];
            if (modules) {
              // 4.4 整合已有的js引用和cdn引用
              data.assets.js = modules
                .filter(m => !!m.path)
                .map(m => {
                  return m.path;
                })
                .concat(data.assets.js);
              // 4.5 整合已有的css引用和cdn引用
              data.assets.css = modules
                .filter(m => !!m.style)
                .map(m => {
                  return m.style;
                })
                .concat(data.assets.css); 
            }
          }
            // 4.6 返回callback函数
          callback(null, data);
        });

      // 5.1 获取externals
        const externals = compiler.options.externals || {};
      // 5.2 cdn配置数据添加到externals
      Reflect.ownKeys(this.modules).forEach(key => {
        const mods = this.modules[key];
        mods
          .forEach(p => {
          externals[p.name] = p.var || p.name; //var为项目中的使用命名
        });
      });
      // 5.3 externals赋值
      compiler.options.externals = externals; //配置externals

      // 6 返回callback
      callback();
  }
}

module.exports = CdnPluginInject;

  この時点で、完全なWebpackプラグインCdnPluginInjectが開発されました。次に試してみましょう。

4つのcdn最適化プラグインの使用

  vueプロジェクトのvue.config.jsファイルで紹介して使用ますCdnPluginInject

cdn構成ファイルCdnConfig.js:

/*
 * 配置的cdn
 * @name: 第三方库的名字
 * @var: 第三方库在项目中的变量名
 * @path: 第三方库的cdn链接
 */
module.exports = [
  {
    name: "moment",
    var: "moment",
    path: "https://cdn.bootcdn.net/ajax/libs/moment.js/2.27.0/moment.min.js"
  },
  ···
];

configureWebpackで構成します。

const CdnPluginInject = require("./CdnPluginInject");
const cdnConfig = require("./CdnConfig");

module.exports = {
  ···
  configureWebpack: config => {
    //只有是生产山上线打包才使用cdn配置
    if(process.env.NODE.ENV =='production'){
      config.plugins.push(
        new CdnPluginInject({
          modules: CdnConfig
        })
      )
    }
  }
  ···
}

chainWebpackでの構成:

const CdnPluginInject = require("./CdnPluginInject");
const cdnConfig = require("./CdnConfig");

module.exports = {
  ···
  chainWebpack: config => {
    //只有是生产山上线打包才使用cdn配置
    if(process.env.NODE.ENV =='production'){
      config.plugin("cdn").use(
        new CdnPluginInject({
          modules: CdnConfig
        })
      )
    }
  }
  ···
}

  を使用してCdnPluginInject

  • 1.構成を通じてCDN最適化の管理と保守を実現します。
  • 2.さまざまな環境のCDN構成の最適化を実現します(開発環境はローカルインストールの依存関係を直接使用してデバッグし、実稼働環境はCDNモードに適応してロードを最適化します)。

V.まとめ

  それを読んだ後webpack、大物からいくつかの疑いがあるに違いありません、このプラグインはwebpack-cdn-pluginの物乞いバージョンではありませんプロジェクトが実際に変更する必要のある模倣バージョンと組み合わせてCdnPluginInjectwebpack-cdn-pluginソースコードから学ぶだけですwebpack-cdn-plugin。cdnリンクの生成をカプセル化するのと比較して、cdnリンクCdnPluginInjectを直接構成することで、cdn構成を選択するのが簡単になります。詳細を知りたい場合は、xdmwebpack-cdn-pluginのソースコードを参照しください。作成者が継続的に繰り返し更新した後、より構成可能なパラメーターとより強力な機能が提供されます(ここでも)。

重点:整理不易,觉得还可以的xdm记得 一键三连 哟!

記事参照

おすすめ

転載: blog.51cto.com/15066867/2597973