Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC]@umijs/plugin-qiankun 2.0 的用法改进 #64

Closed
kuitos opened this issue Mar 3, 2020 · 9 comments · Fixed by #148
Closed

[RFC]@umijs/plugin-qiankun 2.0 的用法改进 #64

kuitos opened this issue Mar 3, 2020 · 9 comments · Fixed by #148

Comments

@kuitos
Copy link
Member

kuitos commented Mar 3, 2020

1.x 的用法是在插件里注册一组应用数据:

'@umijs/plugin-qiankun',
  {
  master: {
    // 注册子应用信息
    apps: [
      {
        name: 'app1', // 唯一 id
        entry: '//localhost:7001', // html entry
        base: '/app1', // app1 的路由前缀,通过这个前缀判断是否要启动该应用,通常跟子应用的 base 保持一致
        history: 'browser', // 子应用的 history 配置,默认为当前主应用 history 配置
      },
      {
        name: 'app2',
        entry: {
          // TODO 支持 config entry
          scripts: [],
          styles: [],
        },
        base: '/app2',
      },
    ],
    jsSandbox: true, // 是否启用 js 沙箱,默认为 false
    prefetch: true, // 是否启用 prefetch 特性,默认为 true
  },
},

当用户场景比较简单,比如所有的应用渲染到一个固定的容器里,这个使用方式足够 cover 了。

但一旦场景复杂了,会触发各种问题或者咨询。

比如子应用挂载的容器是动态渲染的:

<Layout>
  <Router>
  	<Route path="/app1"><AppContainer id="container1"/></Route>
    <Route path="/app2"><AppContainer id="container2"/></Route>
  </Router>
</Layout>

由于路由及事件都是异步的,这种场景下很难确保子应用的 mount 已经开始的时候,对应路由的 Route 组件是已经渲染完毕的,导致由于子应用找不到挂载点而抛异常的问题出现。

而且现在在 plugin 场景下,默认都是 route-based 的用法,对于 bigfly 之类的场景,期望应用在某个指定的局部渲染,而本身跟路由是无关的,支持起来也会比较麻烦。

期望的方式

插件配置只需要配置纯粹的子应用元信息,如只需要有 nameentry 这两个字段:

'@umijs/plugin-qiankun',
  {
  master: {
    // 注册子应用信息
    apps: [
      {
        name: 'app1', // 唯一 id
        entry: '//localhost:7001', // html entry
      },
      {
        name: 'app2',
        entry: '//localhost:7002',
      },
    ],
    jsSandbox: true, // 是否启用 js 沙箱,默认为 false
    prefetch: true, // 是否启用 prefetch 特性,默认为 true
  },
},

应用还是全量在插件配置里注册,好处是可以集中管理子应用信息,也可以方便的做预加载,但是应用激活不再单一的依赖路由状态。

直接基于 umi 的路由配置关联子应用

比如可以直接在 umi routes 里这样配置:

const routes = [
  {
    path: '/app1',
    microApp: 'app1'
  },
  {
    path: '/app2',
    microApp: 'app2'
  },
  {
    path: '/app3',
    microApp: 'app3'
  },
]

在实现上,配置了 microApp 的路由,插件会自动生成一个容器组件,并添加到 component 属性上,容器组件的实现里负责激活对应的应用,并在容器销毁时卸载应用。这样就能很好的保证子应用加载过程中的时序。

直接使用 React 组件渲染子应用

当我们希望不依赖路由配置,将子应用渲染到任意我们期望的地方时,这样用就可以了:

import { MicroApp } from 'umi';

function MyPage() {
  return (
    <div>
      <div>
        <MicroApp name="app1" />
      </div>
    </div>
  )
}

MicroApp 也是由插件生成,可以直接复用路由方式生成的容器组件。

可能存在的问题

  1. 不支持主应用接入其他 history 模式的子应用,也就是主子应用 history mode 必须一致。不确定 umi 场景下这种用户有多少。
  2. qiankun 可能要做些改造,而且现阶段的能力改造后也不定能支持。因为现在这种方式实际上应用的 activeRule 是变化的,同一个应用放到不同的容器里 activeRule 就完全不同,而在目前 qiankun/single-spa 的逻辑里,每个子应用的 activeRule 是固定的。
  3. 第二种直接用 MicroApp 组件渲染的方式,子应用大概率要求不能是有路由的系统,不然就会有改造的需求,因为有路由意味着子应用不是在什么 url 下都能渲染的。
  4. 微应用嵌套?感觉不是插件需要考虑的。
