微前端qiankun改造日渐庞大的项目教程


Posted in Javascript onJune 21, 2022

项目背景

很多小伙伴在工作中都碰到过和我一样的场景,手上的某个项目越来越大,眼看着每次build时间越来越长,吐了?。在杭州某独角兽我碰到了这样的一个项目,他叫运营后台,听名字就知道,他的主要用户是运营人员。问题就是随着公司业务的越来越多,这个运营后台承担的已经不是某一块业务了,而是所有业务的运营操作的中后台都在这上面。你可以这样理解,这个系统的每个一级菜单都是一块独立的业务,相互之间没有任何瓜葛;按常规的理解,这应该是单独的每一个project比较合理,但是正因为他的用户又都是公司的同一群人,他们早已经习惯就在运营后台上去找自己的菜单进行业务操作,拒不接受在他的收藏夹里多出来好几个项目地址。那我们有没有一个办法让我们的项目更好维护,又能让用户不改变他们使用同一个项目的期望呢。这就是写这篇文章的初衷,就是微前端!!??

微前端的好处

除了可以解决上面的问题以外,你想想,只要我们把项目改造成了微前端,那每个业务都是独立的project,只不过最终用户都是在一个主项目里去使用,那我们每个project的技术栈也不用定死了,就不存在老项目的技术栈是vue,以至于后面的项目都必须要用vue了,你也可以用react,这就很香不是吗。微前端的原理就是项目被拆成了父子关系,通过基座去引用了子应用,子应用之间是互相隔离的??

qiankun

可能是你见过最完善的微前端解决方案?——官方是这么介绍的,基于single-spa,这里我就不详细介绍了,感兴趣的去看看文档,地址丢给你

改造过程

首先我先用vue2-admin-cli——我自己做的脚手架工具,创建两个vue-admin项目来演示,一个作为qiankun基座,另外一个就是我要引用的子应用。

全局安装脚手架
npm install -g vue2-admin-cli 
or
yarn global add vue2-admin-cli
创建项目
vue2-admin-cli init <project_name>
安装依赖
yarn
启动项目
yarn serve

运行起来就是这样的

微前端qiankun改造日渐庞大的项目教程

现在我们开始分别改造基座qiankun-base和子应用qiankun-vue,我想达到的效果是主应用qiankun-base只保留header sider footer的一个基本layout的布局,content部分全部加载子应用

qiankun-base

 yarn add qiankun # 或者 npm i qiankun -S

修改package.json启动命令修改启动端口

"serve": "vue-cli-service serve --port 80 --open"

src/router/index.ts修改路由模式为history 

const createRouter = () =>
  new VueRouter({
    mode: "history",
    routes: routes as any,
  });

修改vue.config.js   我这里之前用的路由模式是hash  上线配置了publicPath 导致改为history以后静态资源加载路径有问题所以修改

module.exports = {
  // publicPath: "./",  
  devServer: {
    disableHostCheck: true, // 关闭host检查
  },
};

在入口文件src/main.ts下注册微应用并启动:

import { registerMicroApps, start } from "qiankun";
registerMicroApps([
  {
    name: "qiankunVue",
    entry: "//localhost:8080", //子应用的启动端口修改为8080,基座使用80,不要相同
    container: "#qiankunVue",  //加载子应用的容器
    activeRule: "/qiankunVue", //路由匹配规则
  },
]);
// 启动 qiankun
start();

 在你要放置子应用的位置增加一个容器用于加载子应用 

src/components/layout/index.vue

<template>
  <el-container direction="vertical" style="height: 100%">
    <Header />
    <el-container style="overflow: auto">
      <el-aside width="250px">
        <Menu />
      </el-aside>
      <el-main>
        <el-breadcrumb
          separator-class="el-icon-arrow-right"
          v-if="showBreadcrumb"
        >
          <template v-for="(route, index) in matchedRoutes">
            <el-breadcrumb-item
              v-if="
                (route.meta && route.meta.breadcrumbTo === false) ||
                index === matchedRoutes.length - 1
              "
              :key="route.path"
            >
              {{ route.meta.title }}
            </el-breadcrumb-item>
            <el-breadcrumb-item
              v-else
              :key="route.path"
              :to="{ path: route.path }"
            >
              {{ route.meta.title }}
            </el-breadcrumb-item>
          </template>
        </el-breadcrumb>
        <!-- 本身的路由加载 -->
        <router-view style="margin-top: 20px" />
        <!-- 子应用加载容器 -->
        <div id="qiankunVue" style="width: 100%; height: 100%" />
      </el-main>
    </el-container>
  </el-container>
