<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          YYDS: Webpack Plugin開發(fā)

          共 10246字,需瀏覽 21分鐘

           ·

          2021-01-23 14:04



          作者:哇喔WEB
          來源:SegmentFault 思否社區(qū)



          ??

          剛開始接觸webpack時(shí)第一反應(yīng)這是啥(⊙_⊙)? 怎么這么復(fù)雜,感覺好難呀,算了先不管這些!時(shí)間是個(gè)好東西呀,隨著對(duì)前端工程化的實(shí)踐和理解慢慢加深,跟webpack接觸越來越多,最終還是被ta折服,不禁高呼一聲“webpack yyds(永遠(yuǎn)滴神)!”

          ??

          去年年中就想寫一些關(guān)于webpack的文章,由于各種原因耽擱了(主要是覺得對(duì)webpack理解還不夠,不敢妄自下筆);臨近年節(jié),時(shí)間也有些了,與其 "摸魚"不如摸摸webpack,整理一些"年貨"分享給需要的xdm!后續(xù)會(huì)繼續(xù)寫一些【 Webpack】系列文章,xdm監(jiān)督···




          導(dǎo)讀

          ??

          本文主要通過實(shí)現(xiàn)一個(gè)cdn優(yōu)化的插件CdnPluginInject介紹下webpack的插件plugin開發(fā)的具體流程,中間會(huì)涉及到html-webpack-plugin插件的使用、vue/cli3+項(xiàng)目中webpack插件的配置以及webpack相關(guān)知識(shí)點(diǎn)的說明。全文大概2800+字,預(yù)計(jì)耗時(shí)5~10分鐘,希望xdm看完有所學(xué)、有所思、有所輸出!


          注意:文章中實(shí)例基于vue/cli3+工程展開!




          一、cdn常規(guī)使用


          index.html:



          ??···


          ??"app">

          ??
          ??
          ??···


          vue.config.js:


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




          二、開發(fā)一個(gè)webpack plugin


          webpack官網(wǎng)如此介紹到:插件向第三方開發(fā)者提供了 webpack 引擎中完整的能力。使用階段式的構(gòu)建回調(diào),開發(fā)者可以引入它們自己的行為到 webpack 構(gòu)建流程中。創(chuàng)建插件比創(chuàng)建 loader 更加高級(jí),因?yàn)槟銓⑿枰斫庖恍?webpack 底層的內(nèi)部特性來實(shí)現(xiàn)相應(yīng)的鉤子!

          一個(gè)插件由以下構(gòu)成:

          • 一個(gè)具名 JavaScript 函數(shù)。
          • 在它的原型上定義 apply 方法。
          • 指定一個(gè)觸及到 webpack 本身的?事件鉤子。
          • 操作 webpack 內(nèi)部的實(shí)例特定數(shù)據(jù)。
          • 在實(shí)現(xiàn)功能后調(diào)用 webpack 提供的 callback。
          //?一個(gè)?JavaScript?class
          class?MyExampleWebpackPlugin?{
          //?將?`apply`?定義為其原型方法,此方法以?compiler?作為參數(shù)
          ?apply(compiler)?{
          ???//?指定要附加到的事件鉤子函數(shù)
          ?????compiler.hooks.emit.tapAsync(
          ???????'MyExampleWebpackPlugin',
          ???????(compilation,?callback)?=>?{
          ?????????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?操作構(gòu)建結(jié)果
          ?????????compilation.addModule(/*?...?*/);
          ?????????callback();
          ???????}
          ?????);
          ?}
          }




          三、cdn優(yōu)化插件實(shí)現(xiàn)


          思路:

          • 1、創(chuàng)建一個(gè)具名?JavaScript?函數(shù)(使用ES6的class實(shí)現(xiàn));
          • 2、在它的原型上定義 apply 方法;
          • 3、指定一個(gè)觸及到 webpack 本身的事件鉤子(此處觸及compilation鉤子:編譯(compilation)創(chuàng)建之后,執(zhí)行插件);
          • 4、在鉤子事件中操作index.html(將cdn的script標(biāo)簽插入到index.html中);
          • 5、在apply方法執(zhí)行完之前將cdn的參數(shù)放入webpack的外部擴(kuò)展externals中;
          • 6、在實(shí)現(xiàn)功能后調(diào)用 webpack 提供的 callback;

          實(shí)現(xiàn)步驟:

          1、創(chuàng)建一個(gè)具名?JavaScript?函數(shù)(使用ES6的class實(shí)現(xiàn))

          ??
          創(chuàng)建類cdnPluginInject,添加類的構(gòu)造函數(shù)接收傳遞過來的參數(shù);此處我們定義接收參數(shù)的格式如下:

          modules:[
          ??{
          ????name:?"xxx",????//cdn包的名字
          ????var:?"xxx",????//cdn引入庫在項(xiàng)目中使用時(shí)的變量名
          ????path:?"http://cdn.url/xxx.js"?//cdn的url鏈接地址
          ??},
          ??···
          ]

          定義類的變量modules接收傳遞的cdn參數(shù)的處理結(jié)果:

          class?CdnPluginInject?{
          ??constructor({
          ????modules,
          ??})?{
          ????//?如果是數(shù)組,將this.modules變換成對(duì)象形式
          ????this.modules?=?Array.isArray(modules)???{?["defaultCdnModuleKey"]:?modules?}?:?modules;?
          ??}
          ?···
          }
          module.exports?=?CdnPluginInject;

          2、在它的原型上定義?apply?方法


          插件是由一個(gè)構(gòu)造函數(shù)(此構(gòu)造函數(shù)上的 prototype 對(duì)象具有 apply 方法)的所實(shí)例化出來的。這個(gè) apply 方法在安裝插件時(shí),會(huì)被 webpack compiler 調(diào)用一次。apply 方法可以接收一個(gè) webpack compiler 對(duì)象的引用,從而可以在回調(diào)函數(shù)中訪問到 compiler 對(duì)象

          cdnPluginInject.js代碼如下:

          class?CdnPluginInject?{
          ??constructor({
          ????modules,
          ??})?{
          ????//?如果是數(shù)組,將this.modules變換成對(duì)象形式
          ????this.modules?=?Array.isArray(modules)???{?["defaultCdnModuleKey"]:?modules?}?:?modules;?
          ??}
          ??//webpack?plugin開發(fā)的執(zhí)行入口apply方法
          ??apply(compiler)?{
          ????···
          ??}

          module.exports?=?CdnPluginInject;

          3、指定一個(gè)觸及到 webpack 本身的事件鉤子


          此處觸及compilation鉤子:編譯(compilation)創(chuàng)建之后,執(zhí)行插件。

          ??
          compilation 是 compiler 的一個(gè)hooks函數(shù), compilation 會(huì)創(chuàng)建一次新的編譯過程實(shí)例,一個(gè) compilation 實(shí)例可以訪問所有模塊和它們的依賴,在獲取到這些模塊后,根據(jù)需要對(duì)其進(jìn)行操作處理!

          class?CdnPluginInject?{
          ??constructor({
          ????modules,
          ??})?{
          ????//?如果是數(shù)組,將this.modules變換成對(duì)象形式
          ????this.modules?=?Array.isArray(modules)???{?["defaultCdnModuleKey"]:?modules?}?:?modules;?
          ??}
          ??//webpack?plugin開發(fā)的執(zhí)行入口apply方法
          ??apply(compiler)?{
          ????//獲取webpack的輸出配置對(duì)象
          ????const?{?output?}?=?compiler.options;
          ????//處理output.publicPath,?決定最終資源相對(duì)于引用它的html文件的相對(duì)位置
          ????output.publicPath?=?output.publicPath?||?"/";
          ????if?(output.publicPath.slice(-1)?!==?"/")?{
          ??????output.publicPath?+=?"/";
          ????}
          ????//觸發(fā)compilation鉤子函數(shù)
          ????compiler.hooks.compilation.tap("CdnPluginInject",?compilation?=>?{?
          ?????···
          ??}
          }

          module.exports?=?CdnPluginInject;

          4、在鉤子事件中操作index.html


          這一步主要是要實(shí)現(xiàn)?將cdn的script標(biāo)簽插入到index.html中?;如何實(shí)現(xiàn)呢?在vue項(xiàng)目中webpack進(jìn)行打包時(shí)其實(shí)是使用html-webpack-plugin生成.html文件的,所以我們此處也可以借助html-webpack-plugin對(duì)html文件進(jìn)行操作插入cdn的script標(biāo)簽。

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

          class?CdnPluginInject?{
          ??constructor({
          ????modules,
          ??})?{
          ????//?如果是數(shù)組,將this.modules變換成對(duì)象形式
          ????this.modules?=?Array.isArray(modules)???{?["defaultCdnModuleKey"]:?modules?}?:?modules;?
          ??}
          ??//webpack?plugin開發(fā)的執(zhí)行入口apply方法
          ??apply(compiler)?{
          ????//獲取webpack的輸出配置對(duì)象
          ????const?{?output?}?=?compiler.options;
          ????//處理output.publicPath,?決定最終資源相對(duì)于引用它的html文件的相對(duì)位置
          ????output.publicPath?=?output.publicPath?||?"/";
          ????if?(output.publicPath.slice(-1)?!==?"/")?{
          ??????output.publicPath?+=?"/";
          ????}
          ????//觸發(fā)compilation鉤子函數(shù)
          ????compiler.hooks.compilation.tap("CdnPluginInject",?compilation?=>?{?
          ??????//?4.2?html-webpack-plugin中的hooks函數(shù),當(dāng)在資源生成之前異步執(zhí)行
          ??????HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration
          ???????.tapAsync("CdnPluginInject",?(data,?callback)?=>?{???//?注冊異步鉤子
          ????????????//獲取插件中的cdnModule屬性(此處為undefined,因?yàn)闆]有cdnModule屬性)
          ??????????const?moduleId?=?data.plugin.options.cdnModule;??
          ??????????//?只要不是false(禁止)就行
          ??????????if?(moduleId?!==?false)?{????
          ?????????????//?4.3得到所有的cdn配置項(xiàng)
          ????????????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函數(shù)
          ??????????callback(null,?data);
          ????????});
          ??}
          }

          module.exports?=?CdnPluginInject;

          接下來逐步對(duì)上述實(shí)現(xiàn)進(jìn)行分析:

          • 4.1、引入html-webpack-plugin依賴,這個(gè)不用多說;
          • 4.2、調(diào)用html-webpack-plugin中的hooks函數(shù),在html-webpack-plugin中資源生成之前異步執(zhí)行;這里由衷的夸夸html-webpack-plugin的作者了,ta在開發(fā)html-webpack-plugin時(shí)就在插件中內(nèi)置了很多的hook函數(shù)供開發(fā)者在調(diào)用插件的不同階段嵌入不同操作;因此,此處我們可以使用html-webpack-plugin的beforeAssetTagGeneration對(duì)html進(jìn)行操作;
          • 4.3、 在beforeAssetTagGeneration中,獲取得到所有的需要進(jìn)行cdn引入的配置數(shù)據(jù);
          • 4.4、 整合已有的js引用和cdn引用;通過data.assets.js可以獲取到compilation階段所有生成的js資源(最終也是插入index.html中)的鏈接/路徑,并且將需要配置的cdn的path數(shù)據(jù)(cdn的url)合并進(jìn)去;
          • 4.5、 整合已有的css引用和cdn引用;通過data.assets.css可以獲取到compilation階段所有生成的css資源(最終也是插入index.html中)的鏈接/路徑,并且將需要配置的css類型cdn的path數(shù)據(jù)(cdn的url)合并進(jìn)去;
          • 4.6、 返回callback函數(shù),目的是告訴webpack該操作已經(jīng)完成,可以進(jìn)行下一步了;


          5、設(shè)置webpack的外部擴(kuò)展externals


          在apply方法執(zhí)行完之前還有一步必須完成:將cdn的參數(shù)配置到外部擴(kuò)展externals中;可以直接通過compiler.options.externals獲取到webpack中externals屬性,經(jīng)過操作將cdn配置中數(shù)據(jù)配置好就ok了。

          6、 callback;


          返回callback,告訴webpack CdnPluginInject插件已經(jīng)完成;

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

          class?CdnPluginInject?{
          ??constructor({
          ????modules,
          ??})?{
          ????//?如果是數(shù)組,將this.modules變換成對(duì)象形式
          ????this.modules?=?Array.isArray(modules)???{?["defaultCdnModuleKey"]:?modules?}?:?modules;?
          ??}
          ??//webpack?plugin開發(fā)的執(zhí)行入口apply方法
          ??apply(compiler)?{
          ????//獲取webpack的輸出配置對(duì)象
          ????const?{?output?}?=?compiler.options;
          ????//處理output.publicPath,?決定最終資源相對(duì)于引用它的html文件的相對(duì)位置
          ????output.publicPath?=?output.publicPath?||?"/";
          ????if?(output.publicPath.slice(-1)?!==?"/")?{
          ??????output.publicPath?+=?"/";
          ????}
          ????//觸發(fā)compilation鉤子函數(shù)
          ????compiler.hooks.compilation.tap("CdnPluginInject",?compilation?=>?{?
          ??????//?4.2?html-webpack-plugin中的hooks函數(shù),當(dāng)在資源生成之前異步執(zhí)行
          ??????HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration
          ???????.tapAsync("CdnPluginInject",?(data,?callback)?=>?{???//?注冊異步鉤子
          ????????????//獲取插件中的cdnModule屬性(此處為undefined,因?yàn)闆]有cdnModule屬性)
          ??????????const?moduleId?=?data.plugin.options.cdnModule;??
          ??????????//?只要不是false(禁止)就行
          ??????????if?(moduleId?!==?false)?{????
          ?????????????//?4.3得到所有的cdn配置項(xiàng)
          ????????????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函數(shù)
          ??????????callback(null,?data);
          ????????});
          ??????
          ??????//?5.1?獲取externals
          ?????????const?externals?=?compiler.options.externals?||?{};
          ??????//?5.2?cdn配置數(shù)據(jù)添加到externals
          ??????Reflect.ownKeys(this.modules).forEach(key?=>?{
          ????????const?mods?=?this.modules[key];
          ????????mods
          ??????????.forEach(p?=>?{
          ??????????externals[p.name]?=?p.var?||?p.name;?//var為項(xiàng)目中的使用命名
          ????????});
          ??????});
          ??????//?5.3?externals賦值
          ??????compiler.options.externals?=?externals;?//配置externals
          ??????
          ??????//?6?返回callback
          ??????callback();
          ??}
          }

          module.exports?=?CdnPluginInject;

          至此,一個(gè)完整的webpack插件CdnPluginInject就開發(fā)完成了!接下來使用著試一試。



          四、cdn優(yōu)化插件使用

          ??
          在vue項(xiàng)目的vue.config.js文件中引入并使用CdnPluginInject:

          cdn配置文件CdnConfig.js:

          /*
          ?*?配置的cdn
          ?*?@name:?第三方庫的名字
          ?*?@var:第三方庫在項(xiàng)目中的變量名
          ?*?@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?=>?{
          ????//只有是生產(chǎn)山上線打包才使用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?=>?{
          ????//只有是生產(chǎn)山上線打包才使用cdn配置
          ????if(process.env.NODE.ENV?=='production'){
          ??????config.plugin("cdn").use(
          ????????new?CdnPluginInject({
          ??????????modules:?CdnConfig
          ????????})
          ??????)
          ??????}
          ??}
          ??···
          }

          ??通過使用CdnPluginInject:

          • 1、通過配置實(shí)現(xiàn)對(duì)cdn優(yōu)化的管理和維護(hù);
          • 2、實(shí)現(xiàn)針對(duì)不同環(huán)境做cdn優(yōu)化配置(開發(fā)環(huán)境直接使用本地安裝依賴進(jìn)行調(diào)試,生產(chǎn)環(huán)境適應(yīng)cdn方式優(yōu)化加載);




          五、小結(jié)

          ??
          看完后肯定有webpack大佬有一絲絲疑惑,這個(gè)插件不就是 webpack-cdn-plugin 的乞丐版!CdnPluginInject只不過是本人根據(jù)webpack-cdn-plugin源碼的學(xué)習(xí),結(jié)合自己項(xiàng)目實(shí)際所需修改的仿寫版本,相較于webpack-cdn-plugin將cdn鏈接的生成進(jìn)行封裝,CdnPluginInject是直接將cdn鏈接進(jìn)行配置,對(duì)于選擇cdn顯配置更加簡單。想要進(jìn)一步學(xué)習(xí)的xdm可以看看webpack-cdn-plugin的源碼,經(jīng)過作者的不斷的迭代更新,其提供的可配置參數(shù)更加豐富,功能更加強(qiáng)大(再次膜拜)。



          點(diǎn)擊左下角閱讀原文,到?SegmentFault 思否社區(qū)?和文章作者展開更多互動(dòng)和交流。

          -?END -

          瀏覽 32
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  啪视频在线观看 | 欧美成人免费一区二区三区 | 久久天天操 | 婷婷基地在线 | 高清无码免费视屏 |