日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

三大應(yīng)用場景調(diào)研,Webpack 新功能 Module Federation 深入解析

 西北望msm66g9f 2020-05-06

導(dǎo)讀:Federated Modules 是一個令人激動的功能,它可能會改變未來幾年的前端打包方式,作者深入分析了 Module Federation 的原理及其應(yīng)用場景,希望能對大家有所啟發(fā)。

WHAT(Module Federation 是什么?)

Module Federation  [?fed??re??n] 使 JavaScript 應(yīng)用得以在客戶端或服務(wù)器上動態(tài)運行另一個 bundle 的代碼。

這其中的關(guān)鍵點是:

  • 動態(tài),包含兩個含義:

    • 按需,可以把一個包拆開來加載其中一部分;

    • 運行時,跑在瀏覽器而非 node 編譯時;

  • 另一個 bundle 的代碼,之前應(yīng)用之間做共享是在文件級或 npm 包級 export 成員,現(xiàn)在可以在應(yīng)用級 export 成員屬性。


一些相關(guān)的概念:

  • Remote,被 Host 消費的 Webpack 構(gòu)建;

  • Host,消費其他 Remote 的 Webpack 構(gòu)建;


一個應(yīng)用可以是 Host,也可以是 Remote,也可以同時是 Host 和 Remote。



HOW(它的原理是什么?)

通過回答 Module Federation 如何運轉(zhuǎn)?Host 如何消費 Remote?以及 Remote 如何優(yōu)先使用 Host shared 的依賴?這三個問題,我們分析一下 Module Federation 的原理。

ModuleFederationPlugin

