, explore new features webpack5 Module federation in tencent document application | AlloyTeam author: TAT. Jay
Preface:
Webpack5’s exciting new feature, Module federation, may not excite many developers, but for Tencent docs, which have suffered from multiple apps, it’s a real surprise. This article will take you through Tencent’s documentation dilemma and how Module Federation can help us out of it.
0x1 Tencent documentation is in trouble
1.1 Multi-application Scenario Background
From the functional level of Tencent documents, users are most familiar with the four categories of Word, Excel, PPT and forms, which are independent of each other and may be developed and maintained by different teams. From the perspective of developers, the four categories and four warehouses are maintained independently, which seems to be very simple. But the reality is much more complicated. Here’s a scene:
Notify the center of its needs
For complex permission scenarios, we actually have a requirement for a notification center that looks roughly like the one in the picture above, in order to enable users to get the latest status quickly. Here is the entry seen in the list page of Tencent document. In fact, in the four categories mentioned above, such a page needs to be embedded.
In order to minimize the development and maintenance costs here, it is surely best to have a common set of code for each class, and the easiest thing to think of is to introduce it in a separate NPM package. It is true that many of the internal functions of Tencent Docs are now introduced using the NPM package, but there are actually some problems:
Problem one: Historical code
The history of Tencent documentation is very complicated, in short, at the beginning, there was no support for writing ES6 in the code, so there was no way to introduce the NPM package. It is not realistic to think that the transformation can be completed in a short time, and the product requirements will not wait for you to complete such transformation
Issue 2: Release efficiency
The problem here is actually the same with the NPM package we use today, but we are lazy and opportunistic. When introduced as an NPM package, you need to change 5 warehouses (4 categories + list page) to upgrade the version here once changes are made, which is actually quite expensive to release and painful for developers
1.2 Our solutions
In order to quickly introduce React to accelerate requirements development in environments that do not support ES6 code, we came up with a pattern called script-Loader (SL).
The overall architecture is shown as follows:
Is simply, reference jquery way of introduction, we use another project to achieve these functions, and then packaged into ES5 code, the code provides a lot of interface, and then in each category page, the introduction of the load scripts, we offer internal will automatically go to load the file, each module of the js file for CDN address and load. In this way, each module is independent, and all modules and each category form independent.
In this mode, we only need to publish the changed modules and the latest configuration files for each release, and the other categories will be automatically updated.
This pattern is not necessarily suitable for every project, nor is it necessarily the best solution. From the current point of view, it is a bit like the concept of a micro front end, but it is actually different and will not be expanded here. This mode can indeed solve the Tencent document this multi-application reuse code needs.
1.3 Problems encountered
There is no serious problem with this mode at present, but there is a pain point that has been bothering us, that is, the code sharing problem between category code and SL. Here’s an example:
React was used after Excel category transformation. React was introduced into MODULES A, B and C of SL
Because between SL module is independent, so the React is also their packaging, that is to say, when you open the Excel if you use the module A, B, C, then you will the final page four React to load code, will not bring any problems, but for have A pursuit of the front end of the we still want to solve this problem.
Solution: External
For React, we can configure the React class as External by default, so we will not package React. However, the situation is not so simple:
Problem one: Modules may have separate pages
As for the notification center above, it is not embedded on mobile devices and has a separate page, so you need to manually introduce React to this separate page
Problem 2: Public packages do not match
Simply put, sl-dependent packages that may not be used in the category, such as Mobx or Redux
Problem 3: Not all packages can be configured with External directly
React packages can be shared by setting External to window.react, but not all packages can be shared by setting External to window.react
Based on these problems, our current choice is a compromise solution. We extract the packages that can configure the global environment, and each module indicates the dependency. Then, in SL, the dependency will be detected before loading the module code, and the actual module code will be loaded and executed after loading the dependency code.
The problem with this approach is that you need to manually maintain such dependencies. Each shared package actually needs to be packaged into a separate CDN file so that you can have a bottom-load file if dependency detection fails. Therefore, only the React package currently does this sharing.
So at this point, the core question becomes category code and how does SL do thatCode sharing
. For other projects, it’s really multiple applications how do you do thatCode sharing
.
0x2 Packaging principles of WebPack
To solve the above problem, we actually want to start with WebPack and implement such a plug-in to help us solve this problem. The core idea is the internal require function of Hook Webpack. Before that, let’s take a look at some principles of webpack packaging. This is also the core of Module Federation. If you are familiar with this, you can also quickly skip to section 3, but those of you who are not familiar with it are advised to read it carefully.
2.1 the chunk and the module
There are two core concepts in Webpack, called chunk and module, here for the sake of simplicity, only look at js related, with the author’s own understanding to explain their direct differences:
Module: Each source JS file can be viewed as a module
Chunk: Each JS file that is packaged and landed is actually a chunk, and each chunk contains many modules
The default number of chunks is actually determined by the number of JS in your entry file, but if you configure dynamic loading or extraction of common packages, new chunks will also be generated.
2.2 Interpretation of packaging code
With a basic understanding, we need to understand how webPack wrapped code loads and executes on the browser side. To do this, let’s prepare a very simple demo and take a look at its generation file.
src
---main.js
---moduleA.js
---moduleB.js
/**
* moduleA.js
*/
export default function testA() {
console.log('this is A');
}
/**
* main.js
*/
import testA from './moduleA';
testA();
import('./moduleB').then(module= >{});Copy the code
Modulea. js is imported directly and moduleb. js is dynamically imported. The resulting files are two chunks:
main.js
andmoduleA.js
Consisting of abundle.js
- “moduleB.js
Consisting of a
0.bundle.js`
If you are familiar with the underlying principles of webpack, you will know that the mainTemplate and chunkTemplate are rendered separately, so we will continue to read the generated code
What happens to import
The entire main.js code, packaged, looks like this
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */
var _moduleA__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__( / *! ./moduleA */ "./src/moduleA.js");
Object(_moduleA__WEBPACK_IMPORTED_MODULE_0__["default") (); __webpack_require__.e(/ *! import() */ 0).then(__webpack_require__.bind(null./ *! ./moduleB */ "./src/moduleB.js")).then(module= >{}); })Copy the code
As you can see, our direct import moduleA will eventually become webpack_require, which is a core function of the webpack package that resolves dependency introduction.
How is webpack_require implemented
Let’s look at how webpack_require is implemented:
function __webpack_require__(moduleId) {
// Check if module is in cache
// Check whether the module has been loaded, if it has been loaded directly return
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
// If an import module is being loaded for the first time, it must not have been loaded before and the loading process will be performed
var module = installedModules[moduleId] = {
i: moduleId,
l: false.exports: {}};// Execute the module function
modules[moduleId].call(module.exports, module.module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
Copy the code
If you want to simplify the implementation, it’s pretty simple: every time require, go to the cache map of installedModules to see if it’s loaded. If it’s not loaded, load it from the map of modules.
Where does Modules come from
Modules is a very important map of modules. Let’s simplify the js generated by bundle.js.
(function (modules) ({{})"./src/main.js": (function (module, __webpack_exports__, __webpack_require__) {}),
"./src/moduleA.js": (function (module, __webpack_exports__, __webpack_require__) {})});Copy the code
Modules is an input parameter to the function, and the values are all modules that we contain. You’ll get a sense of how a chunk is loaded and how chunk contains modules.
How does dynamic import work
The chunk above is a js file, so there is no problem maintaining its own local modules, but dynamic import will generate a new JS file, so the new js file 0.bundle.js will also have its own modules. How does bundle.js know about modules in 0.bundle.js?
Let’s look at what the code for dynamic import looks like:
__webpack_require__.e( /*! import() */ 0).then(__webpack_require__.bind(null, /*! ./moduleB */ "./src/moduleB.js")).then(module => {
});
Copy the code
In code, there’s a layer of webpcK_require. e, and then it’s a promise, and then it implements webpack_require.
In fact, webpck_require.e is used to load chunk js file 0.bundle.js.
When loaded, it assumes that modules in **bundle.js must have modules in 0.bundle.js. How does this work?
Let’s take a look at what 0. Bundle.js is all about and make it magic:
(window["webpackJsonp"] = window["webpackJsonp"] || []).push(
[
[0] and {"./src/moduleB.js": (function (module, __webpack_exports__, __webpack_require__) {}}));Copy the code
Jsonp is not a function. Instead, it pushes its module ID and modules into a global array. It would seem that the core of the magic is in bundle.js, and it is.
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] | | [];var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback;
jsonpArray = jsonpArray.slice();
for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
var parentJsonpFunction = oldJsonpFunction;
Copy the code
In bundle.js, we’ve hijacked the push function, and as soon as the loading of 0.bundle.js is complete, we’ll execute it, and we’ll get all the arguments. Then add all modules in 0.bundle.js to your modules!
2.3 Summary
If you don’t quite understand it, read the above code several times with the image below.
For mainChunk files, we maintain a map of all modules like modules and provide functions like webpack_require. For chunkA files (either generated by extracting common code or dynamically loaded), we use a jSONp-like approach to have it add all its modules to the host chunk’s modules.
2.4 How to solve the problem of Tencent documents?
Based on such an understanding, we are thinking, can the multi-application code sharing of Tencent documents be solved?
The actual scene of Tencent document is as follows:
Since it is an independent project, webpack also has two mainchunks and then their respective chunks (actually, there will be chunk overwriting or module overwriting in chunk, so the ID should be md5).
So the core of the problem is how to get through modules of two Mainchunks?
If it is free programming, I think we can achieve too many ways, but under the constraints of the Framework of Webpack, how to quickly achieve this, we have been thinking about the scheme, the current scheme is as follows:
Webpack_require in SL module is hack, every time we can’t find it in modules, we go to Excel modules to find it, so we need Excel modules as a global variable
But what do we do with modules that Excel doesn’t exist?
This is obviously a runtime environment, and we need to degrade the loading failure, but then we will encounter the synchronous to asynchronous problem. Originally, you imported a module synchronously, but if it does not exist in Excel modules, you need to load the chunk corresponding to the module first. It’s like dynamic loading, but your code is still synchronized, so that’s a problem.
Therefore, we need to preload the dependencies, that is, after loading the SL module, it knows which shared modules it depends on, and then detects whether there are any. If there are no shared modules, it will load them successively, and then execute itself after all the dependencies are in place.
0x3 webpack5的Module federation
To be honest, the webpack underlayer was quite complex, unfamiliar and uncertain, so we were slow to actually do it. However, I came across Module federation of webpack5 by chance. After reading the description, I felt it was very similar to what we wanted, so we started to explore it.
3.1 Introduction to Module Federation
What is Module federation and what does it do
Module federation allows a JavaScript application to dynamically run code from another bundle/build, on both client and server
Simply put, this allows the runtime to dynamically determine the introduction and loading of code.
3.2 Module federation的demo
The most important thing we care about is how Module Federation is implemented to determine if it is really suitable for Tencent documentation.
Here we use the existing demo:
module-federation-examples/basic-host-remote
But before I do that, I need to tell you what this demo does
App1 --index.js entry file --bootstrap.js startup file -- app. js React component app2 --index.js entry file --bootstrap.js startup file -- app.js React component - button.js React componentCopy the code
So this is the file structure, but you can actually view it as two separate apps app1 and App2, so what was their love and hate before?
/** app1 **/
/**
* index.js
**/
import('./bootstrap');
/**
* bootstrap.js
**/
import('./bootstrap');
import App from "./App";
import React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<App />, document.getElementById("root"));
/**
* App.js
**/
import('./bootstrap');
import React from "react";
import RemoteButton from 'app2/Button';
const App = () => (
<div>
<h1>Basic Host-Remote</h1>
<h2>App 1</h2>
<React.Suspense fallback="Loading Button">
<RemoteButton />
</React.Suspense>
</div>
);
export default App;
Copy the code
Here I only posted app1 JS code, app2 code you do not need to care about. There’s nothing special about the code, just one thing, inside app.js in app1:
import RemoteButton from 'app2/Button';
Copy the code
The key is to reuse code across applications! The app1 code uses the app2 code, but what does the code end up looking like? How is app2 code introduced?
3.3 Configuring Module Federation
Let’s start with how our WebPack needs to be configured:
/** * app1/webpack.js */
{
plugins: [
new ModuleFederationPlugin({
name: "app1".library: {
type: "var".name: "app1"
},
remotes: {
app2: "app2"
},
shared: ["react"."react-dom"]]}})Copy the code
Module federation = Module federation = Module federation
- It uses the remote module APP2, called app2
- It uses a shared module called Shared
There are a few differences between Remotes and Shared.
Generated HTML file:
<html>
<head>
<script src="app2/remoteEntry.js"></script>
</head>
<body>
<div id="root"></div>
<script src="app1/app1.js"></script><script src="app1/main.js"></script></body>
</html>
Copy the code
Ps: there is a change in the js path, this can be configured, it just indicates which JS files are loaded from where
App1 packages generated files:
app1/index.html
app1/app1.js
app1/main.js
app1/react.js
app1/react-dom.js
app1/src_bootstrap.js
Copy the code
Ps: App2 you also need to package, but I did not post the code and configuration file of app2, I will post it later when needed
Final page representation and loaded JS:
Loading js sequence from top to bottom is actually very careful, behind will be the key to decrypt:
app2/remoteEntry.js
app1/app1.js
app1/main.js
app1/react.js
app1/react-dom.js
app2/src_button_js.js
app1/src_bootstrap.js
Copy the code
The most important thing to focus on here is where each file is loaded from. Before going into the mechanics of file loading, we have at least a few conclusions:
- The remotes code does not package itself, like external. For example, app2/ Button is the code that loads the app2 package
- Shared code is packaged itself
How Module Federation works
Before explaining how this works, I’ll show you the previous image, because this is the core of the WebPack file module, and it hasn’t changed even with upgrade 5
App1 and App2 still have their own modules, so the key to implementation is how to synchronize the two modules, or how to inject them. Let’s look at how Module federation works.
3.3.1 What has import become
/ / the import source
import RemoteButton from 'app2/Button';
// import package code in app1/src_bootstrap.js
/* harmony import */
var app2_Button__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__( / *! app2/Button */ "? ad8d");
/* harmony import */
var app2_Button__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/ __webpack_require__.n(app2_Button__WEBPACK_IMPORTED_MODULE_1__);
Copy the code
So from here, we don’t really see anything, because it’s still normal webpack_require, does it actually rewrite webpack_require as we thought it would?
Unfortunately, there is no change to this function from the source code, so the core point is not here.
But look at the order in which js is loaded:
app2/remoteEntry.js
app1/app1.js
app1/main.js
app1/react.js
app1/react-dom.js
app2/src_button_js.js // The app2 button was loaded first, before our own startup file
app1/src_bootstrap.js
Copy the code
Recall our own analysis in the last section
Therefore, we need to preload the dependencies, that is, after loading the SL module, it knows which shared modules it depends on, and then detects whether there are any. If there are no shared modules, it loads them successively, so it starts to execute itself after the dependencies are in place.
So is it solved by relying on prepositions?
3.3.2 Contents of the main.js file
Because there are only two files in HTML associated with app1: app1/app1.js and app1/main.js
So let’s see what main.js actually says
((a)= > { // webpackBootstrap
var __webpack_modules__ = ({})
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
if (__webpack_module_cache__[moduleId]) {
return __webpack_module_cache__[moduleId].exports;
}
var module = __webpack_module_cache__[moduleId] = {
exports: {}}; __webpack_modules__[moduleId](module.module.exports, __webpack_require__);
return module.exports;
}
__webpack_require__.m = __webpack_modules__;
__webpack_require__("./src/index.js"); }) ()Copy the code
You can see that there is little difference, just changing the previous modules to webpack_modules, and then changing the initialization of the modules from parameters to internal declared variables.
Let’s look at the internal implementation of webpack_modules:
var __webpack_modules__ = ({
"./src/index.js": ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) = > {
__webpack_require__.e( / *! import() */ "src_bootstrap_js").then(__webpack_require__.bind(__webpack_require__, / *! ./bootstrap */ "./src/bootstrap.js"));
}),
"container-reference/app2": ((module) = > {
"use strict";
module.exports = app2;
}),
"? 8bfd": ((module, __unused_webpack_exports, __webpack_require__) = > {
"use strict";
var external = __webpack_require__("container-reference/app2");
module.exports = external; })});Copy the code
The code looks like there are three modules:
./ SRC /index.js This looks like our app1/index.js, In dynamic loading the bootstrap. Js corresponding the chunk src_bootstrap_js container - reference/app2 directly returns a global app2, feel with our app2 here?8The BFD string is the file reference ID of the app2/button we mentioned aboveCopy the code
Who made the react files and app2/button files loaded before src_bootstrap.js? Through debug, we find that the secret is in the webpack_require__.e(“src_bootstrap_js”) sentence
In section 2, when parsing the WebPack load, we learned:
In fact, webpck_require.e loads the chunk js file 0.bundle.js, and when it’s loaded back, it assumes that modules in bundle.js must have the modules contained in 0.bundle.js
So the original webpack_require__.e was plain, loading a script that we didn’t even want to post, but with this update everything is different, it’s the key of the keys!
3.3.3 What does webpack_require__.e do
__webpack_require__.e = (chunkId) = > {
return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) = > {
__webpack_require__.f[key](chunkId, promises);
returnpromises; } [])); };Copy the code
If you look at the code, it does change. Now the bottom line is to call the function above webpack_require.f, and wait until all the functions are completed before executing the promise then
Webpack_require. f: webpack_require.f: webpack_require.f: webpack_require.f: webpack_require.f
A: overridables
/* webpack/runtime/overridables */
__webpack_require__.O = {};
var chunkMapping = {
"src_bootstrap_js": [
"? a75e"."? 6365"]};var idToNameMapping = {
"? a75e": "react-dom"."? 6365": "react"
};
var fallbackMapping = {
"? a75e": (a)= > {
return __webpack_require__.e("vendors-node_modules_react-dom_index_js").then((a)= > () => __webpack_require__("./node_modules/react-dom/index.js"))},"? 6365": (a)= > {
return __webpack_require__.e("vendors-node_modules_react_index_js").then((a)= > () => __webpack_require__("./node_modules/react/index.js"))}}; __webpack_require__.f.overridables =(chunkId, promises) = > {}
Copy the code
Two: the remotes
/* webpack/runtime/remotes loading */
var chunkMapping = {
"src_bootstrap_js": [
"? ad8d"]};var idToExternalAndNameMapping = {
"? ad8d": [
"? 8bfd"."Button"]}; __webpack_require__.f.remotes =(chunkId, promises) = > {}
Copy the code
3: the json
/* webpack/runtime/jsonp chunk loading */
var installedChunks = {
"main": 0
};
__webpack_require__.f.j = (chunkId, promises) = > {}
Copy the code
These three functions I have excerpted the core part, in fact, the annotation is also written more clearly, I still explain:
- If you look at the code, you should already know that the shared configuration is involved
- Remotes is remote, and the code is clearly relevant to the Remotes configuration
- Jsonp this is the original loading chunk function, corresponding to the previous lazy loading or common code extraction
3.3.4 Loading Process
After knowing the core in webpack_require.e and internal implementation, I don’t know if you have a certain idea of the whole loading process in your mind, if not, let me give you a breakdown
- Src_main.js is loaded in HTML
- Webpack_require (“./ SRC /index.js”)
- SRC /index.js dynamically loads chunk srC_bootSTRap_js
- When loading src_bootstrap_js chunk dynamically, the overridables find that this chunk depends on react and react-dom. If not, load the corresponding JS file. The address is also told
- When loading src_bootSTRap_js chunk dynamically, the chunk is found to be dependent by remotes. Ad8d, then load this JS
- The chunk srC_bootSTRap_js is loaded dynamically through JSONP
- Webpack_require src_bootstrap_js module:./ SRC /bootstrap.js
At this point, everything starts up normally, just like the dependency preloading we mentioned earlier, analyzing, generating, and loading the configuration file.
It all looks great, but there is one key piece of information that remains unsolved!
3.3.5 How do I Know the Existence of App2
When loading the React file in step 4 above, we can load a copy of the react file and know the address because we have actually packed the react file ourselves
But at step 5, when the page has never loaded app2/Button, where do we load the file?
We’ll use webpack_modules in main.js
var __webpack_modules__ = ({
"container-reference/app2":
((module) = > {
"use strict";
module.exports = app2;
}),
"? 8bfd":
((module, __unused_webpack_exports, __webpack_require__) = > {
"use strict";
var external = __webpack_require__("container-reference/app2");
module.exports = external; })});Copy the code
There are three modules in it. What more do we have? 8BFD/container reference/app2 is not used. Let’s look at remotes implementation
/* webpack/runtime/remotes loading */
var chunkMapping = {
"src_bootstrap_js": [
"? ad8d"]};var idToExternalAndNameMapping = {
"? ad8d": [
"? 8bfd"."Button"]}; __webpack_require__.f.remotes =(chunkId, promises) = > {
if (__webpack_require__.o(chunkMapping, chunkId)) {
chunkMapping[chunkId].forEach((id) = > {
if (__webpack_modules__[id]) return;
var data = idToExternalAndNameMapping[id];
promises.push(Promise.resolve(__webpack_require__(data[0]).get(data[1])).then((factory) = > {
__webpack_modules__[id] = (module) = > {
module.exports = factory(); }}}))); }}Copy the code
When we load src_bootSTRap_js chunk, after remotes, we find that the chunk is dependent. Ad8d, which at runtime:
id = "? 8bfd"
data = [
"? 8bfd"."Button"
]
/ / the source code
__webpack_require__(data[0]).get(data[1])
/ / runtime
__webpack_require__('? 8bfd').get("Button")
Copy the code
Module with main.js? App2.get (“Button”)
Isn’t that a global variable? It looks a bit strange!
3.3.6 look at app2 / remoteEntry. Js
We seem to have been ignoring this file, it is the first to load, must have its role, with the global app2 what strange question, we went to see this file, and indeed found the mystery!
var app2;
app2 =
(() = > {
"use strict";
var __webpack_modules__ = ({
"? 8619": ((__unused_webpack_module, exports, __webpack_require__) = > {
var moduleMap = {
"Button": (a)= > {
return __webpack_require__.e("src_Button_js").then((a)= > () => __webpack_require__( / *! ./src/Button */ "./src/Button.js")); }};var get = (module) = > {
return (
__webpack_require__.o(moduleMap, module)? moduleMap[module] () :Promise.resolve().then((a)= > {
throw new Error("Module " + module + " does not exist in container."); })); };var override = (override) = > {
Object.assign(__webpack_require__.O, override);
}
__webpack_require__.d(exports, {
get: (a)= > get,
override: (a)= >override }); })});return __webpack_require__("? 8619"); }) ()Copy the code
If you look closely, you’ll see that this file defines the global app2 variable and then provides a get function that actually loads the specific module
So app2.get(“Button”) here becomes the internal get function defined by App2, which then executes its own webpack_require
Don’t you feel like you’ve been enlightened?!
This is how it creates a rainbow bridge between two separate packaged applications using global variables!
Of course, app2/ remoteentry. js is packaged by App2 according to the configuration, which is actually generated from the exported module of the configuration file
You may have missed bootstrap.js
Careful readers will notice that there is an extra bootstrap.js between the entry file index.js and the actual file app.js, and the contents of the bootstrap.js are asynchronous loading of app.js
Is this file redundant? I tried to change the entry to app.js or synchronous loading here, and the whole application would not work
In fact, from the principle of analysis is understandable:
Since dependencies need to be preloaded, and can’t execute their own import files until the dependency is loaded, how can you do this without making the import an asynchronous chunk? After all, the core of implementing dependent preloading is webpack_require.e
3.3.7 summary
Module federation implements shared and remotes configurations. The following is an example of how Module federation implements shared and remotes configurations:
-
How to solve the dependency problem by overwriting the chunk loading webpack_require.e to preload the dependency
-
How to solve
modules
Shared problem, here is the use of global variables to hook
The overall implementation looks very clever, not webpack core developers, estimates can not think of such a solution, in fact, the change is quite large.
The pros and cons of this approach are obvious:
Advantages: Code is loaded at run time, and shared code does not need to be packaged manually
Disadvantages: Dependencies on other applications are actually strongly dependent, meaning you don’t know if App2 is implemented according to the interface
App2 packages must be used asynchronously in code, as some other articles on the web say. There is no such restriction, as you know from the previous demo and after knowing the principle.
0 x4 summary
For Tencent documentation, in fact, the current shared capability is more needed, some common common dependency library configuration shared can be solved, but this is only ideal, in fact, there are still some visible problems, such as:
- Different versions generate different common library ids, which can lead to repeated loading
- How do I get the latest address from remotEntry of app2
- How do I obtain the export interfaces of other applications
The remotes configuration also opens up the possibility for multiple applications to share code, so look forward to the release of webpack5!
Finally, if there is something wrong, please correct it
AlloyTeam welcomes excellent friends to join. Resume submission: [email protected] For details, please click Tencent AlloyTeam to recruit Web front-end engineers.