Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Micro-front-end scenario practice based on Vue technology stack


May 31, 2021 Article blog


Table of contents


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

Background

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:

  • The large size of the code results in long compilation times and slower development and packaging
  • The increasing number of project files makes it increasingly difficult to find related files
  • Minor changes to a business that result in the packaging and deployment of the entire project

Introduction to the scenario

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:

  • Main project: Used to manage routing switching for sub-projects, resealing routes for sub-projects and global store tiers, providing global libraries and methods
  • Sub-projects: Used to develop sub-line-of-business code, a sub-project corresponds to a sub-line of business and contains both ends (PC-Mobile) code and multiplexing layer code (non-view layers in project hierarchy)

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:

 Micro-front-end scenario practice based on Vue technology stack1

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.

Implementation mechanism

How to preload the route

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

  • If you are developing locally, you can provide the proxy through webpack, for example:

const PROXY = {
  '/app-a/': {
    target: 'http://localhost:10241/'
  }
};

  • If you are deploying online, you can forward through nginx or reference the packaged master and sub-projects in a folder along relative paths.

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

Lazy loading routing

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.

Advantages and disadvantages

The pros and cons of this package are interviewed below:

merit

  • Sub-projects can be packaged separately and deployed online separately, increasing the speed of development and packaging
  • Development between sub-projects is independent of each other, does not affect each other, can be maintained in different warehouses, reducing the size of a single project
  • Keep the one-page app experience and switch between sub-items without refreshing
  • The cost of retrofitting is low, the intrusion of existing projects is low, and the cost of line-of-business migration is low
  • Ensure that the overall project is unified by a single technology stack

Cons:

  • The main and sub-projects need to share a Vue instance, so it is not possible for a sub-project to use the latest version of Vue (such as Vue3) or React alone

Some of the questions are answered

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;
    ...
}

To be perfected

  • You can automatically generate sub-projects and related configurations by writing a scaffold

end

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.