qiankun+vue构建主应用

更新时间: 2021-01-29 10:41:08

# 引言

本教程将引导大家将普通的vue项目改造成qiankun微前端基座。

# 构建主应用基座

我们先使用vue-cli生成一个Vue的项目,初始化主应用。本教程使用的是vue-cli4版本,如果版本低的可以先升级。 将普通的项目改造成qiankun主应用基座,需要进行三步操作:

  1. 创建微应用容器 - 用于承载微应用,渲染显示微应用;
  2. 注册微应用 - 设置微应用激活条件,微应用地址等等;
  3. 启动qiankun

# 创建微应用容器

  1. 在主应用中创建微应用的承载容器,这个容器规定了微应用的显示区域,微应用将在该容器内渲染并显示。我们先设置主应用自身的路由。代码如下:
      // src/router/index.js    

      import Vue from 'vue'
      import VueRouter from 'vue-router'

      Vue.use(VueRouter)

      const routes = [
          {
              path:"/",
              name:"Home",
              component:() => import("../pages/start.vue") //开始界面
          },
          {
              path:"/login",
              name:"Login",
              component:() => import("../pages/login.vue"), //登录界面
              meta:{ 
                  fullscreen:true //是否全屏
              }
          }
      ]

      export default new VueRouter({ //网上大多数都是使用history模式,但是我使用的是hash模式
          routes
      })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
            //    src/main.js
            import Vue from 'vue'
            import App from './App.vue'
            import router from "./router"
            import store from './store'

            Vue.config.productionTip = false

            new Vue({
                router,
                store,
                render: h => h(App),
            }).$mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. 设置主应用的布局,我们会有一个菜单和显示区域,代码实现如下: 首先是菜单配置:(目前只有主应用的一个页面要显示在菜单上)
            // src/data/nav.js
            export default [
                {
                    title: "开始",
                    path: "/",
                }
            ]
1
2
3
4
5
6
7

这是App.vue。我将布局写在了layout中。

            // src/App.vue
            <template>
              <div class="main-contenter">
                  <layout></layout>
              </div>
            </template>

           <script>
            export default {
                name: 'App',
                components:{
                    layout: () => import("@/layout/index.vue")
                },
            }
            </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

接下来查看layout

            // src/layout/index.vue
            <template>
                <div class="layout-content">
                    <div class="header" v-if="!$route.meta.fullscreen">
                        <!--头部组件-->
                       <header-component></header-component>
                    </div>
                    <div class="content">
                        <div class="nav" v-if="!$route.meta.fullscreen">
                            <!--导航组件-->
                            <nav-component></nav-component>
                        </div>
                        <div class="frame-wrapper" :style="$route.name&&!$route.meta.fullscreen?'padding:10px;':'padding:0px;'">
                          <!-- 主应用渲染区,用于挂载主应用路由触发的组件,使用$route.name来判断是不是主应用组件 -->
                          <router-view v-show="$route.name"></router-view> 
                          <!--子应用渲染区,用户挂载子应用节点-->
                          <div v-show="!$route.name" id="frame" :style="!$route.name&&!$route.meta.fullscreen?'padding:10px;':'padding:0px;'"></div> 
                        </div>
                    </div>
                </div>
            </template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

nav.vue是用来渲染左侧导航栏的组件

            // src/layout/nav.vue
            <template>
                <div class="nav-content">
                    <el-menu>
                        <template v-for="(item,index) in navData">
                            <el-menu-item @click="goto(item.path)" :index="'nav'+index" :key="'nav'+index" v-if="!item.children || item.children.length == 0">
                                <span slot="title"><span v-pre>{{item.title}}</span></span>
                            </el-menu-item>
                            <el-submenu v-if="item.children && item.children.length > 0" :index="'nav'+index" :key="'nav'+index">
                                <span slot="title"><span v-pre>{{item.title}}</span></span>
                                <el-menu-item @click="goto(child.path)" v-for="(child,idx) in item.children" :key="'nav'+index+'-'+idx" :index="'nav'+index+'-'+idx"><span v-pre>{{child.title}}</span></el-menu-item>
                            </el-submenu>
                        </template>
                    </el-menu>
                </div>
            </template>

            <script>
            import navData from "@/data/nav.js"

            export default {
                data(){
                    return {
                        navData:navData
                    }
                },
                methods:{
                    goto(path){
                        this.$router.push(path)
                    }
                }
            }
            </script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 注册微应用

构建好主框架后,我们需要使用qiankunregisterMicroApps方法注册微应用,代码实现如下: 首先主应用安装qiankun

            npm install qiankun -S
1
            // src/micro/app.js
            //此时我们还没有微应用,所以app为空
            const apps = [
                /**
                * name: 微应用名称 - 具有唯一性
                * entry: 微应用入口 - 通过该地址加载微应用
                * container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
                * activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
                */
            ]
            export default apps
1
2
3
4
5
6
7
8
9
10
11
            // src/micro/index.js
            import {
                registerMicroApps,
                addGlobalUncaughtErrorHandler,
                start,
            } from "qiankun";

            // 微应用注册信息
            import apps from "./apps";

            /**
            * 注册微应用
            * 第一个参数 - 微应用的注册信息
            * 第二个参数 - 全局生命周期钩子
            */
            registerMicroApps(apps, {
                // qiankun 生命周期钩子 - 微应用加载前
                beforeLoad: (app) => {
                    console.log("before load", app.name);
                    return Promise.resolve();
                },
                // qiankun 生命周期钩子 - 微应用挂载后
                afterMount: (app) => {
                    console.log("after mount", app.name);
                    return Promise.resolve();
                },
            });
            
            /**
            * 添加全局的未捕获异常处理器
            */
            addGlobalUncaughtErrorHandler((event) => {
                console.error(event);
                const { msg } = event;
                // 加载失败时提示
                if (msg && msg.includes("died in status LOADING_SOURCE_CODE")) {
                    console.error("微应用加载失败,请检查应用是否可运行");
                }
            });
            
            // 导出 qiankun 的启动函数
            export default start;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

从上面可以看出,我们的微应用注册信息在apps数组中(此时为空,我们在后面接入微应用时会添加微应用注册信息),然后使用 qiankunregisterMicroApps方法注册微应用,最后导出了start函数,注册微应用的工作就完成啦!

# 启动主应用

我们在注册好了微应用,导出start函数后,我们需要在合适的地方调用start启动主应用。 本例中我们在layout的mounted钩子中启动qiankun主应用,以保证主应用加载时容器已经加载完毕,代码如下:

           // src/layout/index.vue

           //导入start函数
           import startQiankun from "@/micro"

           export default {
               data(){
                   return{

                   }
               },
               components:{
                   headerComponent:() => import("@/layout/header.vue"),
                   navComponent:() => import("@/layout/nav.vue")
               },
               mounted(){
                   //启动主应用
                   startQiankun()
               }
           }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

至此,主应用改造完毕!