[qiankun]微前端实现方案

微前端的实现方案-qiankun

2018年 Single-SPA诞生了, single-spa是一个用于前端微服务化的JavaScript前端解决方案 (本身没有处理样式隔离、js执行隔离) 实现了路由劫持和应用加载;

2019年 qiankun基于Single-SPA, 提供了更加开箱即用的 API (single-spa + sandbox + import-html-entry),它 做到了技术栈无关,并且不推荐抽取公共方法,工具等,它推荐的是每个微服务是独立的,并且不相互影响的,并且接入简单。

微前端的框架现在比较成熟的是 “Single-spa” 和 “QianKun”, 而"QianKun"是基于Single-spa实现的,所以使用"QianKun"可能会更加的简单些,所以采用了笔者采用该方案去实现

实践环境

 "@vue/cli-service": "~5.0.0-beta.6",
 "ant-design-vue": "^2.2.8",
 "vue": "^3.2.20",

  nginx:1.19.0
  node:V12.18.2

主应用与子应用都是vue3.x开发的,下面开始实现的具体过程

实践代码

因为是在现有的vue3.x项目的基础上去实现微服务,因此,整个实践过程并不是从零开始的,也即对现有的可运行项目的改造过程

对于项目的整个构想如下图所示
在这里插入图片描述
主应用基本是作为一个加载器的功能,只有登录,登录后获取到当前用户的所有菜单,然后展示所有的菜单,用于导航,在主应用注册所有的微服务,并提供公共信息给微服务,上图中的公共设置主要是404等展现的公共路由等

基本所有的导航都是微服务

端口号是否必须不同?

本地测试的时候,因为是本地启动的项目,都是localhost,只能通过端口号区分不同的微服务,所以端口必须不相同;
但是线上子应用,是可以单独部署的,所以端口号可能相同,也可能不相同,此时就不影响,只要部署成功即可

路由模式是否必须是history?
虽然网上大部分都是推荐的使用history路由模式,但是也支持使用哈希模式–createWebHashHistory

本次实践过程中所有的项目都是vue3.x开发的,以下是对项目的改造

主应用改造

  1. 引入qiankun
npm i qiankun
  1. 设置qiankun的启动标志

window[“qiankunStarted”]=false;

该标志主要是用于确定是否已经启动qiankun服务,因为不能重复注册与启动,所以设置了该标志

  1. 注册微服务

history路由的注册方式

registerMicroApps([
  {
    name: 'child1', //子应用的名称,和子应用的ouput名称必须一致
    entry: '//localhost:8081',//微服务的入口
    container: '#sonApp',//主应用中用于展示微服务的container
    activeRule: '/child/child1',         // 子应用触发规则(路径)
  },
]);

hash路由的注册方式

// 注册子应用
registerMicroApps([
  {
    name: 'child1', 
    entry: '//localhost:8081',//微服务的入口
    container: '#sonApp',
    activeRule: '#/micro/xxx/cli', // hash路由的注册方式
  },
]);

通过实践发现注册子应用不是必须在main.ts文件中,可以是真正呈现子应用的页面中进行注册,只要确保加载微服务的时候已经注册了就可以了

container的值是否有什么要求?是否#sonApp必须是直接在App.vue中,还是在App.vue的组件中也可以?

container的id是用来加载子应用的dom的Id,因此只要保证加载微服务的时候该值存在即可,可以是App.vue文件中直接存在,也可以是用来展示微服务的其它vue单文件中的dom的id值

  1. 启动微服务
  start({
     prefetch:false//是否开启预加载
  })

如果注册了多个微服务,开启预加载会加载其它微服务,该值默认是开启状态,如果微服务存在有问题的,打开预加载其实增加了调试的难度,所以可以选择关闭