</template>

 将主应用之前的路由配置进行修改,不渲染自己的内容了,因为要改成去加载子应用的内容才是我们想要的,我的左侧菜单栏sider也是用路由配置这份文件生成的,所以我只需要注释这些路由要渲染的components就行,让他只充当一个生成sider菜单栏的作用,但是注意要保留容器所在的layout,因为我的子应用加载容器在这里面,加载子应用之前你必须保证容器被加载了

src/router/config.ts

const routes: Array<IBaseRouter> = [
  {
    path: "/",
    redirect: "/home",
    hidden: true,
  },
  {
    path: "/login",
    name: "login",
    hidden: true,
    component: () => import("../views/Login.vue"),
  },
  //保证子应用加载时容器页面必须加载
   {
    path: "/qiankunVue/*",
    name: "qiankunVue",
    hidden: true,
    component: Layout,
  },
  {
    path: "/qiankunVue/home",
    name: "home",
    component: Layout,
    redirect: "/qiankunVue/home/index",
    meta: {
      title: "首页",
      icon: "el-icon-s-home",
    },
    children: [
      {
        path: "index",
        name: "index",
        hidden: true,
        // component: () => import("../views/Home.vue"),
        meta: {
          title: "首页",
          breadcrumb: false,
        },
      },
      {
        path: "bar/:width/:height",
        name: "bar",
        props: true,
        hidden: true,
        // component: () => import("@/components/echarts/Bar.vue"),
        meta: {
          title: "柱状图",
          activeMenu: "/home/index",
        },
      },
      {
        path: "pie/:width/:height",
        name: "pie",
        props: true,
        hidden: true,
        // component: () => import("@/components/echarts/Pie.vue"),
        meta: {
          title: "饼图",
          activeMenu: "/home/index",
        },
      },
      {
        path: "line/:width/:height",
        name: "line",
        props: true,
        hidden: true,
        // component: () => import("@/components/echarts/Line.vue"),
        meta: {
          title: "折线图",
          activeMenu: "/home/index",
        },
      },
    ],
  },
  .....
  {
    path: "*",
    redirect: "/error/404",
    hidden: true,
  },
];
export default routes;

改完之后刷新看一看,这样基座项目就改造好了,保留了基本页面的框架,中间的内容到时候都由子应用来填充就行了

微前端qiankun改造日渐庞大的项目教程

qiankun-vue

修改package.json启动命令修改启动端口

"serve": "vue-cli-service serve --port 8080 --open"

入口文件 src/main.ts 修改

