May 31, 2021 Article blog
The article comes from the public number: Programmer Growth Refers to the North, author mcuking
A few days ago saw the micro-front end in the United States group takeaway practice, feel and the author's team practice for more than a year of micro-front-end scheme is very similar, but we are based on the Vue technology stack, so also want to summarize an article to share with you. Because the author's writing is not too good, which borrowed some of the Summary text of the United States Group article, but also please forgive Oh
For large front-end projects, such as in-house management systems (typically including OA, HR, CRM, conference appointments, etc.), putting all your business in one front-end project can lead to these problems as business capabilities increase:
preload-routes
and
async-routes
currently used by the author's team and will eventually split the entire front-end project into one master project and multiple subprogrammes, both of which work as follows:
Combined with the author's previous use of layered architecture to implement the reuse of non-view code (if interested, please refer to the author's previous article on the front-end layered architecture practice), the complete scenario is as follows:
As shown in the figure, the entire front-end project is split out of multiple sub-projects by line of business, each of which is a separate repository that contains only the code of a single line of business, can be developed and deployed independently, reducing the complexity of project maintenance.
With this approach, our front-end projects not only maintain the scalability of the landscape (multiple sub-projects), but also have vertical (single subproducient) reusability. S o how exactly did this package work? The implementation mechanism of the scenario is explained in detail below.
Before explaining, first make it clear that there are two ways to implement this scheme, one is preloaded routing, the other is lazy loading routing, you can choose one according to the actual needs. Next, we introduce the implementation mechanism of these two approaches.
preload-routes
1. Sub-projects are packaged in the library mode of vue-cli 3 for subsequent references to the main project
Note: In
library
mode,
Vue
is external. T
his means that there will be no
Vue
in the package, even if you imported
Vue
in your code.
If the library is used through a wrapper, it will attempt to load
Vue
in a dependent manner through the wrapper;
2. When compiling the main project, insert the main entry file of the sub-project into the html of the main project as a script tag.js through the InsertScriptPlugin plug-in
Note: Be sure to place the
script
main.js
script label for the entry file for the sub-project on the
script
label for the main project entry file
app.js
to ensure that the entry file for the subpronect is executed before the entry file code for the main project, and the next steps will understand why.
Note: The main, compiled
main.js
for the project in a local development environment, is stored in memory, so it is not visible on disk, but can be accessed.
The core code for InsertScriptPlugin is as follows:
compiler.hooks.compilation.tap('InsertScriptWebpackPlugin', compilation => {
compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing.tap(
'InsertScriptWebpackPlugin',
htmlPluginData => {
const {
assets: { js }
} = htmlPluginData;
// 将传入的 js 以 script 标签形式插入到 html 中
// 注意:需要将子项目的入口文件 main.js 放在主项目入口文件 app.js 之前,因为需要子项目提前将自己的 route list 注册到全局上
js.unshift(...self.files);
}
);
});
3. The
html
of the main project needs proxy forwarding to access resources such as compiled
js / css
in the subproject
const PROXY = {
'/app-a/': {
target: 'http://localhost:10241/'
}
};
4. When the browser parses
html,
it resolves and executes the main
main.js
the portal file to
the sub-project, registering the route list of the sub-project with Vue
share
.routes so
that subsequent master projects merge them into the overall route.
Sub-project
main.js
code is as follows: (To minimize the resources loaded when the main project page is rendered for the first time, the sub-project's entry file recommends only routing mounts)
import Vue from 'vue';
import routes from './routes';
const share = (Vue.__share__ = Vue.__share__ || {});
const routesPool = (share.routes = share.routes || {});
// 将子项目的 route list 挂载到 Vue.__share__.routes 上,以便后续主项目将其合并到总的路由中
routesPool[process.env.VUE_APP_NAME] = routes;
5. Continue to parse
html
down, parse and execute to the main project
main.js
from
Vue
share
.routes takes the route list for all sub-projects, merges them into the overall routing table,
and then initializes a
vue-router
instance and passes in to the new Vue
The key code is as follows
// 从 Vue.__share__.routes 获取所有子项目的 route list,合并到总的路由表中
const routes = Vue.__share__.routes;
export default new Router({
routes: Object.values(routes).reduce((acc, prev) => acc.concat(prev), [
{
path: '/',
redirect: '/app-a'
}
])
});
At this point, a single-page application is split into sub-projects by business, and the main
main.js
the entry file for sub-projects is a bridge linking the main project to the sub-project.
Also if you need to use
vuex
the order is the opposite of
vue-router
(first main project, then subproduct):
1. First initialize a
store
instance new Vuex.Store in the main
new Vuex.Store
entry file, and then hang it on
Vue.__share__.store
2. Then get to
Vue.__share__.store
in the
App.vue
of the sub-project and call
store.registerModule(‘app-x', store)
to register the
store
of the sub-project as a submodule to the
store
async-routes
Lazy loading of a route, as the name implies, means waiting until the user clicks to enter the sub-project module, determines which sub-project is by parsing the route that is about to jump, and then asynchronously loads the sub-project's entry file
main.js
(either by systemjs or by writing a method of dynamically
script
a script label and
body
Once loaded successfully, the routing dynamics of the sub-project can be added to the overall route of the main project.
1. The main project
router.js
file defines
the beforeEach hook in vue-router to intercept the route, and analyze which sub-project is required based on the route to be jumped, and then go asynchronously to load the corresponding sub-project entry file,
here is the core code:
const cachedModules = new Set();
router.beforeEach(async (to, from, next) => {
const [, module] = to.path.split('/');
if (Reflect.has(modules, module)) {
// 如果已经加载过对应子项目,则无需重复加载,直接跳转即可
if (!cachedModules.has(module)) {
const { default: application } = await window.System.import(
modules[module]
);
if (application && application.routes) {
// 动态添加子项目的 route-list
router.addRoutes(application.routes);
}
cachedModules.add(module);
next(to.path);
} else {
next();
}
return;
}
});
2. The entry file for the sub-project
main.js
only
needs to expose the routes of the sub-project to the main project,
the code is as follows:
import routes from './routes';
export default {
name: 'javascript',
routes,
beforeEach(from, to, next) {
console.log('javascript:', from.path, to.path);
next();
}
};
Note: In addition to exposing
routes
method, the
beforeEach
method is exposed here, in fact, in order to support the page permission restrictions on sub-projects through routing guards, the main project gets this sub-project
beforeEach
which can be
beforeEach
vue-router
beforeEach hook, please refer to
async-routes
In addition to the different interactions between primary and subpro projects, agent forwarding subpro project resources,
vuex store
registrations, and so on are exactly the same as the preloaded routes above.
The pros and cons of this package are interviewed below:
merit
Cons:
1. If the sub-project code is updated, do you need to package and deploy the main project in addition to packaging the deployment sub-project?
The deployment master project does not need to be updated. H
ere's a
trick
that you forgot to mention above, that is, the sub-project packaged portal file without
chunkhash
directly
main.js
(the other js of the sub-project have chunkhash).
This means that the main project only needs to remember the name of the sub-project to find the portal file
subapp-name/main.js
so the master project does not need to update anything after the sub-project is packaged and deployed.
2. For the second question, the neutron project entry file main.js How can I prevent the file from always being cached without chunkhash?
You can force the cache to not cache on the static resource server side for sub-project entry files, and here is the configuration of the server for
nginx
situation:
location / {
set $expires_time 7d;
...
if ($request_uri ~* \/(contract|meeting|crm)-app\/main.js(\?.*)?$) {
# 针对入口文件设置 expires_time -1,即expire是服务器时间的 -1s,始终过期
set $expires_time -1;
}
expires $expires_time;
...
}
If there is no need to use multiple technology stacks in a large front-end project, it is still highly recommended that the author currently practice this scenario. In addition, if it is a React technology stack, it is possible to implement a similar scenario according to this idea.
That's what
W3Cschool编程狮
has to say about
Vue Technology Stack-based micro-front-end scenario practices,
and I hope it's helpful.