完整代码


      // 注册子应用 && 启动微服务
      if(!window["qiankunStarted"]){
        registerMicroApps([
          {
            name: 'cli5-beta6-test', //和导出微服务的名称相同
            entry: 'http://localhost:8081',
            container: '#micro',
            activeRule: '#/micro/cli',         // 子应用触发规则(路径)
          },
          {
            name:'micro-permission-web', 
            entry: 'http://localhost:8083',
            container: '#micro',
            activeRule: '#/micro/permission',         // 子应用触发规则(路径)
          },
        ],{
          beforeLoad: [
            app => {
               console.log(`${app.name}的beforeLoad阶段`)
              return Promise.resolve();
            }
          ],
          beforeMount: [
            app => {
               console.log(`${app.name}的beforeMount阶段`)
              return Promise.resolve();
            }
          ],
          afterMount: [
            app => {
               console.log(`${app.name}的afterMount阶段`)
              return Promise.resolve();
            }
          ],
          beforeUnmount: [
            app => {
               console.log(`${app.name}的beforeUnmount阶段`)
              return Promise.resolve();
            }
          ],
          afterUnmount: [
            app => {
              console.log(`${app.name}的afterUnmount阶段`)
              return Promise.resolve();
            }
          ]
        });
        
        start({// 开启服务
          prefetch:false//是否开启预加载,开启预加载会加载其它微服务
        })

        addGlobalUncaughtErrorHandler((event) => {
          //捕获微服务加载失败的错误
          if(undefined!=event["error"].appOrParcelName){
            //子应用加载失败跳转404
            router.push("/404")
          }
        });

        //启动qiankun的标志
        window["qiankunStarted"]=true;
      }

子应用改造

子应用需要做的改造包括:

  1. 微服务的导出

vue.config.js
添加

const config = require('./package');

module.exports的devServer中添加

headers: {
    'Access-Control-Allow-Origin': '*',
 },

否则本地测试的时候,主应用会报跨域的错误

module.exports的根节点中添加配置

configureWebpack:{
        output: {
          library: {
            name:`${config.name}`,//这里的导出值必须和主应用注册的时候的name值相同,否则会导致引入问题
            type:'umd' // 把子应用打包成 umd 库格式
          },
          // jsonpFunction: `webpackJsonp_${config.name}`,//该值发现随着webpack的升级没有了,而且该值不影响跨域等问题,可以忽略该值
        }
 }

${config.name}必须和主应用注册的微服务名称相同,否则加载失败

  1. 获取当前子应用被加载的环境

public-path.ts

在src目录下直接添加 public-path.ts文件