let vm: any = null;
function render(props: any = {}) {
  const { container } = props;
  vm = new Vue({
    router,
    store,
    render: (h) => h(App),
  }).$mount(container ? container.querySelector("#app") : "#app");
}
// 在被qiankun引用时 修改运行时的 `publicPath`
if ((window as any).__POWERED_BY_QIANKUN__) { 
  __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// 独立运行时
if (!(window as any).__POWERED_BY_QIANKUN__) {
  render();
}
导出三个生命周期函数
export async function bootstrap() {
  console.log("[vue] vue app bootstraped");
}
export async function mount(props: any) {
  console.log("[vue] props from main framework", props);
  render(props);
}
export async function unmount() {
  vm.$destroy();
  vm.$el.innerHTML = "";
  vm = null;
}
export default vm;

src/router/index.ts修改路由模式并增加base(和主应用设置的activeRule一致)

const createRouter = () =>
  new VueRouter({
    mode: "history",
    base: "/qiankunVue",
    routes: routes as any
  });

 打包配置修改(`vue.config.js`)

module.exports = {
  devServer: {
    disableHostCheck: true, // 关闭host检查
    headers: {
      "Access-Control-Allow-Origin": "*", // 防止加载时跨域
    },
  },
  configureWebpack: {
    output: {
      library: "qiankunVue",
      libraryTarget: "umd", // 把微应用打包成 umd 库格式
    },
  },
};

基座和子应用都修改完以后刷新看看,控制台报错了

微前端qiankun改造日渐庞大的项目教程

立马查看官方文档,发现是因为我的子应用加载容器在基座的某个路由页面即我的layout里面,文档里指出必须保证微应用加载时主应用这个路由页面也加载了,就很喜欢这种文档??,于是立马改一改

注释之前qiankun-base注册子应用时的启动qiankun命令,改到路由页面layout里面启动
src/main.ts

// 启动 qiankun
//start();
src/components/layout/index.vue
import { start } from "qiankun";
 mounted() {
    if (!(window as any).qiankunStarted) {
      (window as any).qiankunStarted = true;
      start();
    }
  }

重新刷新看看,成功了??,多少有点舒服了

微前端qiankun改造日渐庞大的项目教程

接下来要做的就是把子应用再改造一下,在qiankun中就只需要展示子应用content的内容,单独运行的时候为了方便调试我们就保留layout布局。看了这么久的官方文档,我当然知道用它就可以做出判断__POWERED_BY_QIANKUN__,思路很清晰,冲他??

qiankun-vue

src/components/layout/index.vue

<template>
  <el-container direction="vertical" v-if="isQiankun">
    <el-main>
      <el-breadcrumb
        separator-class="el-icon-arrow-right"
        v-if="showBreadcrumb"
      >
        <template v-for="(route, index) in matchedRoutes">
          <el-breadcrumb-item
            v-if="
              (route.meta && route.meta.breadcrumbTo === false) ||
              index === matchedRoutes.length - 1
            "
            :key="route.path"
          >
            {{ route.meta.title }}
            <!-- {{ route.path }} -->
          </el-breadcrumb-item>
          <el-breadcrumb-item
            v-else
            :key="route.path"
            :to="{ path: route.path }"
          >
            {{ route.meta.title }}
            <!-- {{ route.path }} -->
          </el-breadcrumb-item>
        </template>
      </el-breadcrumb>
      <router-view style="margin-top: 20px" />
    </el-main>
  </el-container>
  <el-container direction="vertical" style="height: 100%" v-else>
    <Header />
    <el-container style="overflow: auto">
      <el-aside width="250px">
        <Menu />
      </el-aside>
      <el-main>
        <el-breadcrumb
          separator-class="el-icon-arrow-right"
          v-if="showBreadcrumb"
        >
          <template v-for="(route, index) in matchedRoutes">
            <el-breadcrumb-item
              v-if="
                (route.meta && route.meta.breadcrumbTo === false) ||
                index === matchedRoutes.length - 1
              "
              :key="route.path"
            >
              {{ route.meta.title }}
              <!-- {{ route.path }} -->
            </el-breadcrumb-item>
            <el-breadcrumb-item
              v-else
              :key="route.path"
              :to="{ path: route.path }"
            >
              {{ route.meta.title }}
              <!-- {{ route.path }} -->
            </el-breadcrumb-item>
          </template>
        </el-breadcrumb>
        <router-view style="margin-top: 20px" />
      </el-main>
    </el-container>
  </el-container>
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
import Header from "./Header.vue";
import Menu from "./Menu.vue";
// import { IBaseRouter } from "@/router/config";
@Component({
  name: "Layout",
  components: { Header, Menu },
})
export default class Layout extends Vue {
  private get showBreadcrumb() {
    return this.$route?.meta?.breadcrumbAll !== false;
  }
  private get matchedRoutes() {
    return this.$route.matched?.filter(
      (v) => v.meta?.title && v?.meta?.breadcrumb !== false
    );
  }
  private get isQiankun() {
    return (window as any).__POWERED_BY_QIANKUN__;
  }
}
</script>
<style lang="less" scoped></style>

至此完结撒花,改造结束❤️❤️,看看效果 基座正常展示子应用

微前端qiankun改造日渐庞大的项目教程

子应用单独运行也正常展示,并且丝毫不影响开发体验

微前端qiankun改造日渐庞大的项目教程

项目地址

qiankun-base qiankun基座

qiankun-vue qiankun子应用

vue-admin ## vue 中后台系统解决方案

vue2-admin-cli vue2-admin-cli是vue-admin的cli脚手架工具,支持快速搭建企业级中后台项目模板

结尾

关于父子通信这个示例就不做概述,有兴趣的可以自己看看文档

以上就是微前端qiankun改造日渐庞大的项目教程的详细内容,更多关于微前端qiankun改造庞大项目的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
Javascript Web Slider 焦点图示例源码
Oct 10 Javascript
jQuery+ajax实现鼠标单击修改内容的方法
Jun 27 Javascript
JS实现列表的响应式排版(推荐)
Sep 01 Javascript
基于JS实现回到页面顶部的五种写法(从实现到增强)
Sep 03 Javascript
AngularJS 使用ng-repeat报错 [ngRepeat:dupes]
Jan 19 Javascript
js中new一个对象的过程
Feb 20 Javascript
vue中组件的过渡动画及实现代码
Nov 21 Javascript
Vue项目安装插件并保存
Jan 28 Javascript
vue实现PC端录音功能的实例代码
Jun 05 Javascript
JavaScript在web自动化测试中的作用示例详解
Aug 25 Javascript
js实现树形数据转成扁平数据的方法示例
Feb 27 Javascript
微信小程序scroll-view不能左右滑动问题的解决方法
Jul 09 Javascript
JavaScript架构localStorage特殊场景下二次封装操作
Jun 21 #Javascript
js前端图片加载异常兜底方案
Jun 21 #Javascript
JavaScript中10个Reduce常用场景技巧
Jun 21 #Javascript
js前端面试常见浏览器缓存强缓存及协商缓存实例
Jun 21 #Javascript
JavaScript前端面试组合函数
Jun 21 #Javascript
Vue2项目中对百度地图的封装使用详解
JavaScript原型链中函数和对象的理解
You might like
用文本文件制作留言板提示(上)
2006/10/09 PHP
php使用反射插入对象示例分享
2014/03/11 PHP
php中数组最简单的使用方法
2020/12/27 PHP
IE6 弹出Iframe层中的文本框“经常”无法获得输入焦点
2009/12/27 Javascript
js 获取屏幕各种宽高的方法(浏览器兼容)
2013/05/15 Javascript
浅析Cookie中的Path与domain
2013/12/18 Javascript
浅谈JavaScript的事件
2015/02/27 Javascript
javascript中callee与caller的区别分析
2015/04/20 Javascript
js数组去重的方法汇总
2015/07/29 Javascript
jquery实现仿新浪微博带动画效果弹出层代码(可关闭、可拖动)
2015/10/12 Javascript
基于BootStarp的Dailog
2016/04/28 Javascript
localStorage实现便签小程序
2016/11/28 Javascript
jQuery扩展+xml实现表单验证功能的方法
2016/12/25 Javascript
JQuery选中select组件被选中的值方法
2018/03/08 jQuery
详解ajax的data参数错误导致页面崩溃
2018/04/30 Javascript
Vue一次性简洁明了引入所有公共组件的方法
2018/11/28 Javascript
jQuery AJAX与jQuery事件的分析讲解
2019/02/18 jQuery
js+canvas实现两张图片合并成一张图片的方法
2019/11/01 Javascript
javascript数组元素删除方法delete和splice解析
2019/12/09 Javascript
详解JavaScript中的Object.is()与&quot;===&quot;运算符总结
2020/06/17 Javascript
vue swipeCell滑动单元格(仿微信)的实现示例
2020/09/14 Javascript
[40:27]完美世界DOTA2联赛PWL S3 PXG vs GXR 第一场 12.19
2020/12/24 DOTA
python编码总结(编码类型、格式、转码)
2016/07/01 Python
Python GUI布局尺寸适配方法
2018/10/11 Python
python读取几个G的csv文件方法
2019/01/07 Python
python自制包并用pip免提交到pypi仅安装到本机【推荐】
2019/06/03 Python
python输入错误后删除的方法
2019/10/12 Python
wxPython绘图模块wxPyPlot实现数据可视化
2019/11/19 Python
基于Python 的语音重采样函数解析
2020/07/06 Python
HTML5各种头部meta标签的功能(推荐)
2017/03/13 HTML / CSS
Vision Directa智利眼镜网:框架眼镜、隐形眼镜和名牌太阳眼镜
2016/11/23 全球购物
大学总结自我鉴定
2014/01/18 职场文书
绿色学校实施方案
2014/03/31 职场文书
高一学生期末评语
2014/04/25 职场文书
2015年员工试用期工作总结
2014/12/12 职场文书
邀请书模板
2015/02/02 职场文书