<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>

          微前端場(chǎng)景下的代碼共享

          共 31097字,需瀏覽 63分鐘

           ·

          2022-11-15 01:17

          術(shù)  堅(jiān)  


          前言

          在現(xiàn)有前端應(yīng)用日益復(fù)雜化的業(yè)務(wù)場(chǎng)景下,將一個(gè)體積龐大的前端應(yīng)用拆分為幾個(gè)可以獨(dú)立開發(fā)、測(cè)試、部署的微應(yīng)用變得越來越普遍。微前端的這種模式這大大提高了我們的構(gòu)建效率,在每次構(gòu)建時(shí)我們不再需要去構(gòu)建一個(gè)龐大的應(yīng)用,而是構(gòu)建我們所需要構(gòu)建的某個(gè)子應(yīng)用。通常在一個(gè)微前端的架構(gòu)下應(yīng)用之間又會(huì)有許多公共的代碼,那么在此基礎(chǔ)上又如何更加靈活更加有效的共用這些代碼呢?(下面介紹的各種方案與微前端的場(chǎng)景并無(wú)綁定關(guān)系,只是基于這個(gè)場(chǎng)景更好去說明一些問題)。

          Common Solutions

          NPM依賴

          比較常見的做法是將公有模塊作為npm包發(fā)布,然后作為每個(gè)子應(yīng)用的依賴來引入,但實(shí)際上我們只做到了代碼層面上的共享與復(fù)用,在構(gòu)建時(shí)我們?nèi)匀粫?huì)重復(fù)打包,并沒有真正實(shí)現(xiàn)共享,而且以npm包的形式引入的話也存在一些版本問題,每次公共代碼的更新都得去所有依賴方應(yīng)用升級(jí)這個(gè)版本,發(fā)布成本較大。而且從性能上考慮,若子應(yīng)用A、B、C都為依賴方,那最終頁(yè)面上會(huì)加載三份公共npm包的代碼。


          Monorepo

          還有一個(gè)比較常見的方法就是將公共代碼也作為Monorepo下的一個(gè)子項(xiàng)目,之后將這個(gè)package作為其他應(yīng)用的dependency來import


          {
            "name""@package/a",
            "dependencies": {
               "@package/common""0.0.1"
             }
          }

          雖然不像 npm 那樣存在版本更新的問題,但是他們也有一個(gè)同樣的問題就是只做到了代碼層面上的共享與復(fù)用,實(shí)際上在構(gòu)建時(shí)還是會(huì)重復(fù)打包,在頁(yè)面性能上也會(huì)造成重復(fù)加載問題。

          Webpack DLLPlugin

          DLL(Dynamic Link Library)文件為動(dòng)態(tài)鏈接庫(kù)文件,在Windows中,許多應(yīng)用程序并不是一個(gè)完整的可執(zhí)行文件,它們被分割成一些相對(duì)獨(dú)立的動(dòng)態(tài)鏈接庫(kù),即DLL文件,放置于系統(tǒng)中。當(dāng)我們執(zhí)行某一個(gè)程序時(shí),相應(yīng)的DLL文件就會(huì)被調(diào)用。

          形象一點(diǎn)說就是,我們把一部分公用模塊抽離預(yù)先打包成動(dòng)態(tài)鏈接庫(kù),每次構(gòu)建時(shí)我們只需要構(gòu)建我們的業(yè)務(wù)代碼而不需要再去打包構(gòu)建動(dòng)態(tài)庫(kù),除非公用模塊有更新,我們才需要去對(duì)其進(jìn)行打包構(gòu)建。

          這里只做簡(jiǎn)單介紹:


          • 將需要共享的依賴打包,通過DllPlugin生成manifest.json文件描述對(duì)應(yīng)的dll文件里保存的模塊
          module.exports = {
                  resolve: {
                          extensions: [".js"".jsx"]
                  },
                  entry: {
                          alpha: ["./a""./b"]
                  },
                  output: {
                          path: path.join(__dirname, "dist"),
                          filename: "MyDll.[name].js",
                          library: "[name]_[fullhash]"
                  },
                  plugins: [
                          new webpack.DllPlugin({
                                  path: path.join(__dirname, "dist""[name]-manifest.json"),
                                  name: "[name]_[fullhash]"
                          })
                  ]
          };
          • 需要使用DLL模塊則通過manifest.json文件把依賴名稱映射成DLL模塊上對(duì)應(yīng)的模塊id來require
          {"name":"alpha_32ae439e7568b31a353c","content":{"./a.js":{"id":1,"buildMeta":{}},"./b.js":{"id":2,"buildMeta":{}}}}
          • 使用DLL Reference模塊來讀取manifest文件實(shí)現(xiàn)依賴的映射
            // webpack.config.js
            new webpack.DllReferencePlugin({
              context: path.join(__dirname, "..""dll"),
              manifest: require("../dll/dist/alpha-manifest.json")
            }),
          • 在入口html文件中插入打包生成的dll文件
          <body>
            <!--引用dll文件-->
            <script src="../dist/dll/MyDll.alpha.js" ></script>
          </body>

          可以看到這個(gè)配置成本還是蠻高的,實(shí)在不是很友好,但是確實(shí)解決了每次構(gòu)建重復(fù)打包的問題,但是和下文要介紹的external一樣也造成了一些問題,例如入口文件隨著dll文件的插入會(huì)越來越大導(dǎo)致性能的下降

          Webpack Externals

          再來看看Externals,它解決的問題與上面說到的DLLPlugin差不多,防止將某些依賴打包到bundle中,而是在運(yùn)行時(shí)再去加載這部分依賴,實(shí)現(xiàn)模塊的復(fù)用同時(shí)提高編譯速度

          • index.html
          <script src="https://code.jquery.com/jquery-3.1.0.js"integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="crossorigin="anonymous"></script>
          • webpack.config.js
          module.exports = {//...
            externals: {
              jquery: 'jQuery',},
          };
          • 剝離掉不需要改動(dòng)的模塊,下面代碼仍能正確運(yùn)行
          import $ from 'jquery';

          $('.my-element').animate(/* ... */);

          可以看到Externals比之前介紹的DLLPlugin在使用上要簡(jiǎn)單的多,同時(shí)這也是現(xiàn)在一些微前端框架依賴共享的方式(如Garfish[1])

          總結(jié)

          上文我們簡(jiǎn)單聊了四種代碼共享的解決方案,看起來Externals是最能解決我們的問題的,也是目前一些微前端框架使用的依賴共享方案,既在打包構(gòu)建上解決了重復(fù)打包的問題,提高打包效率,減小了包體積,又在頁(yè)面性能上避免了公共代碼的重復(fù)加載,但是Externals并不是那么靈活,同時(shí)也有許多問題:

          • 模塊可能獨(dú)立頁(yè)面

          如果子應(yīng)用是會(huì)作為獨(dú)立頁(yè)面的話,通過external在主應(yīng)用加載依賴就不行了,而需要手動(dòng)引入

          • 公共包不匹配

          external是比較受限制的,對(duì)于那些非常通用不需要頻繁更新的依賴可以采用這種方式(如react,react-dom),其他如Redux或者M(jìn)obx等別的依賴不一定是每個(gè)子應(yīng)用都在使用的

          • 性能差

          主模塊越來越大,加載了許多該模塊不需要的代碼導(dǎo)致首屏很慢

          所以我們是否可以更加動(dòng)態(tài)化得按需加載,而不是像external一樣在入口處加載所有公共代碼呢?那當(dāng)然是有的,就是下文將介紹的Webpack 5新特性Module Federation Plugin

          Webpack 5 Module Federation[2]

          什么是Module Federation?

          官方文檔的解釋為可以讓一個(gè)應(yīng)用與其他應(yīng)用在運(yùn)行時(shí)互相提供或者消費(fèi)各自的模塊,即它可以讓一個(gè)JS應(yīng)用在進(jìn)程中動(dòng)態(tài)地去加載其他應(yīng)用的代碼。為了更加清晰一點(diǎn),我們可以將每個(gè)模塊定義為下面三種角色:

          • Host(消費(fèi)方): 消費(fèi)其他應(yīng)用內(nèi)容的消費(fèi)方
          • Remote(被消費(fèi)方):提供部分內(nèi)容給其他應(yīng)用消費(fèi)的一方
          • Bidirectional-hosts(既是消費(fèi)方也是被消費(fèi)方): 一個(gè)既作為host消費(fèi)其他應(yīng)用的內(nèi)容,又作為remote提供給其他應(yīng)用內(nèi)容的構(gòu)建

          我們從一個(gè)官方的demo[3]開始,先簡(jiǎn)單介紹下這個(gè)demo做的事情

          complete-react-case
          ├─ component-app 組件層App,依賴lib-app,暴露一些組件,既是Remote也是Host
          │  ├─ App.jsx
          │  ├─ bootstrap.js
          │  ├─ index.js
          │  ├─ package.json
          │  ├─ public
          │  │  └─ index.html
          │  ├─ src
          │  │  ├─ Button.jsx
          │  │  ├─ Dialog.jsx
          │  │  ├─ Logo.jsx
          │  │  ├─ MF.jpeg
          │  │  ├─ ToolTip.jsx
          │  │  └─ tool-tip.css
          │  └─ webpack.config.js
          ├─ lib-app 底層App,暴露了一些基礎(chǔ)庫(kù):react, react-dom,屬于一個(gè)remote
          │  ├─ index.js
          │  ├─ package.json
          │  └─ webpack.config.js
          ├─ main-app 上層App,依賴了lib-app和component-app應(yīng)用,一個(gè)純粹的host
          │  ├─ App.jsx
          │  ├─ bootstrap.js
          │  ├─ index.js
          │  ├─ package.json
          │  ├─ public
          │  │  └─ index.html
          │  └─ webpack.config.js
          ├─ package-lock.json
          └─ package.json

          我們?cè)賮砜聪耺ain_app的代碼,因?yàn)樗鳛閔ost消費(fèi)了其他兩個(gè)應(yīng)用的代碼

          import React from 'lib-app/react';
          import Button from 'component-app/Button';
          import Dialog from 'component-app/Dialog';
          import ToolTip from 'component-app/ToolTip';
          export default class App extends React.Component {
            constructor(props) {
              super(props);
              this.state = {
                dialogVisible: false,
              };
              this.handleClick = this.handleClick.bind(this);
              this.handleSwitchVisible = this.handleSwitchVisible.bind(this);
            }
            handleClick(ev) {
              console.log(ev);
              this.setState({
                dialogVisible: true,
              });
            }
            handleSwitchVisible(visible) {
              this.setState({
                dialogVisible: visible,
              });
            }
            render() {
              return (
                <div>
                  <h1>Open Dev Tool And Focus On Network,checkout resources details</h1>
                  <p>
                    react、react-dom js files hosted on <strong>lib-app</strong>
                  </p>
                  <p>
                    components hosted on <strong>component-app</strong>
                  </p>
                  <h4>Buttons:</h4>
                  <Button type="primary" />
                  <Button type="warning" />
                  <h4>Dialog:</h4>
                  <button onClick={this.handleClick}>click me to open Dialog</button>
                  <Dialog switchVisible={this.handleSwitchVisible} visible={this.state.dialogVisible} />
                  <h4>hover me please!</h4>
                  <ToolTip content="hover me please" message="Hello,world!" />
                </div>
              );
            }
          }

          我們可以看到main_app從lib-app里引入了依賴react,又從componet-app里引入了幾個(gè)組件,通過配置Module federation plugin實(shí)現(xiàn)了跨應(yīng)用的代碼復(fù)用,而且配置也非常簡(jiǎn)單,下面會(huì)繼續(xù)介紹

          import React from 'lib-app/react';
          import Button from 'component-app/Button';
          import Dialog from 'component-app/Dialog';
          import ToolTip from 'component-app/ToolTip';

          Module federation的配置

          /**
           * lib-app/webpack.config.js
           */
           {
            plugins: [
              new ModuleFederationPlugin({
                name: 'lib-app',
                filename: 'remoteEntry.js',
                exposes: {
                  './react''react',
                  './react-dom''react-dom'
                }
              })
            ],
          }

          /**
           * component-app/webpack.config.js
           */
           {
            plugins: [
              new ModuleFederationPlugin({
                name: 'component-app',
                filename: 'remoteEntry.js',
                exposes: {
                  './Button''./src/Button.jsx',
                  './Dialog''./src/Dialog.jsx',
                  './Logo''./src/Logo.jsx',
                  './ToolTip''./src/ToolTip.jsx',
                }
                remotes: {
                  'lib-app''lib_app@http://localhost:3000/remoteEntry.js',
                },
              })
            ],
          }
           
           /**
           * main-app/webpack.config.js
           */
           {
            plugins: [
              new ModuleFederationPlugin({
                name: 'main_app',
                remotes: {
                  'lib-app''lib_app@http://localhost:3000/remoteEntry.js',
                  'component-app''component_app@http://localhost:3001/remoteEntry.js',
                },
              })
            ],
          }

          這個(gè)就是Module federation的配置,大概想表達(dá)的意思如下圖:


          頁(yè)面表現(xiàn)

          看到這里可能我們還沒啥感覺,不就是從一個(gè)應(yīng)用引另一個(gè)應(yīng)用的代碼嗎,但是接下來要介紹的頁(yè)面表現(xiàn)才是令人驚訝的地方,我們來看下main_app的頁(yè)面文件加載

          可以看到文件的加載順序?yàn)椋?/p>

          1. main.js (主模塊)
          1. remoteEntry.js(指向lib-app 113B)
          1. remoteEntry.js(指向component-app 113B)
          1. bootstrap_js.js(主模塊啟動(dòng)文件)
          1. lib-app/react
          1. lib-app/react-dom
          1. component-app/Button
          1. component-app/Dialog
          1. component-app/Tooltip

          這里我們就能看到幾個(gè)非常吸引人的點(diǎn)了

          Host模塊

          main_app的外部依賴不會(huì)被打包進(jìn)入主模塊的main.js,解決了隨著公共代碼使用的增多導(dǎo)致main.js越來越大的問題,其次我們可以看到lib-app暴露的兩個(gè)依賴(react, react-dom)及component-app暴露的組件(Button,Dialog及ToolTip)是分開加載的,作為不同的文件進(jìn)行了構(gòu)建(實(shí)際看build生成也是如此),在動(dòng)態(tài)加載下可以進(jìn)一步提高頁(yè)面性能

          舉個(gè)例子,我們將上面main_app稍作改造,懶加載Dialog

          const Dialog = React.lazy(() => import('component-app/Dialog'));

          <button onClick={this.handleClick}>click me to open Dialog</button>
          {this.state.dialogVisible && (
            <React.Suspense fallback={null}>
              <Dialog switchVisible={this.handleSwitchVisible} visible={this.state.dialogVisible} />
            </React.Suspense>
          )}

          看下頁(yè)面表現(xiàn):


          Remote模塊

          那么Remote模塊的性能又會(huì)如何呢?隨著公共代碼的增多會(huì)不會(huì)影響Remote模塊的頁(yè)面性能?

          暴露出去的每個(gè)外部模塊都是單獨(dú)打包,而不是全部打在remote模塊的入口文件main.js里,如果沒有使用這些代碼的話并不會(huì)加載,因此被消費(fèi)方并不會(huì)因?yàn)楣泊a的增多而影響到頁(yè)面性能

          源碼解析

          加載main_app的入口文件main.js

          首先我們來簡(jiǎn)單看看作為消費(fèi)方的main_app入口文件main.js里webpack_modules的定義

          var __webpack_modules__ = ({

          /***/ "webpack/container/reference/component-app":
          /*!*********************************************************************!*\
            !*** external "component_app@http://localhost:3001/remoteEntry.js" ***!
            *********************************************************************/
          /***/ ((module, __unused_webpack_exports, __webpack_require__) => {

          "use strict";
          var __webpack_error__ = new Error();
          module.exports = new Promise((resolve, reject) => {
              if(typeof component_app !== "undefined"return resolve();
              __webpack_require__.l("http://localhost:3001/remoteEntry.js", (event) => {
                  if(typeof component_app !== "undefined"return resolve();
                  var errorType = event && (event.type === 'load' ? 'missing' : event.type);
                  var realSrc = event && event.target && event.target.src;
                  __webpack_error__.message = 'Loading script failed.\n(' + errorType + ': ' + realSrc + ')';
                  __webpack_error__.name = 'ScriptExternalLoadError';
                  __webpack_error__.type = errorType;
                  __webpack_error__.request = realSrc;
                  reject(__webpack_error__);
              }, "component_app");
          }).then(() => (component_app));

          /***/ "webpack/container/reference/lib-app": //.... 與component-app類似
          /***/ }),

          入口文件定義的webpack_modules即為兩個(gè)被消費(fèi)應(yīng)用的remoteEntry文件,這個(gè)文件看起來就像DLL Plugin里的mainfest.json文件一樣鏈接起了這些應(yīng)用,讓我們隨著頁(yè)面的文件加載流程接著往下看

          加載啟動(dòng)文件bootstrap.js

          (() => {
          /*!******************!*\
            !*** ./index.js ***!
            ******************/
          __webpack_require__.e(/*! import() */ "bootstrap_js").then(__webpack_require__.bind(__webpack_require__, /*! ./bootstrap.js */ "./bootstrap.js"));
          })();

          加載完main.js文件后就要開始加載啟動(dòng)文件bootstrap.js了,從之前的加載過程我們看到remoteEntry文件是先于啟動(dòng)文件bootstrap文件的,看上面的代碼這一步應(yīng)該發(fā)生在__webpack_require__.e中,在remoteEntry文件執(zhí)行完后再執(zhí)行bootstrap.js,確保了依賴的前置

          webpack_require.e

          __webpack_require__.e = (chunkId) => {
            return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
               __webpack_require__.f[key](chunkId, promises "key");
               return promises;
            }, []));
          };

          這里實(shí)際調(diào)用了__webpack_require__.f上的函數(shù),生成一個(gè)promise數(shù)組,并resolve所有的promise

          webpack_require.f

          接下來看下__webpack_require__.f上的函數(shù)

          • webpack_require.f.remotes
          /* webpack/runtime/remotes loading */

          /******/        var chunkMapping = {
          /******/            "bootstrap_js": [
          /******/                "webpack/container/remote/lib-app/react",
          /******/                "webpack/container/remote/component-app/Button",
          /******/                "webpack/container/remote/component-app/Dialog",
          /******/                "webpack/container/remote/component-app/ToolTip",
          /******/                "webpack/container/remote/lib-app/react-dom"
          /******/            ]
          /******/        };
          /******/        var idToExternalAndNameMapping = {
          /******/            "webpack/container/remote/lib-app/react": [
          /******/                "default",
          /******/                "./react",
          /******/                "webpack/container/reference/lib-app"
          /******/            ],
          /******/            "webpack/container/remote/component-app/Button": [
          /******/                "default",
          /******/                "./Button",
          /******/                "webpack/container/reference/component-app"
          /******/            ],
          /******/            "webpack/container/remote/component-app/Dialog": [
          /******/                "default",
          /******/                "./Dialog",
          /******/                "webpack/container/reference/component-app"
          /******/            ],
          /******/            "webpack/container/remote/component-app/ToolTip": [
          /******/                "default",
          /******/                "./ToolTip",
          /******/                "webpack/container/reference/component-app"
          /******/            ],
          /******/            "webpack/container/remote/lib-app/react-dom": [
          /******/                "default",
          /******/                "./react-dom",
          /******/                "webpack/container/reference/lib-app"
          /******/            ]
          /******/        };

          /******/        __webpack_require__.f.remotes = (chunkId, promises) => {
          /******/                    var handleFunction = (fn, arg1, arg2, d, next, first) => {
          /******/                        try {
          /******/                            var promise = fn(arg1, arg2);
          /******/                            if(promise && promise.then) {
          /******/                                var p = promise.then((result) => (next(result, d)), onError);
          /******/                                if(first) promises.push(data.p = p); else return p;
          /******/                            } else {
          /******/                                return next(promise, d, first);
          /******/                            }
          /******/                        } catch(error) {
          /******/                            onError(error);
          /******/                        }
          /******/                    }
          /******/                    var onExternal = (external, _, first) => (external ? handleFunction(__webpack_require__.I, data[0], 0, external, onInitialized, first) : onError());
          /******/                    var onInitialized = (_, external, first) => (handleFunction(external.get, data[1], getScope, 0, onFactory, first));
          /******/                    var onFactory = (factory) => {
          /******/                        data.p = 1;
          /******/                        __webpack_require__.m[id] = (module) => {
          /******/                            module.exports = factory();
          /******/                        }
          /******/                    };
          /******/                    handleFunction(__webpack_require__, data[2], 0, 0, onExternal, 1);
          /******/                });
          /******/            }
          /******/        }

          這里的代碼量比較多,簡(jiǎn)單來說就是通過用當(dāng)前模塊id通過Map找到所依賴remote模塊id再添加具體的依賴到webpack_modules中。

          以Button為例,當(dāng)我們加載 src_bootstrap_js 這個(gè) chunk 時(shí),經(jīng)過 remotes,發(fā)現(xiàn)這個(gè) chunk 依賴了component-app/Button

          var chunkMapping = {
              "bootstrap_js": [
                  "webpack/container/remote/component-app/Button",
                  //...
              ]
          };
          var idToExternalAndNameMapping = {
             "webpack/container/remote/component-app/Button": [
             "default",
             "./Button",
             "webpack/container/reference/component-app"
             ],
             //...
           };
           
           
           // 最終結(jié)果可以簡(jiǎn)化為下面這段代碼
            __webpack_require__.m[id] = (module) => {
                  module.exports = __webpack_require__( "webpack/container/reference/component-app").then(componet-app => component-app.get('./Button'))
               }
            }

          可以看出我們對(duì)遠(yuǎn)程模塊componet-app上button的使用其實(shí)就是調(diào)用component-app.get('./Button'),這貌似就是使用了個(gè)全局變量的get方法?這些又是在哪里定義的呢,就是remoteEntry文件!

          • remoteEntry.js

          看下component-app的remoteEntry.js文件,把整個(gè)代碼簡(jiǎn)化一下

           // 定義全局變量
          var component_app;
          (() => { //....
          (() => {
          var exports = __webpack_exports__;
          /*!***********************!*\
            !*** container entry ***!
            ***********************/
          // 生成暴露出去的模塊Map
          var moduleMap = {
              "./Button": () => {
                  return Promise.all([__webpack_require__.e("webpack_container_remote_lib-app_react"), __webpack_require__.e("src_Button_jsx")]).then(() => (() => ((__webpack_require__(/*! ./src/Button.jsx */ "./src/Button.jsx")))));
              },
              "./Dialog": () => {
                  return Promise.all([__webpack_require__.e("webpack_container_remote_lib-app_react"), __webpack_require__.e("src_Dialog_jsx")]).then(() => (() => ((__webpack_require__(/*! ./src/Dialog.jsx */ "./src/Dialog.jsx")))));
              },
              "./Logo": () => {
                  return Promise.all([__webpack_require__.e("webpack_container_remote_lib-app_react"), __webpack_require__.e("src_Logo_jsx")]).then(() => (() => ((__webpack_require__(/*! ./src/Logo.jsx */ "./src/Logo.jsx")))));
              },
              "./ToolTip": () => {
                  return Promise.all([__webpack_require__.e("webpack_container_remote_lib-app_react"), __webpack_require__.e("src_ToolTip_jsx")]).then(() => (() => ((__webpack_require__(/*! ./src/ToolTip.jsx */ "./src/ToolTip.jsx")))));
              }
          };

          // 定義Get方法
          var get = (module, getScope) => {
              __webpack_require__.R = getScope;
              getScope = (
                  __webpack_require__.o(moduleMap, module)
                      ? moduleMap[module]( "module")
                      : Promise.resolve().then(() => {
                          throw new Error('Module "' + module + '" does not exist in container.');
                      })
              );
              __webpack_require__.R = undefined;
              return getScope;
          };

          // 定義init方法
          var init = (shareScope, initScope) => {
              if (!__webpack_require__.S) return;
              var name = "default"
              var oldScope = __webpack_require__.S[name];
              if(oldScope && oldScope !== shareScope) throw new Error("Container initialization failed as it has already been initialized with a different share scope");
              __webpack_require__.S[name] = shareScope;
              return __webpack_require__.I(name, initScope);
          };

          // 在component_app上定義get, init方法
          __webpack_require__.d(exports, {
              get: () => (get),
              init: () => (init)
          });
          })();

          component_app = __webpack_exports__;
          })();

          總結(jié)來說這個(gè)文件定義了全局的 component-app變量,然后提供了一個(gè) get 函數(shù),通過get函數(shù)來加載moduleMap里具體的模塊,即通過全局變量鏈接了兩個(gè)應(yīng)用。

          • webpack_require.f.j

          webpack_require.f上的另一個(gè)方法,也是實(shí)際加載模塊的方法,這里加載了bootstrap.js文件,這個(gè)函數(shù)沒啥好說的,就是webpackJsonp異步加載,但也正因?yàn)槿绱瞬疟WC了文件的加載順序

          加載流程

          1. 加載應(yīng)用入口文件main.js
          1. 準(zhǔn)備加載啟動(dòng)文件bootstap.js
          1. 加載啟動(dòng)文件前發(fā)現(xiàn)有依賴的remotes模塊
          1. 動(dòng)態(tài)加載依賴的remotes模塊
          1. 加載執(zhí)行完所有的前置依賴后再加載bootstrap.js
          1. 所有依賴加載完成,再執(zhí)行then邏輯,即webpack_require( "./bootstrap.js")

          在啟動(dòng)應(yīng)用時(shí),進(jìn)行了依賴的前置分析,通過生成的remoteEntry文件內(nèi)的全局變量來get依賴的內(nèi)容,最后再執(zhí)行業(yè)務(wù)代碼。

          與微前端的結(jié)合

          對(duì)比微前端框架現(xiàn)在普遍使用的Externals方式,顯然Module Federation是個(gè)更好的解決方案,

          • 主應(yīng)用

          通過主應(yīng)用可以導(dǎo)出一些公共庫(kù)及公共組件等,但并不影響主應(yīng)用頁(yè)面性能

          • 子應(yīng)用

          動(dòng)態(tài)加載主應(yīng)用暴露的公共庫(kù)及公共組件,減小了入口文件的大小,提高了頁(yè)面性能

          總結(jié)

          Module Federation Plugin通過提供鏈接文件remoteEntry.js的cdn地址即可將不同的應(yīng)用連接起來,不僅局限于微前端場(chǎng)景,即使不同的項(xiàng)目工程也可以讓我們輕松做到更細(xì)粒度的代碼共享。

          ?? 謝謝支持

          如果想學(xué)習(xí)更多H5游戲webpacknode,gulp,css3,javascript,nodeJScanvas數(shù)據(jù)可視化等前端知識(shí)和實(shí)戰(zhàn),歡迎在《趣談前端》加入我們的技術(shù)群一起學(xué)習(xí)討論,共同探索前端的邊界。


          從零搭建全??梢暬笃林谱髌脚_(tái)V6.Dooring

          從零設(shè)計(jì)可視化大屏搭建引擎

          Dooring可視化搭建平臺(tái)數(shù)據(jù)源設(shè)計(jì)剖析

          可視化搭建的一些思考和實(shí)踐

          基于Koa + React + TS從零開發(fā)全棧文檔編輯器(進(jìn)階實(shí)戰(zhàn)




          點(diǎn)個(gè)在看你最好看


          瀏覽 83
          點(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>
                  五月丁香色婷婷 | 伊人大香蕉精品在线网 | 玖玖在线| 五月激情视频 | 亚洲精品黄色电影网站 |