整體是通過 ModuleFederationPlugin(https://github.com/webpack/webpack/blob/dev-1/lib/container/ModuleFederationPlugin.js)這個插件串聯(lián)起來的。

配置示例:

  1. new ModuleFederationPlugin({

  2. name: 'app-1',

  3. library: { type: 'var', name: 'app_1' },

  4. filename: 'remoteEntry.js',

  5. remotes: {

  6. app_02: 'app_02',

  7. app_03: 'app_03',

  8. },

  9. exposes: {

  10. antd: './src/antd',

  11. button: './src/button',

  12. },

  13. shared: ['react', 'react-dom'],

  14. }),


配置屬性:

  • name,必須,唯一 ID,作為輸出的模塊名,使用的時通過 ${name}/${expose} 的方式使用;

  • library,必須,其中這里的 name 為作為 umd 的 name;

  • remotes,可選,表示作為 Host 時,去消費哪些 Remote;

  • exposes,可選,表示作為 Remote 時,export 哪些屬性被消費;

  • shared,可選,優(yōu)先用 Host 的依賴,如果 Host 沒有,再用自己的;




產(chǎn)物:




  • main.js,應(yīng)用主文件;

  • remoteEntry.js,作為 remote 時被引的文件;

  • 一堆異步加載的文件,main.js 或 remoteEntry.js 里可能加載的文件;


所以比如下面如圖示例的應(yīng)用集群:



加載方式應(yīng)該這樣:

  1. <script src='C/remoteEntry.js'></script>

  2. <script src='B/remoteEntry.js'></script>

  3. <script src='A/main.js'></script


C/remoteEntry.js 和 B/remoteEntry 的順序沒有要求,只要在 A/main.js 之前就好了。

A 如何消費 B ?

可以通過代碼示例來進行理解。

B 源碼:

  1. // src/react.js

  2. export * from 'react';

  3. // webpack.config.js

  4. ...

  5. exposes: {

  6. react: './src/react',

  7. },


A 源碼:

  1. // 異步加載 B 的 react 模塊

  2. const React = await import('B/react');


B 構(gòu)建產(chǎn)物:

  1. // windows 變量

  2. let B;

  3. const moduleMap = {

  4. 'react': () => {

  5. return Promise.all([e('a'), e('b'), e('c')]),

  6. },

  7. };

  8. B = {

  9. get(moduleId) {

  10. return moduleMap(moduleId);

  11. }

  12. }

A 構(gòu)建產(chǎn)物:

  1. const modules = {

  2. 'B': () => {

  3. return B;

  4. }

  5. };

  6. // 異步獲取模塊 export 內(nèi)容

  7. function e(moduleId) {

  8. // 1. 取 shared 的模塊

  9. // 2. 取 remote 的模塊

  10. const idToExternalAndNameMapping = {

  11. 'B/react': ['B', 'react'],

  12. };

  13. // 從 module B 里取 react

  14. const data = idToExternalAndNameMapping[moduleId];

  15. __webpack_require__(data[0]).get(data[1]);

  16. // 3. 取當(dāng)前項目的異步模塊

  17. }

  18. // 初始化

  19. e('B/react');


這其中的原理:

  • 多個 Bundler 之間通過全局變量串聯(lián);

  • Remote 會 export get 方法獲取他的子模塊,子模塊的獲取通過 Promise 以按需的方式引入;

A 如何讓 B 用 A shared 的庫?

再看如下兩個代碼示例。

B 構(gòu)建產(chǎn)物:

  1. let B;

  2. __webpack_require__.Overrides = {};

  3. function e(moduleId) {

  4. // 1. 取 shared 的模塊

  5. // 當(dāng)前項目的 shared 模塊列表

  6. const fallbackMapping = {};

  7. // 先從 Overrides 里取,再從當(dāng)前項目里取

  8. push_require_try(__webpack_require__.Overrides[moduleId] || fallbackMapping[moduleId]);

  9. // 2. 取 remote 的模塊

  10. // 3. 取當(dāng)前項目的異步模塊

  11. }

  12. B = {

  13. override(override) {

  14. Object.assign(__webpack_require__.Overrides, override);

  15. }

  16. }


A 構(gòu)建產(chǎn)物:

  1. B.override(Object.assign({

  2. 'react': () => {

  3. // A 的 react 內(nèi)容

  4. },

  5. }, __webpack_require__.Overrides));


原理分析:

  • Remote(B)export override 方法,Host(A) 會調(diào)用其關(guān)聯(lián) Remote 的 override 方法,把 shared 的依賴寫進去;

  • Remote(B) 獲取模塊時會優(yōu)先從 override 里取,沒有再從當(dāng)前 Bundle 的模塊索引里??;


這樣,B 里面在 require react 時,就會用 A 的 react 模塊。

WHY(它的應(yīng)用場景有哪些?)

Module Federation 可以用在哪里?

微前端



如上圖,這是去年畫的一張微前端的圖,其中最下面的 “公共依賴加載” 一直是沒有非常優(yōu)雅的方案。



方法一:讓每個子應(yīng)用都分開打包,主應(yīng)用不管,這樣不會有問題,但問題就是尺寸大,而且大了不是一點點。

方法二:主應(yīng)用包含 antd 和 react,子應(yīng)用如果版本一致不打包 react 和 antd,版本不一致就自己打一份,但有幾個問題:

    1. antd 和 react 是通過 umd 的方式同步載入的,主應(yīng)用初始化會比較慢;

    2. 主應(yīng)用升級了 antd 的時候,所有子應(yīng)用可能需要一起升級,這個成本就很大了。


方法三:利用 Module Federation 的 shared 能力,子應(yīng)用的依賴如果和主應(yīng)用匹配,那么,能解決方法二里的第一個問題,但第二個問題依舊解不了。

方法四:利用 Module Federation 的 remotes 能力,再提一個應(yīng)用專門提供庫被消費,看起來前面的問題都能解。



有沒有感覺技術(shù)又輪回到了 seajs + spmjs 的時代。

應(yīng)用集群

微前端是應(yīng)用集群的解法之一,但不是唯一方案。


現(xiàn)狀是,通過 npm 共享組件。



基于 Module Federation,除通過 npm 共享依賴,還可以有運行時的依賴、組件、頁面甚至應(yīng)用的直接共享。



這樣一來,靈活性就非常大了,可以在應(yīng)用的各個層面做共享。A 應(yīng)用引用 B 整個應(yīng)用,也可以應(yīng)用 B 的的頁面和組件,還可以提一個庫應(yīng)用,做 npm 依賴的運行時共享。

編譯提速,應(yīng)用秒開

我們大部分場景不是微前端或應(yīng)用集群,Module Federation 還可以幫助我們干什么?


現(xiàn)在項目組織和文件依賴通常是這樣:



現(xiàn)狀是:

  • 全部打成一個包;

  • 打包時間較慢,據(jù)統(tǒng)計,內(nèi)部云編譯平臺的平均編譯時間在 100s 以上;


期望的是:

  • node_modules 下的提前打包好,通過 runtime 的方式引;

  • 本地調(diào)試和編譯時只打項目文件;

  • 快,根據(jù)項目復(fù)雜度可提升到 1s - 7s 之內(nèi);


為什么不是其他的編譯速度優(yōu)化方案?



舉一個對比的例子,比如 external,我們之前還有做過自動的 external 方案,雖然他也可能顯著提速,但有以下問題:

  • 以空間換時間,依賴包全量引用導(dǎo)致 npm,用在生產(chǎn)上會犧牲部分產(chǎn)品體驗,需權(quán)衡;

  • 不是所有的依賴都有 umd 包,覆蓋率不夠;

  • npm 可能有依賴,比如 antd 依賴 react 和 moment,那么 react 和 moment 也得 external 并且在html 里引用他們;

  • 需要手動修改 html 里的引用,維護上有成本提升。

更多參考:


關(guān)注「Alibaba F2E」

把握阿里巴巴前端新動向

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多