zoukankan      html  css  js  c++  java
  • antdesignpro使用qiankun微服务详解

    微服务现在挺火的,优点也很明显
    如果有多个应用都有相同页面时,就可以使用微服务,可以避免重复写代码
    在网上搜了下,很多例子都是基于官方文档的例子,官方文档:https://umijs.org/zh-CN/plugins/plugin-qiankun.比较简单,实际使用场景会有特殊情况
    我根据自己项目的情况总结了一下使用方法
    我们两个项目都是ant-design-pro的,我这里的例子也用的ant-design-pro项目生成的, githup地址:https://github.com/ant-design/ant-design-pro
     
    微服务需要有主应用和子应用   一个子应用可以配置多个相关联的主应用,配置方法都是一样的

    这是我的项目,一个主应用,一个子应用
     
    首先,主应用和子应用都要安装yarn add @umijs/plugin-qiankun -D

    主应用配置

    1. config.ts配置

     

    2. app.tsx配置,这个entry,就是子应用的地址,可以写成变量形式,区分本地和线上环境
     
    3. 修改document.ejs文件,可以解决页面一直加载问题

     

    4. 修改routes文件

     

    5. 配置proxy.tx接口代理.因为在主应用,请求的地址是主应用的,要代理回子应用的请求地址,如果已经有类似/api这种前缀,要注意主应用和子应用区分,不要用相同的前缀

     

    子应用配置
    1. config.ts配置
      qiankun: {
        slave: {},
      }
    2. app.tsx配置.子应用可以通过生命周期函数拿到主应用传递的参数.如果子应用本身是有菜单,面包屑等,应该要区别,在主应用不显示,否则会重复
    let isMenu = true // 设置一个变量,判断是否需要展示layout
    // ProLayout 支持的api https://procomponents.ant.design/components/layout
    export const layout: RunTimeLayoutConfig = ({ initialState }) => {
      console.log(initialState)
      const prop = {}
      if (!isMenu) { // 如果是加载在主应用中,不展示菜单和头部
        prop.menuRender = false
        prop.headerRender = false
        prop.contentStyle = { margin: 0 }
    
      }
      return {
        rightContentRender: () => <RightContent />,
        disableContentMargin: false,
        waterMarkProps: {
          content: initialState?.currentUser?.name,
        },
        footerRender: () => <Footer />,
        headerContentRender: () => <ProBreadcrumb />,
        breadcrumbRender: (routers = []) => [
          {
            path: '/',
            breadcrumbName: '主页',
          },
          ...routers,
        ],
        onPageChange: () => {
          const { location } = history;
          // 如果没有登录,重定向到 login
          if (!initialState?.currentUser && location.pathname !== loginPath) {
            history.push(loginPath);
          }
        },
        links: isDev
          ? [
              <Link to="/umi/plugin/openapi" target="_blank">
                <LinkOutlined />
                <span>OpenAPI 文档</span>
              </Link>,
              <Link to="/~docs">
                <BookOutlined />
                <span>业务组件文档</span>
              </Link>,
            ]
          : [],
        menuHeaderRender: undefined,
        // 自定义 403 页面
        // unAccessible: <div>unAccessible</div>,
        ...prop,
        ...initialState?.settings,
      };
    };
    
    export const qiankun = {
      // 应用加载之前
      async bootstrap(props: any) {
        console.log('app1 bootstrap', props);
        if (props) {
          isMenu = props.isMenu
          const logins = async() => {
            await login({ ...props.accountInfo })
          }
          logins()
        }
      },
      // 应用 render 之前触发
      async mount(props: any) {
        console.log('app1 mount', props);
      },
      // 应用卸载之后触发
      async unmount(props: any) {
        console.log('app1 unmount', props);
      },
    }
    3.routes.ts文件,正常配置路由就可以了
    ...
    {
        name: 'Form表单',
        icon: 'form',
        path: '/form',
        routes: [
          {
            path: '/form',
            redirect: '/form/add'
          },
          {
            name: 'form表单',
            icon: 'table',
            path: '/form/add',
            component: './product/addProduct',
          },
          {
            name: 'debonceselect',
            icon: 'table',
            path: '/form/debonceselect',
            component: './form/debounce',
          },
          {
            name: 'upload上传',
            icon: 'table',
            path: '/form/upload',
            component: './form/upload',
          },
          {
            name: 'child详情页',
            icon: 'table',
            path: '/form/detail', // 主应用配置时路由要和子应用一致
            component: './form/detail',
          },
          {
            name: 'child详情页2',
            icon: 'table',
            path: '/form/detail2',
            component: './form/detail2',
          }
        ],
      },
    4. proxy.ts文件
    export default {
      dev: {
        '/childapi/api/': {
          target: 'http://localhost:8091',
          changeOrigin: true,
          pathRewrite: { '^/childapi': '' },
        },
      },
      test: {
        '/api/': {
          target: 'https://preview.pro.ant.design',
          changeOrigin: true,
          pathRewrite: { '^': '' },
        },
      },
      pre: {
        '/api/': {
          target: 'your pre url',
          changeOrigin: true,
          pathRewrite: { '^': '' },
        },
      },
    };
    运行效果
    主应用页面:

     

    子应用页面:

     

    几个项目实际要解决的问题

    1. 子应用登录态问题
    如果子应用不需要登录,只是查看可以忽略这个问题
    如果主应用和子应用都有自己的登录态.如果不做任何处理,想要在主应用运行子应用,必须要同时运行子应用,并且子应用要登录,这样不太合理
    一般处理,主应用传递token,在子应用请求时加上这个token
    let token = ''
    const middleware: OnionMiddleware = async (ctx, next) => {
      const {
        req: { url },
      } = ctx
    
      ctx.req.options.headers = { // 在请求头加入传递的token
        token,
      }
    
      await next()
    
      if (ctx.res.responseCode !== '000000') {
        if (!(ctx.res && ctx.res.size)) throw { ...ctx.res, url }
      }
    }
    
    export const qiankun = {
      // 应用加载之前
      async bootstrap(props: any) {
        if (props) {
          token = props.token
        }
      },
      // 应用 render 之前触发
      async mount(props: any) {
        console.log('app1 mount', props)
      },
      // 应用卸载之后触发
      async unmount(props: any) {
        console.log('app1 unmount', props)
      },
    }
    
    export const request: RequestConfig = {
      prefix: URL_PREFIX,
      method: 'POST',
      middlewares: [middleware],
      credentials: 'include',
      errorHandler,
    }
    我这个例子因为用的官方的项目,直接模拟了下,调用了子应用的登录接口
    export const qiankun = {
      // 应用加载之前
      async bootstrap(props: any) {
        console.log('app1 bootstrap', props);
        if (props) {
          isMenu = props.isMenu
          const logins = async() => { // 调用登录接口
            await login({ ...props.accountInfo }) // 传递账户信息过来
          }
          logins()
        }
      },
      // 应用 render 之前触发
      async mount(props: any) {
        console.log('app1 mount', props);
      },
      // 应用卸载之后触发
      async unmount(props: any) {
        console.log('app1 unmount', props);
      },
    }
    这样不用打开子应用也能加载子应用页面了

    2. 子应用运行时,会自动添加前缀
    因为qiankun框架的代码切割,子应用运行时会根据package.json里的name加入一个前缀.如果不想要可以在config.ts里配置base: '/'.我这里是换了一个前缀名称

     

     

    3. 子应用路由匹配问题.为了主应用的路由地址和子应用一致,配置路由时microAppProps的base传了''
    这样会导致子应用页面加载错误或登录态失效时,页面显示404页面.因为现在是精确的路由匹配,没有登录会重定向到登录页,这样就会找不到页面.一般设置好登录态不会有这个问题,不过想要完善点,可以写上错误路由跳转到登录页面,或者提示刷新页面等
     
     
    如果遇到跟着文档配置,项目启动报错问题,可能是项目运行问题,可以尝试重启,删包重装.在加入qiankun组件前,可以备份代码,配置运行好后再加入代码中, 以免出现代码运行不了,恢复不了情况o(╯□╰)o

    微服务其实有很多功能,不过目前也只用了些简单的功能.如果有其他业务场景,可以留言,共同学习进步呀(*^▽^*)

    最近新写了一篇关于子应用动态配置成主应用主题色的方法,有需要可以看看 https://www.cnblogs.com/steamed-twisted-roll/p/15754035.html

  • 相关阅读:
    【二分图匹配/匈牙利算法】飞行员配对方案问题
    【模板/学习】匈牙利算法
    【tarjan缩点+分层图】草鉴定Grass Cownoisseur
    【微笑】
    【质因数分解】SAC E#1 一道中档题 Factorial
    【dfs+dp】砝码称重
    【背包dp】自然数拆分Lunatic版
    【单调队列】最大子序和
    【单调队列】滑动窗口
    bzoj 2834: 回家的路
  • 原文地址:https://www.cnblogs.com/steamed-twisted-roll/p/15697081.html
Copyright © 2011-2022 走看看