Microfront background

For the company’s internal management system, ToB SaaS system and a series of projects, due to the long life cycle of the project itself, and long-term continuous iteration, adding new functional modules, will eventually lead to the project itself more and more large size, structure more and more complex. Not only affect the future maintenance cost, performance and other aspects, and for development, but also write more and more write down, the final recommendation may be Emm… You know.

The concept of a micro front end is very old, such as iframe in the early days. However, with the continuous development of front-end technology, there are many excellent micro front-end solutions, such as Single-SPA and Ant Qiankun. If you are interested, you can learn about it.

Implementation scheme

The micro front end introduced in this paper is realized based on the route preloading method of Vue. For the specific architecture and process, please refer to the following figure:

The specific implementation

subprojects

Build subprojects through the Vue family bucket

  • Standardize the subproject name and port number, and use the submodule name in the subsequent proxy and route names
// package.json
{
    "name": "mobile-wechat"."devPort": 10011}Copy the code
  • Adjust the directory structure. Since the subproject depends on the main project, the PUBic folder can be deleted. The final general directory structure is as follows
├ ─ ─ the SRC │ ├ ─ ─ views │ ├ ─ ─ the router │ ├ ─ ─ the main, js ├ ─ ─ Babel. Config. Js ├ ─ ─ vue. Config. Js ├ ─ ─ the env. The dev ├ ─ ─ the env. Production ├ ─ ─. Env. TestCopy the code
  • Configure the config file after adjustment
// vue.config.js

const APP_NAME = require('./package.json').name; // define project name const PORT = require('./package.json').devPort;

module.exports = {
  publicPath: `/${APP_NAME}/`,
  chainWebpack: (config) => {
    config.externals({
      'vue': 'Vue'
    })

    config.output
      .filename('main.js')
      .chunkFilename('[name].[chunkhash:8].js')
      .jsonpFunction(`webpackJsonp-${APP_NAME}`)
      .library(`app-${APP_NAME}`)
      .libraryExport('default')
      .libraryTarget('umd')

    config.plugin('define').use(webpack.DefinePlugin, [{
      'process.env.VUE_APP_NAME': JSON.stringify(APP_NAME)
    }])

    config.plugins
      .delete('html')
      .delete('preload')
      .delete('prefetch')
  },

  devServer: {
    port: PORT,
  },
};

Copy the code
  • After config is configured, mount the defined routing table to the Vue in the entry file and organize the routes in the following format for easy management.

import Vue from 'vue'
import router from './router'/ / define __shareRouter__ attribute is used to store routing information const shareRouter = (Vue) __shareRouter__ = Vue) __shareRouter__ | | {}) / / defines the router to the property shareRouter[process.env.VUE_APP_NAME] = routerCopy the code

Finally start the project

The main project

  • The subproject entry file is first packaged into the main project via Webpack and can be passed before the main project entry fileinsert-script-webpack-pluginPlug-in to implement
// webpack.config.base.js

const InsertScriptWebpackPlugin  = require('insert-script-webpack-plugin');

module.exports = {
    // ...
    plugins: [
        // ...
        new InsertScriptWebpackPlugin({
            paths: ['/mobile-crm/main.js'.'/mobile-wechat/main.js']})]}Copy the code
  • The development environment configures the proxy, and the production environment can implement the proxy through Nginx, loading entry files for subprojects
// webpack.config.dev.js

const PROXY = {
    '/mobile-crm/': {
      target: 'http://127.0.0.1:10011/'
    },
    '/mobile-wechat/': {
        target: 'http://127.0.0.1:10012/'}}Copy the code
  • Intercept subproject routes in the main project routing file and add them to the main project routing table
import Vue from 'vue';
import Router from 'vue-router';
import routes from './routes/index'; Vue.use(Router); const routes_crm = Vue.__shareRouter__ routes = Object.values(routes_crm).reduce((acc, Prev) =>{// Register subproject routes to the main project router-view const b = {path:'/',
        component: () => import('@/views/index.vue'),
        children: [...prev]
    }
    return  acc.concat(b)
}, routes)

const router = new Router({
    routes,
});

Copy the code

When you start the project, you can see that the main project is ready to load the main.js file of its children.

Issues for attention

  • To ensure the order of resource loading, load co-dependent resources first -> submodules -> parent modules.
  • The same front-end framework needs to be guaranteed.
  • ifrouterModel forhistory, requires server configuration to limit the proxy resource file type tojs/css/imageAnd so on, rather thanhtml. And set not to cache entry files, the specific configuration is as follows:
location ~ \/mobile-crm\/(.+\.js)$ {
    if ( $request_uri= /mobile-crm/main.js ){ add_header Cache-Control no-cache; }}Copy the code