微前端micro-app的使用

演示效果

子应用的项目

基应用嵌入子应用效果图


前言

博主在项目中由于多个团队开发项目,需要相互嵌套,从而了解到微前端micro-app嵌套项目,从而简单记录一下使用过程


一、微前端是什么?

微前端的概念是由ThoughtWorks在2016年提出的,它借鉴了微服务的架构理念,核心在于将一个庞大的前端应用拆分成多个独立灵活的小型应用,每个应用都可以独立开发、独立运行、独立部署,再将这些小型应用融合为一个完整的应用,或者将原本运行已久、没有关联的几个应用融合为一个应用。微前端既可以将多个项目融合为一,又可以减少项目之间的耦合,提升项目扩展性,相比一整块的前端仓库,微前端架构下的前端仓库倾向于更小更灵活。

它主要解决了两个问题:

  • 1、随着项目迭代应用越来越庞大,难以维护。
  • 2、跨团队或跨部门协作开发项目导致效率低下的问题

二、使用步骤

1.安装依赖

npm i @micro-zoe/micro-app --save

2.在入口处引入

main.js

import microApp from "@micro-zoe/micro-app";


microApp.start();

if (window.__MICRO_APP_ENVIRONMENT__) console.log('我在微前端环境中')
else console.log('我不在微前端环境中')
if (window.__MICRO_APP_BASE_APPLICATION__) {
	console.log('我是基座应用')
  }

3.子应用的路由()

 