if ((window as any).__POWERED_BY_QIANKUN__) {
    __webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

该文件主要是用获取当前微服务所处的环境,是浏览器直接打开的,还是作为微服务被加载的
如果当前环境是微服务,则(window as any).INJECTED_PUBLIC_PATH_BY_QIANKUN qiankun的注入值是true
设定__webpack_public_path__的值为当前环境值

  1. 微服务的入口文件

main.ts

main.ts的修改包括最上面添加:

import "./public-path";

用于获取当前环境值,然后在引入其它文件后,添加

  const isQiankun = (window as any).__POWERED_BY_QIANKUN__


function render(props:any = {}) {
    const { container } = props;
    const app = createApp(App)
    app.use(store).use(router).mount(container ? container.querySelector("#child") : "#child")//注意该值不能和主应用的值相同,该值为子应用index.html中的id值
}
/**
 * 每次应用作为微服务被进入时都会调用 bootstrap 方法,并且只有第一次加载微服务会触发bootstrap
 */
export async function bootstrap() {
    console.log('app bootstraped');
}
/**
 * 每次应用作为微服务被进入时都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props: any) {
    console.log("child application mount",props)
    render(props)
}

/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例,
 * 这里需要根据具体项目进行卸载,否则二次进入的时候会有问题
 */
export async function unmount(props: any) {
    console.log(props)
}

/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update(props: any) {
    console.log('update props', props);
}

//独立运行时
isQiankun || render();

子应用之间的id是否可以重复?

子应用id之间是可以重复的,因为子应用之间嵌入在主应用内部的,如果不存在同时在一个页面加载多个微服务,则没有影响,但是主应用和子应用最好不要相同,因为可能会存在于同一个页面,主应用的挂载Id可以变更

子应用配置完成后是否可以直接运行查看?

可以的,通过标志位判断当前微服务的运行环境,是运行在qiankun的环境下,还是独立运行,若是独立运行则直接渲染

isQiankun || render();

判断是否是在启动qiankun的环境下,如果不是则执行render(),启动项目,方便调试

注意需要设置unmount卸载内容,否则导致重复加载,已挂载,资源已注册等等问题

vue3.x 中的卸载方式

export async function unmount(props: any) {
    console.log("micro application unmount", props)
    app.unmount();
    app = null;
    router = null;
}

  1. 微服务的路由前缀

用于区分其它微服务,尤其是本地测试的时候,也可以根据主应用传递的值设置微服务的路由前缀

const router = createRouter({
  history: createWebHashHistory((window as any).__POWERED_BY_QIANKUN__ ? "/#/micro/xxx" : "#"),
  routes
});

动态设置微服务的前缀,微服务的卸载,比较完整的改造,子应用的完整代码:

const isQiankun = (window as any).__POWERED_BY_QIANKUN__

let app = null;
let router = null;

/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
function render(props: any = {}) {
    const { container } = props;
    console.log("application render",store)
    app=createApp(App, {
        setup() {
        	//全局设置国际化,使用可以直接用$t('page.button.add')
            const { t } = useI18n({ useScope: 'global' })
            return { t }
        }
    });
    const activeRule=store.state.activeRule;
    router = createRouter({
   		//动态设置前缀
        history: createWebHashHistory(isQiankun? activeRule : ""),
        routes
    });
    app.use(i18n).use(store).use(router).mount(container ? container.querySelector("#app") : "#app")
}

export async function bootstrap() {
    console.log('app bootstraped');
}

export async function mount(props: any) {
    // props为注册该微服务时主应用传递的值
    // console.log("micro application mount", props)
    props.onGlobalStateChange((state, prev) => {
      console.log("micro onGlobalStateChange mount");
      // state: 变更后的状态; prev 变更前的状态
    //   console.log(state, prev);

    });
    //vuex改变微服务的路由前缀值
    store.commit("setProjectActiveRule", props.activeRule)
    // props.setGlobalState(state);
    render(props)
}

/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props: any) {
    console.log("micro application unmount", props)
    app.unmount();
    app = null;
    router = null;
}

/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update(props: any) {
    // console.log('micro application update props', props);
}

//独立运行时
isQiankun || render();

通信问题

主应用传递信息

目前比较简单的通讯方式,并且是主应用传递信息给微服务

registerMicroApps([{
                name:projectName, 
                entry:projectDns,
                container: '#micro',
                activeRule:projectActiveRule,         // 子应用触发规则(路径)
                props:{
                  user:{ },
                  activeRule:projectActiveRule//用于动态设置子应用的路由前缀
                }
            }
          }])

以上就是简单的微服务实现过程,因为在实现的过程中遇到的问题过多,内容过长因此进行了拆分,如果此时以满足你的需求你就可以忽视以下内容了

微服务接受信息方式参考上面 4. 微服务的路由前缀

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
qiankun是一个由蚂蚁金服开源的前端解决方案,旨在解决多个团队或多个子系统之间协同开发的问题,同时提高前端应用的可维护性和可扩展性。 前端是指将一整个网站或应用分为若干个小的部分,每个部分都是一个独立的子应用,可以由不同的团队或开发者进行独立开发和管理,并最终将这些子应用集成起来形成一个完整的网站或应用。前端可以提高应用的可伸缩性、可维护性和灵活性,同时为团队之间协同开发提供了更好的支持。 qiankun通过使用Vue、React等主流框架来构建子应用,并通过一个主应用来动态加载和协调这些子应用,有效解决了子应用的模块化开发、独立部署和协同开发的问题。此外,qiankun还提供了完备的生命周期钩子函数和通信机制,方便子应用与主应用之间进行数据交互和状态同步。 除此之外,qiankun还支持多种场景下的子应用加载方式,包括iframe、HTTP、和WebComponents等,灵活地满足了不同场景下的应用需求。同时,qiankun还提供了插件化的开发模式,可以集成多种生态工具进行开发,如React Devtools、Vue Devtools、Webpack等。 综上所述,qiankun是一款非常优秀的前端解决方案,不仅提升了前端的可维护性和可扩展性,同时为团队协同开发提供了更好的支持,对于企业级应用开发来说具有非常重要的意义。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三知之灵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值