微前端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实现DIV滚动自动滚动到底部的代码
Mar 01 Javascript
JS Replace()的高级使用方法介绍
Jun 29 Javascript
javascript编写实用的省市选择器
Feb 12 Javascript
jquery插件corner实现圆角边框的方法
Mar 09 Javascript
jQuery的load()方法及其回调函数用法实例
Mar 25 Javascript
js实现文本框宽度自适应文本宽度的方法
Aug 13 Javascript
jquery Easyui快速开发总结
Aug 20 Javascript
Javascript小技能总结(推荐)
Jun 02 Javascript
AngularJS模板加载用法详解
Nov 04 Javascript
微信小程序实现点击按钮移动view标签的位置功能示例【附demo源码下载】
Dec 06 Javascript
小程序获取周围IBeacon设备的方法
Oct 31 Javascript
JavaScript offset实现鼠标坐标获取和窗口内模块拖动
May 30 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
是否存在第一台收音机的说法
2021/03/01 无线电
php使用json_encode对变量json编码
2014/04/07 PHP
PHP+ajaxfileupload+jcrop插件完美实现头像上传剪裁
2014/06/09 PHP
php基于curl扩展制作跨平台的restfule 接口
2015/05/11 PHP
CodeIgniter辅助之第三方类库third_party用法分析
2016/01/20 PHP
46 个非常有用的 PHP 代码片段
2016/02/16 PHP
javascript之Partial Application学习
2013/01/10 Javascript
javascript上传图片前预览图片兼容大多数浏览器
2013/10/25 Javascript
jQuery模拟点击A标记示例参考
2014/04/17 Javascript
jquery点击展示与隐藏更多内容
2016/12/03 Javascript
微信小程序开发之入门实例教程篇
2017/03/07 Javascript
动态加载、移除js/css文件的示例代码
2018/03/20 Javascript
Vue SSR 组件加载问题
2018/05/02 Javascript
详解Vue中数组和对象更改后视图不刷新的问题
2018/09/21 Javascript
webstorm+vue初始化项目的方法
2018/10/18 Javascript
基于Vue2-Calendar改进的日历组件(含中文使用说明)
2019/04/14 Javascript
python判断、获取一张图片主色调的2个实例
2014/04/10 Python
Python中实现的RC4算法
2015/02/14 Python
Linux下为不同版本python安装第三方库
2016/08/31 Python
Python利用BeautifulSoup解析Html的方法示例
2017/07/30 Python
tensorflow saver 保存和恢复指定 tensor的实例讲解
2018/07/26 Python
Python实现的批量修改文件后缀名操作示例
2018/12/07 Python
Python实现病毒仿真器的方法示例(附demo)
2020/02/19 Python
PyQt5实现仿QQ贴边隐藏功能的实例代码
2020/05/24 Python
pytorch随机采样操作SubsetRandomSampler()
2020/07/07 Python
行政部总经理岗位职责
2014/01/04 职场文书
关于人生的感言
2014/01/17 职场文书
教学器材管理制度
2014/01/26 职场文书
法律专业自荐信
2014/06/03 职场文书
宣传普通话标语
2014/06/27 职场文书
学校领导班子成员查摆问题及整改措施
2014/10/28 职场文书
2015教师节通讯稿
2015/07/20 职场文书
如何用python识别滑块验证码中的缺口
2021/04/01 Python
Python 把两层列表展开平铺成一层(5种实现方式)
2021/04/07 Python
PyQt5 显示超清高分辨率图片的方法
2021/04/11 Python
windows server2016安装oracle 11g的图文教程
2022/07/15 Servers