@paranoidjk
Copy link

主子应用 history mode 必须一致

这点如果子应用是云凤蝶的话,倒是没有问题,我们做了特殊支持,follow 了 qiankun 给的主应用 history

子应用大概率要求不能是有路由的系统

这个在云凤蝶也可行,虽然比较奇怪,因为一个云凤蝶站点里面用户会做很多页面,那么这里必然就引入了路由系统,除非用户只能说我只想嵌入某一个云凤蝶的页面,那么我的 runtime 可以把其它的页面都删掉,倒是可以特殊剥掉外层的路由系统,但这也就意味着这个 MircoApp 只能嵌入一个 Page,而且这个 Page 里面云凤蝶的开发者也不要使用云凤蝶的 router api,因为没有云凤蝶的 router 了

@eynol
Copy link
Contributor

eynol commented Apr 1, 2020

除非用户只能说我只想嵌入某一个云凤蝶的页面,那么我的 runtime 可以把其它的页面都删掉,倒是可以特殊剥掉外层的路由系统

对于React应用来说,一个路由下的一个页面只是一个 url => 组件 的映射,url只是一种状态,用户想要的效果是在组件(页面)之间跳转,所以才会去跳转url。这里的url就变成了组件的id而已。

之前umijs-qiankun-plugin 1.0版本, 也是页面级的思路在做(配置对应的url 路径,匹配到了,就挂载子应用)。现在这种2.0思路后,可以是一个小组件,也能挂载,解耦了对url状态的依赖 (👍)

云凤蝶lowcode用起来很爽,但现在云凤蝶还是被router给绑定了,其实用户想要的只是跳转云凤蝶内的组件而已。

会有这样的情况吗?

domain1.com/subpath/ => 云凤蝶报表A
domain2.com/subdir/ => 云凤蝶报表A
domain3.com/thirdparty/ => 云凤蝶报表A

如果用户就是在云凤蝶上的 /components/tab 这个页面下放的是tab组件呢?

感觉可以在中间做一个代理逻辑处理,(location)=> 云凤蝶内应用的id => 云凤蝶组件。云凤蝶应用永远在‘/’ 路径下挂载一个组件容器,路径再自己处理然后映射一下。 主要是传一个自定义的 history

这样子使用 可以传一个函数给云凤蝶,然后不就可以为所欲为了吗?或者在云凤蝶里面判断外面的url来展示不同的页面。

@kuitos
Copy link
Member Author

kuitos commented Apr 1, 2020

感觉可以在中间做一个代理逻辑处理,(location)=> 云凤蝶内应用的id => 云凤蝶组件。云凤蝶应用永远在‘/’ 路径下挂载一个组件容器,路径再自己处理然后映射一下。 主要是传一个自定义的 history

有一些更极端的场景,比如 /path/a -> [ 云凤蝶报表A、云凤蝶报表B]。

@18792758160
Copy link

在插件版本0.2.40时,切换子应用时,会渲染一次上次子应用,比如,从app1切换到app2,会先进入一次app2的layout之后,会再次进入到app1的layout

@peterzhai
Copy link

export default {
qiankun: {
master: {
apps: [
{
name: 'microApp1',
entry: '//test.com/app1',

  •     base: '/app1',
    
  •     mountElementId: 'app1-root'
      }
    ]
    
    }
    }
    }
    请问文档里这样配置是什么意思。

@ksh033
Copy link

ksh033 commented Aug 24, 2021

请问能使用splitChunks分割吗?

@wangxiangsong
Copy link

不同的子应用怎么挂载到不同的 dom 上,用 display 去控制展示不同的子应用,大佬们有 demo 嘛

@EdenStrive
Copy link

Uncaught (in promise) TypeError: Failed to fetch 父应用引入子应用后报这个错,

@Elainemi1998abc
Copy link

一个应用既能做主应用又能做子应用吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants