The original project used the current popular AngularJS. Now xiao Li has taken over the maintenance of some AngularJS projects. Vue3 just learned is not good enough…

Li, who does not already know AngularJS, can only stick to the basics of the framework to solve daily needs, but when he meets some problems, it is still difficult to solve. While searching the Internet for solutions, he thinks about how to change the situation of old projects that are difficult to maintain.

One day, just crowded on the subway Xiao Li looked at the phone, a few days ago to join the Fes. Js wechat group push micro front-end universe plug-in. Fesjs is a good front-end application solution when you’ve used it before, but with the introduction of a micro front-end plug-in, you might be able to give it a try and start an interesting journey.

First stop: Think

The micro front end concept is similar to the micro service, the concept is not described here, mainly focus on the micro front end features:

  • Technology stack independence
  • Independent development, independent deployment
  • The incremental upgrade
  • Stand-alone runtime

Stack independence makes it possible to no longer maintain old Angular, while incremental upgrades make it possible to move the project gradually and iterate on function modules without any worries.

The migration plan The difficulty The workload
No micro front end is used, direct migration One-time migration takes a long period of time and conflicts with incremental requirements big
The new framework is the main application, the old project, the new function to do the micro application The common logic is migrated to the main application first, the old project needs to deal with the common logic interaction with the main application, the old function can be split and migrated, and the new requirements do not conflict larger
The old project is the main application, and the new function is the micro application Common logic migrations can be post-migrated, old functions can be migrated separately, and new requirements do not conflict small

Undoubtedly, the third option is the best option, whether it is the separation of old and new features, iteration, or workload.

Stop two: Practice

Li started out with an old AngularJS-based project as the main application and a micro front end implemented through fes.js. The process for migrating a functional module is as follows:

The main application

1. Install the Qiankun dependency

<script src="/qiankun/dist/index.umd.js"></script>
Copy the code

2. Encapsulate and load microapplication components

function micro() {
    return {
        restrict: 'EA'.scope: {
            name: The '@'.// The name of the microapplication
            entry: The '@'.// Micro application entry
            props: '=' // Attributes passed to the microapplication
        },
        template: ` 
      
{{vm.error}}
`
.replace: true.link($scope, $element, $attrs) { $scope.vm = {}; var vm = $scope.vm; function getProps() { var props = {}; var removekeys = Object.keys($scope); Object.keys($attrs).forEach((key) = > { $scope = $scope if ((key.startsWith('$') || removekeys.includes(key))) return; props[key] = $attrs[key]; }); return Object.assign(props, $scope.props); } function loadApp() { if (!window.qiankun) { vm.error = Error occurred: Qiankun dependency does not exist; return; } vm.error = ' '; var { loadMicroApp } = window.qiankun; var props = getProps(); vm.appInstance = loadMicroApp({ name: `${$scope.name || 'webMicro'}_The ${Math.random()}`.entry: $scope.entry, container: $element[0], props }, { sandbox: { experimentalStyleIsolation: true}}); } loadApp(); $scope.$on('$destroy'.() = > { if(vm.appInstance) { vm.appInstance.unmount(); }}); }}; } angular.module('ui.webank.micro', []).directive('micro', micro); Copy the code

3. Use micro-application components on demand in the main application

Parent application routing page /#/home/foo

 <micro entry="/micro/" props="{id: 11111}"></micro>
Copy the code

Parent application routing page /#/home/bar

 <micro entry="/micro/" props="{id: 22222}"></micro>
Copy the code

The child application

Fes.js has been integrated with the micro front end plugin, first create a micro front end project based on fes.js, then follow the steps in the document to introduce qiankun plug-in.

1. Fesjs project import dependencies [packgae.json]

  "dependencies": {
    "@fesjs/fes": "2.0.0"."@fesjs/plugin-qiankun": "2.0.1"
  }
Copy the code

2. Enable micro front end support [Fes.js]

export default {
    qiankun: {
        micro: {}}}Copy the code

3. Create a child application page. Fes.js follows the convention more than the configuration, and routes are automatically created

  • pages/home/foo.vue —> /#/home/foo
  • pages/home/bar.vue —> /#/home/bar

If you do not want to create a file based on the parent application route, add the following configuration to match the file to [app.js]

export function patchRoutes({ routes }) {
    routes.unshift({
        path: '/home/foo'.component: require('@/pages/foo').default
    });
    routes.unshift({
        path: '/home/bar'.component: require('@/pages/bar').default
    });
}
Copy the code

4. Hook callbacks that need to be added to the lifecycle as a child application [app.js]

// The attribute passed by the parent application can be retrieved with the props parameter
export const qiankun = {
    // Before the application loads
    async bootstrap(props) {
        console.log('app1 bootstrap', props);
        // If the parent application page is /#/home/foo, props. Id is 11111
        // If the parent application page is /#/home/bar, props. Id: 22222
    },
    // Triggers before applying render
    async mount(props) {
        console.log('app1 mount', props);
    },
    // Triggers when props are updated
    async update(props) {
        console.log('app1 update', props);
    },
    // Triggers after the application is uninstalled
    async unmount(props) {
        console.log('app1 unmount', props); }};Copy the code

Using the useModel plug-in, the props sent by the parent application will be updated to qiankunStateFromMain, which can be directly used on the page

<script>
export default {
    setup(){
        const mainState = useModel('qiankunStateFromMain');
        // If the parent application page is /#/home/foo, mainstate. id is 11111
        // The parent application page is /#/home/bar, mainstate. id is 22222
        return {
            mainState
        };
    }
}
</script>
Copy the code

The deployment of

  • The parent application is properly packaged and deployed on the previous server path
  • The child application is packaged normally, which is different from the path name of the parent application plus -micro. For example, if the parent application is /data/ HTML /test, the child application is test-micro, and the entry of the micro application component of the parent application is changed accordingly.

By doing this, Li is perfectly able to get the new vuE3 functionality running in the old AngularJS project. Happy big pu Ben, quickly drink 82 years of happy water pressure pressure shock ~ later can and Fesjs, vuE3 happy play.

Third stop: Advanced

After playing happily for a period of time, the business brought new requirements, and the analysis found that the page involved is very complex, if pushed to the micro front-end implementation, it can not be delivered on time, if changed on the page, and say goodbye to the fun of fes.js.
< appwithmemohistory /> < appwithMemohistory /> Let’s go!!

Based on the implementation of the second station, the routes of the main application and micro application need to correspond one to one, as shown in the figure below:This leads to a large amount of work to migrate the parent application page if it is very complex.

<MicroAppWithMemoHistory />Can perfectly solve this problem, to achieve the following effects:

  • Controllable migration granularity: the same page of a parent app is split into one or more pages of a child app.
  • Multi-system sharing: the division of services responsible for sub-applications is more clear, which facilitates the reuse of various systems.

is a component of the parent app. Old projects cannot be used directly. Xiao Li stripped the source code, found its essence is Memory VueRouter, Xiao Li hurriedly learn next.

Memory VueRouter

Create a memory-based history. The main purpose of this history is to deal with SSR. It starts in a particular place, and that place is everywhere. If the user is not in the browser context, they can replace the location with the launch location by calling router.push() or router.replace().

In a child application of the VUE technology stack, instead of creating a route, create a Memory route

const router = VueRouter.createRouter({
  / / createMemoryHistory instead
  history: VueRouter.createMemoryHistory(),
  routes, // 'routes: routes'
})
Copy the code

After a Memory route is created, the child does not need to redirect to the same URL as the parent.

Parent application micro front-end component

A child application based on the Fes. Js plugin already supports Memory VueRouter. The parent application only needs to pass the URL attribute to the child application, and then the Memory VueRouter is automatically enabled.

function micro() {
    return {
        restrict: 'EA'.scope: {
            name: The '@'.url: The '@'.entry: The '@'.props: '='
        },
        template: ` 
      
{{vm.error}}
`
.replace: true.link($scope, $element, $attrs) { $scope.vm = {}; var vm = $scope.vm; function getProps() { var props = {}; var removekeys = Object.keys($scope); Object.keys($attrs).forEach((key) = > { $scope = $scope if ((key.startsWith('$') || removekeys.includes(key))) return; props[key] = $attrs[key]; }); return Object.assign(props, $scope.props); } function loadApp() { if (!window.qiankun) { vm.error = Error occurred: Qiankun dependency does not exist; return; } vm.error = ' '; var { loadMicroApp } = window.qiankun; var props = getProps(); // When specified, the child application will use Memory routing if ($scope.url) { props.url = $scope.url; } props.onRouterInit = function (router) { vm.router = router; }; vm.appInstance = loadMicroApp({ name: `${$scope.name || 'webMicro'}_The ${Math.random()}`.entry: $scope.entry, container: $element[0], props }, { sandbox: { experimentalStyleIsolation: true}}); }function updateApp() { var app = vm.appInstance; if(! app)return; if(! vm.updatePromise) { vm.updatePromise = app.mountPromise; }else { vm.updatePromise = vm.updatePromise.then(() = > { if (app.update && app.getStatus() === 'MOUNTED') { var props = getProps(); if ($scope.url) props.url = $scope.url; returnapp.update(props); }}); } } loadApp(); $scope.$watch('url'.() = > { if(vm.router && vm.appInstance) { updateApp(); vm.router.push($scope.url); }}); $scope.$watch('props', updateApp); $scope.$on('$destroy'.() = > { if(vm.appInstance) { vm.appInstance.unmount(); }}); }}; } angular.module('ui.webank.micro', []).directive('micro', micro); Copy the code

parameter

parameter instructions type The default value
name Subapplication name string ‘webMicro’
entry Subapplication entry URL string
url The routing address of the child application page is not the same as that of the parent application string
props Attributes passed to child applications that can respond to changes object {}
attribute Other static property values passed to the child application

The instance


<! The child application route matches the parent application route -->
<micro entry="/micro/" props="{id: 11111}" desc="CSCM subapplication"></micro>

<! -- independent route, does not match the parent application route -->
<micro entry="/micro/" url="/cscm/foo" props="{id: 11111}" desc="CSCM subapplication"></micro>

Copy the code

Terminal: end or start?

If you can do this for AngularJS projects, it will make it easier for projects based on other technology stacks. Considering the consistency and maintainability of the main application in the use of the micro front end, for “fes.js child applications, parent applications of different technology stacks” in the implementation of loading micro front end components, here are some unified specification suggestions:

  • attribute
Name: indicates the name of the child application. Entry: indicates the entry of the child application. Url: indicates the route of the child application. Other extended attributes that can be monitored for changes: Provided by the attribute and not necessarily monitored for changes.Copy the code
  • loadMicroApp
Name: Combined with a hash or timestamp, url, props, attr, and onRouterInit (enabling memory routing and returning route instances) are propsCopy the code
  • Listening: url, props change, atRR can listen to and handle. Call the update method of the microapplication instance

  • Special processing of memory routing: listening for component attribute URL changes, realizing the parent application controlling the redirect of the child application route (less used scenarios)

  • Destruction requires uninstalling the child application instance

Complex pages can now be migrated by function, Xiao Li smiles with joy, later old projects will be replaced by micro front end step by step, no need to go back to the old AngularJS era, but is this the end? Xiao Li recalled the pit he stepped on. Perhaps this is the starting point. Who will be the next technology stack to be eliminated?