微前端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 相关文章推荐
我遇到的参数传递中 双引号单引号嵌套问题
Feb 11 Javascript
javascript温习的一些笔记 基础常用知识小结
Jun 22 Javascript
json对象转字符串如何实现
Dec 02 Javascript
防止文件缓存的js代码
Jan 10 Javascript
jQuery点击弹出下拉菜单的小例子
Aug 01 Javascript
javascript基于HTML5 canvas制作画箭头组件
Jun 25 Javascript
JS给Array添加是否包含字符串的简单方法
Oct 29 Javascript
微信小程序商城项目之淘宝分类入口(2)
Apr 17 Javascript
node.js 基于cheerio的爬虫工具的实现(需要登录权限的爬虫工具)
Apr 10 Javascript
js常见遍历操作小结
Jun 06 Javascript
基于纯JS实现多张图片的懒加载Lazy过程解析
Oct 14 Javascript
浅析我对JS延迟异步脚本的思考
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中Session的概念
2006/10/09 PHP
php中的数组操作函数整理
2008/08/18 PHP
php GeoIP的使用教程
2011/03/09 PHP
基于PHP异步执行的常用方式详解
2013/06/03 PHP
PHP保留两位小数并且四舍五入及不四舍五入的方法
2013/09/22 PHP
thinkphp3.x中变量的获取和过滤方法详解
2016/05/20 PHP
List the Codec Files on a Computer
2007/06/11 Javascript
jQuery 对象中的类数组操作
2009/04/27 Javascript
判断客户端浏览器是否安装了Flash插件的多种方法
2010/08/11 Javascript
10款非常有用的 Ajax 插件分享
2012/03/14 Javascript
利用js判断浏览器类型(是否为IE,Firefox,Opera浏览器)
2013/11/22 Javascript
判断window.onload是否多次使用的方法
2014/09/21 Javascript
AngularJs ng-repeat 嵌套如何获取外层$index
2016/09/21 Javascript
bootstrap常用组件之头部导航实现代码
2017/04/20 Javascript
浅谈sass在vue注意的地方
2017/08/10 Javascript
Material(包括Material Icon)在Angular2中的使用详解
2018/02/11 Javascript
实例详解Vue项目使用eslint + prettier规范代码风格
2018/08/20 Javascript
微信小程序视图容器(swiper)组件创建轮播图
2020/06/19 Javascript
JS+HTML实现自定义上传图片按钮并显示图片功能的方法分析
2020/02/12 Javascript
js实现网页版贪吃蛇游戏
2020/02/22 Javascript
JavaScript实现图片放大预览效果
2020/11/02 Javascript
python opencv之SURF算法示例
2018/02/24 Python
Python OpenCV获取视频的方法
2018/02/28 Python
django解决跨域请求的问题
2018/11/11 Python
详解PANDAS 数据合并与重塑(join/merge篇)
2019/07/09 Python
Pytorch 多维数组运算过程的索引处理方式
2019/12/27 Python
python使用openpyxl操作excel的方法步骤
2020/05/28 Python
Spartoo英国:欧洲最大的网上鞋店
2016/09/13 全球购物
香港草莓网土耳其网站:Strawberrynet TR
2017/03/02 全球购物
美国帽子俱乐部商店:Hat Club
2019/07/05 全球购物
马德里运动鞋商店:Nigra Mercato
2020/02/16 全球购物
公务员转正考察材料
2014/02/07 职场文书
新年晚会主持词
2014/03/24 职场文书
节能环保家庭事迹材料
2014/08/27 职场文书
优秀班主任推荐材料
2014/12/17 职场文书
初中政治教学工作总结
2015/08/13 职场文书