微前端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 相关文章推荐
js获得参数的getParameter使用示例
Feb 26 Javascript
Android中的jQuery:AQuery简介
May 06 Javascript
js函数调用的方式
May 06 Javascript
JS获取当前日期时间并定时刷新示例
Mar 04 Javascript
javascript学习总结之js使用技巧
Sep 02 Javascript
基于JavaScript实现瀑布流效果(循环渐近)
Jan 27 Javascript
深入浅析JSON.parse()、JSON.stringify()和eval()的作用详解
Apr 03 Javascript
大型JavaScript应用程序架构设计模式
Jun 29 Javascript
完美实现八种js焦点轮播图(上篇)
Jul 18 Javascript
VUE渲染后端返回含有script标签的html字符串示例
Oct 28 Javascript
js实现3D照片墙效果
Oct 28 Javascript
JavaScript实现拖动对话框效果的实现代码
Oct 12 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
php导入模块文件分享
2015/03/17 PHP
php异步:在php中使用fsockopen curl实现类似异步处理的功能方法
2016/12/10 PHP
php解决约瑟夫环算法实例分析
2019/09/30 PHP
laravel5.6框架操作数据curd写法(查询构建器)实例分析
2020/01/26 PHP
在网站上应该用的30个jQuery插件整理
2011/11/03 Javascript
《JavaScript高级程序设计》阅读笔记(二) ECMAScript中的原始类型
2012/02/27 Javascript
自动最大化窗口的Javascript代码
2013/05/22 Javascript
js解析json读取List中的实体对象示例
2014/03/11 Javascript
js针对ip地址、子网掩码、网关的逻辑性判断
2016/01/06 Javascript
Angularjs material 实现搜索框功能
2016/03/08 Javascript
JavaScript记录光标在编辑器中位置的实现方法
2016/04/22 Javascript
Node.js调用fs.renameSync报错(Error: EXDEV, cross-device link not permitted)
2017/12/27 Javascript
使用ThinkJs搭建微信中控服务的实现方法
2019/08/08 Javascript
layui 弹出层值回传解决方式
2019/11/14 Javascript
js消除图片小游戏代码
2019/12/11 Javascript
Vue实现跑马灯效果
2020/05/25 Javascript
js+for循环实现字符串自动转义的代码(把后面的字符替换前面的字符)
2020/12/24 Javascript
Python实现的单向循环链表功能示例
2017/11/10 Python
Django使用paginator插件实现翻页功能的实例
2018/10/24 Python
一篇文章搞懂Python的类与对象名称空间
2018/12/10 Python
使用python批量化音乐文件格式转换的实例
2019/01/09 Python
Pytorch环境搭建与基本语法
2020/06/03 Python
Django rest framework分页接口实现原理解析
2020/08/21 Python
python爬虫请求头的使用
2020/12/01 Python
python基于selenium爬取斗鱼弹幕
2021/02/20 Python
html5 figure和figcaption的使用方法
2018/09/10 HTML / CSS
ghd澳大利亚官方网站:英国最受欢迎的美发工具品牌
2018/05/21 全球购物
校园安全教育广播稿
2014/02/17 职场文书
市政管理求职信范文
2014/05/07 职场文书
优秀大专毕业生求职信
2014/08/04 职场文书
合同和协议有什么区别?
2014/10/08 职场文书
怎样写离婚协议书
2015/01/26 职场文书
单位收入证明范本
2015/06/18 职场文书
法人代表证明书范本
2015/06/18 职场文书
《悬崖边的树》读后感2篇
2019/12/02 职场文书
5种方法告诉你如何使JavaScript 代码库更干净
2021/09/15 Javascript