Single page applications (SPA) are in vogue in the front-end space. As time goes on and applications become more functional, they become bigger and harder to maintain. Hence the concept of a “micro front end”.
“Front end” from 2016 ThoughtWorks radar technology, refers to the project into each one can be independent operation, independent development and deployment of front-end applications, these applications can be parallel development, Shared components.
The micro front end can be implemented in many ways: server routing redirection, combining multiple independent applications, iFrame, building through Web Components, etc.
The concepts related to the microfront-end are also applied in some projects in the Push front-end (based on the Vue framework).
Why front-end microservitization
I emphasize “part project” because every technology or concept has its own scenarios, and the micro front end is no exception. For small and medium-sized projects, using a micro front end can complicate things because it is not development-friendly.
Take the following business scenario as an example:
There are 10-20 modules in the A project line, with 5-15 pages in each module. All of the products in project line A are composed based on these modules, which means that if we follow the normal SPA development path, we might need many branches or REPOs to maintain these products, because the version of modules required for each product will be slightly different.
Evolutionary process
In order to avoid A series of problems such as branch chaos, huge project, code conflict and troublesome packaging, we began to adjust the front-end development and deployment mode of project line A by taking the opportunity of back-end microservice separation.
Initially, instead of using front-end microservices development and deployment, we split the modules of the project into many independent REPOs, avoiding the need for engineers to pull code and resolve conflicts during development (one or two people per iteration of a module).
Therefore, the question is: how to solve the problems of development, package deployment, and common dependencies and component reuse in the project once the modules are broken down.
The directory structure of the split module project is roughly as follows:
The main.js entry and common components in the project are separated into a separate project, called the Main project.
The DEV submodule cannot be developed directly because each submodule project contains only the page code and routing and menu configuration for the current module. So we developed a CLI tool called Lego. When developing modules, developers only need to run the “Lego dev” command in the module root directory to start the development service of a current module, and the developed modules will be published to our own NPM source for version management.
If the module is only split, the developer needs to configure the corresponding operating environment for the module when developing the module separately, and the mutual call between modules is also very troublesome. The LEGO CLI solves the problem of the module running environment, which is automatically loaded by the CLI. The module developer only needs to pay attention to the business logic of the module itself.
In addition, the module provides a config.js file that allows you to configure other dependent modules from the NPM source, making it easier for developers to call different modules at development time. Using the “Lego dev” command also supports the introduction of the “@self/” path, “@self/” to the SRC/folder of the current module and “@/” to the SRC/folder of the main project, thus avoiding the problem of importing paths during module development.
Through the module split transformation, solved the problem of huge project, branch chaos, code conflict is also significantly reduced. But for a packaged deployment of a single product, we still need to get the source code from each module and package it into a separate product through the Main project. Even if you change just one line of code in one module, the entire system needs to be repackaged, and the entire product needs to be regression tested.
In response to this problem, we wondered if modules could be packaged directly as applications to be called.
Module packaging and independent deployment
Ideally, each module can be developed and deployed independently, and then the product itself decides which modules to load.
The effect is as follows:
So we need the entry (index.js) to be injected into the main project as needed after the module is packed and loaded (routed) by the main project.
On the one hand, for projects packaged using WebPack, the code is based on the CommonJS specification. Because the UMD specification is compatible with the CommonJS specification, this allows developers to use modules packaged based on the UMD specification directly in their projects.
Vue-router and vuex, on the other hand, both support dynamic loading of the addRouter/registerModule API.
We have used two solutions:
First, the main project exposes the vuE-Router and VUex instances to global (Window) when the Vue instance is initialized, and stores the route prefix of the submodule in the routing table of the project. When the page jumps to the route of the matching submodule, the main project loads the submodule umd.js file and dynamically registers the Router and vuex module to render the page.
The simple DEMO looks like this:
Second: the umd.js file of the submodule is loaded first to expose the routing and VUEX information of the submodule to the global (Window). The Vue instance obtains routing information, vuex Module, menu information, and so on from the Window to form an independent product.
The simple DEMO looks like this:
Of course, both schemes have certain disadvantages:
The first scheme: first of all, the sub-module JS file is loaded after the page jump, therefore, in 404 jump and route permission verification implementation will encounter some problems; Second, there is a long time of page white screen before the submodule file is loaded and before the submodule is rendered.
Second option: the UMD entry file needs to be loaded regardless of whether the submodule user will access it or not. This requires the entry file to be small enough, which means that the submodule cannot merge chunks using the min-chunk-size-plugin, requiring the developer to use handwritten webpackChunkName or other tools to merge chunks.
Based on vuE-CLI3 practice
Vue-cli3.x provides good support for the packaging of submodules. Use “vue-cli-service build-target =lib” to package the submodule code into umD standard format.
However, the following issues need to be noted:
-
“–target=lib” is intended to be used by components published to NPMJS, so packaged files are hash free (even if chunkName is configured in vue.config.js). The solution we took was to modify the vue-CLI-service source code before executing the lego scaffolding package command.
-
If csS-in-js is not configured, the background-image path in the packed CSS file is faulty. Based on this, we give two solutions: configure CSS-in-JS, or modify the source code of vue-cli-service in node_modules and then package it.
The above is a practical example of pushing the development and deployment of front-end microservitization.
In practice, we have found that the access of microservitization has solved the problems of difficulty in maintenance and trouble in compiling and deploying products. The CLI tool developed by us also solves the problem of separate module development and operation.
Of course, there are limitations to our microservitization approach. It is suitable for large projects where modules are closely linked, without the technical independence and team code isolation emphasized in the microfront-end concept.