4.分配一个路由给子应用(重要)(基应用的路由

/**

 *  注意:

 *    权限是通过路由中的 name 属性来进行定位,所以 name 必须填写,且唯一

 *

 *    isMenu  false表示该路由仅仅是路由,而不生成菜单

 *        默认所有路由都生成菜单,不想生成菜单直接添加属性 isMenu 为 false 即可

 *      注意:1.不需要每个路由都添加 isMenu 属性,只需要给不需要生成菜单的路由添加该属性,需要生成菜单的路由不需要加该属性

 *             2.二级菜单 isMenu 必须写在 children 里的每条路由上,一级菜单写在 children 外面(当前路由即可)

 *         3.权限路由不需要添加 isMenu ,因为权限路由是有后端返回的,所以不需要添加 isMenu 属性

 *    特别注意: 后台返回的权限菜单数据

 *            path值与name的值(后台返回的权限菜单中的path值与name值),必须对应组件目录中的组件所在目录

 *           如:角色路由组件 roles 目录必须与后台返回的数据中的角色路由 name 值一致,否则加载不到组件

 *              path:'/组件目录名称',

 *              name:'组件目录名称'

 *           path 与 name 必须对应组件目录名称

 */


// 所有人都可以访问

const routes = [

  {

    path: "/login",

    //name:'login',

    isMenu: false,

    component: () =>

      import(/* webpackChunkName: "login" */ "@/views/login.vue"),

  },

  {

    path: "/",

    name: "dashboard",

    redirect: "/home",

    isMenu: false,

    children: [

      {

        path: "/home",

        name: "home",

        meta: { title: "主页", icon: false },

        component: () => import(/* webpackChunkName: "home" */ "@/views"),

      },

    ],

  },

  {

    path: "/test",

    name: "test",

    isMenu: true,

    meta: { title: "测试管理", icon: "el-icon-s-tools" },

    component: () => import(/* webpackChunkName: "test" */ "@/views/test"),

  },

  {

    path: "/versiontwotable",

    name: "表格",

    isMenu: true,

    meta: { title: "表格2", icon: "el-icon-s-tools" },

    component: () =>

      import(

        /* webpackChunkName: "test" */ "@/views/versiontwo/pages/makecodeIndex.vue"

      ),

  },

  {

    path: "/my-page/*",

    name: "子应用",

    isMenu: true,

    meta: { title: "子应用", icon: "el-icon-s-tools" },

    children: [

      // 其他的路由都写到这里

      {

        name: "/",

        path: "/",

        redirect: "/index",

        meta: { title: "index" },

        isMenu: true,

        children: [

          {

            name: "index",

            path: "/index",

            meta: { title: "index" },

            component: () => import("@/views/microapp/pages/index.vue"),

          },

        ],

      },

      {

        name: "/form",

        path: "/form",

        redirect: "/form/index",

        meta: { title: "form" },

        isMenu: true,

        children: [

          {

            name: "/form/index",

            path: "/form/index",

            meta: { title: "form" },

            component: () => import("@/views/microapp/pages/index.vue"),

          },

        ],

      },

      {

        name: "/example",

        path: "/example",

        redirect: "/example/tree",

        meta: { title: "example" },

        isMenu: true,

        children: [

          {

            name: "/tree",

            path: "/example/tree",

            meta: { title: "tree" },

            component: () => import("@/views/microapp/pages/index.vue"),

          },

          {

            name: "/copy",

            path: "/example/copy",

            meta: { title: "copy" },

            component: () => import("@/views/microapp/pages/index.vue"),

          },

        ],

      },

      {

        name: "/table",

        path: "/table",

        meta: { title: "table" },

        redirect: "/table/index",

        isMenu: true,

        children: [

          {

            name: "/table/index",

            path: "/table/index",

            meta: { title: "table" },

            component: () => import("@/views/microapp/pages/index.vue"),

          },

        ],

      },

      {

        name: "/admin",

        path: "/admin",

        meta: { title: "admin" },

        redirect: "/admin/index",

        isMenu: true,

        children: [

          {

            name: "/admin/index",

            path: "/admin/index",

            meta: { title: "admin" },

            component: () => import("@/views/microapp/pages/index.vue"),

          },

        ],

      },

      {

        name: "/people",

        path: "/people",

        meta: { title: "people" },

        redirect: "/people/index",

        isMenu: true,

        children: [

          {

            name: "/people/index",

            path: "/people/index",

            meta: { title: "people" },

            component: () => import("@/views/microapp/pages/index.vue"),

          },

        ],

      },

    ],

  },

  {

    path: "/test1",

    name: "test1",

    isMenu: true,

    meta: { title: "角色管理", icon: "el-icon-s-tools" },

    children: [

      {

        path: "/roles3-2",

        name: "roles93-2",

        meta: { title: "角色管理3-2", icon: "el-icon-s-data" },

        component: () => import(/* webpackChunkName: "test1" */ "@/views/test"),

      },

      {

        path: "/roles3-3",

        name: "roles93-3",

        isMenu: true,

        meta: { title: "角色管理3-3", icon: "el-icon-s-data" },

        component: () => import(/* webpackChunkName: "tes3" */ "@/views/test"),

      },

      {

        path: "/roles3-1",

        name: "roles93-1",

        isMenu: true,

        meta: { title: "角色管理3-1", icon: "el-icon-s-data" },

        children: [

          {

            path: "/a",

            name: "a",

            meta: { title: "角色管理1-1", icon: "el-icon-s-data" },

            component: () =>

              import(/* webpackChunkName: "test5555" */ "@/views"),

          },

        ],

      },

    ],

  },



  {

    path: "/401",

    name: "401",

    isMenu: false,

    component: () =>

      import(/* webpackChunkName: "401" */ "@/views/error/401.vue"),

  },

];

5.在基应用的MyPage页面中嵌入子应用

基座下发跳转指令

<!--  -->
<template>
  <div>
    <h1>子应用</h1>
    <!--
      name(必传):应用名称
      url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
      baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
     -->
    <micro-app
      name="app1"
      url="http://localhost:8888/"
      baseroute="/my-page"
      keep-alive
      @datachange="handleDataChange"
    ></micro-app>
  </div>
</template>

<script>
  import microApp from "@micro-zoe/micro-app";

  export default {
    data() {
      //这里存放数据
      return {};
    },
    //监听属性 类似于data概念
    computed: {},
    //监控data中的数据变化
    watch: {
      "$route.path": {
        handler(newValue, oldValue) {
          console.log("当前的数据", newValue);
          microApp.setData("app1", { path: newValue }); //向子应用发送路由数据
        },
        immediate: true,
      },
    },
    //方法集合
    methods: {
      AppTo() {},
    },
    //生命周期 - 创建完成(可以访问当前this实例)
    created() {},
    //生命周期 - 挂载完成(可以访问DOM元素)
    mounted() {
      // const childData = microApp.getData("app1"); // 返回子应用的data数据
      // console.log("childData", childData);
    },
    methods: {
      handleDataChange(e) {
        console.log("来自子应用的数据", e.detail);
      },
    },
  };
</script>
<style lang="scss" scoped></style>

子应用的操作

1、设置基础路由(如果基座应用是history路由,子应用是hash路由,这一步可以省略)

/router/index.js

import Vue from "vue";
import VueRouter from "vue-router";

const router = new VueRouter({
  // 👇 设置基础路由,子应用可以通过window.__MICRO_APP_BASE_ROUTE__获取基座下发的baseroute,如果没有设置baseroute属性,则此值默认为空字符串
  base: window.__MICRO_APP_BASE_ROUTE__ || process.env.BASE_URL,
  routes,
});


main.js

let app = new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

2、接收基应用传过来的数据,进行路由跳转

在APP.vue这里
<template>
  <div id="app">
    <router-view />
  </div>
</template>
<script>
export default {
  data() {
    //这里存放数据
    return {};
  },

  //生命周期 - 创建完成(可以访问当前this实例)
  created() {},
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    //第一种跳转
    // if (window.__MICRO_APP_BASE_ROUTE__) {
    //   window.microApp.addDataListener((data) => {
    //     console.log("data的数据在这里冲冲冲", data);
    //     this.$router.push({ path: data.path });
    //   });
    // }
  },
};
</script>
<style>
html,
body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}
</style>


在main.js这里

// 与基座进行数据交互
//第二种跳转
if (window.__MICRO_APP_ENVIRONMENT__) {
  console.log("我在微前端环境中");
  if (window.__MICRO_APP_BASE_ROUTE__) {
    // 监听基座下发的数据变化
    window.microApp.addDataListener((data) => {
      console.log("data的数据在这里冲冲冲", data);
      console.log("router.currentRoute.value.path", router.currentRoute.path);
      // 当基座下发path时进行跳转
      if (data.path && data.path !== router.currentRoute.path) {
        router.push({ path: data.path });
      }
    });
  }

  // 向基座发送数据
  setTimeout(() => {
    window.microApp.dispatch({ myname: "tenant-app" });
  }, 3000);
}

3.子应用隐藏左边侧边栏和上边头部栏

第一种方法:判断是否在微前端环境中,如果是,则隐藏,反之显示

<template>
  <el-container>
    <!-- undefined取反是true -->
    <el-header v-if="!isShow">
      <header-temp></header-temp>
    </el-header>
    <el-container>
      <el-aside v-if="!isShow" width="200px"
        ><sidebar class="sidebar-container"></sidebar
      ></el-aside>
      <el-main><app-main></app-main></el-main>
    </el-container>
  </el-container>
</template>
<script>
import AppMain from "./appMain"; // 页面布局的右侧区域
import sidebar from "./sideBar"; // 页面布局的左侧菜单
import headerTemp from "./headerTemp"; // 页面布局的header菜单
export default {
  name: "layout",
  components: { sidebar, AppMain, headerTemp },
  data() {
    return {
      isShow: window.__MICRO_APP_ENVIRONMENT__, //判断当前是否在微前端环境,如果在则是true 如果不在是undefined
    };
  },
  created() {
    console.log("isShow", this.isShow);
  },
};
</script>
<style>
.el-header {
  padding: 0 !important;
  margin-left: 180px;
}
</style>

 第二种方法:子应用中添加一个非layout布局的空页面,当子应用单独运行时,指向layout布局页面,如果是在微服务中使用,则指向空页面,在src/layout下创建EmptyLayout.vue

<template>
  <div class="emptyLayout">
    <router-view> </router-view>
  </div>
</template>
<style lang="scss" scoped>
.emptyLayout {
  height: 100%;
}
</style>

在src\router\index.js下

//尝试微前端获取layout
function getComponent() {
  if (window.__MICRO_APP_ENVIRONMENT__) {
    console.log("进入了这里了嘛 微前端");
    return () => import("@/components/layout/EmptyLayout.vue");
  } else {
    return Layout;
  }
}

component: getComponent(), //尝试微前端

总结

这里博主只是通过简单的两个项目进行微前端的搭建,具体项目不便编写,若有遇到的可以咨询博主,有用的的铁子可以一键三联,谢谢

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值