qiankun icon indicating copy to clipboard operation
qiankun copied to clipboard

fix: 修复styled-components兼容性问题

Open Aeero opened this issue 1 year ago • 10 comments

Checklist
  • [x] npm test passes
  • [ ] tests are included
  • [ ] documentation is changed or added
  • [ ] commit message follows commit guidelines
Description of change
  • close https://github.com/umijs/qiankun/issues/2603

Aeero avatar Aug 18 '23 04:08 Aeero

Someone is attempting to deploy a commit to a Personal Account owned by @umijs on Vercel.

@umijs first needs to authorize it.

vercel[bot] avatar Aug 18 '23 04:08 vercel[bot]

没看懂,这个修复是怎么 work 的? styled-component 的问题在于 recordStyledComponentsCSSRules 在调用时,因为 styleElement 可能已经被从文档上移除了,导致 styleElement.sheet.cssRules 获取到的是空的。这个 pr 劫持了 insertRule 这些方法,但没看到怎么记录的,而且这个时候应用已经在卸载了,劫持起不到任何效果了。

"styled-component 的问题在于 recordStyledComponentsCSSRules 在调用时,因为 styleElement 可能已经被从文档上移除了,导致 styleElement.sheet.cssRules 获取到的是空的",这个问题我暂时没有复现过,styleElement.sheet.cssRules一直都是有的,我并不是要解决cssRules为空的问题。

这个PR要解决的是style节点从document移除,然后rebuil之后styleElement.sheet引用变动导致新增样式失效的问题。举个例子,document上有一个style节点style1,sheet1 = style1.sheet,然后移除style节点再重新插入到document上(模拟应用切换style重建的过程),此时style1.sheet和sheet1是不相等的。然后styled-components内部只会记录sheet1,后续动态的样式只会往sheet1里插入,所以后续的样式都失效了。此PR拦截了sheet1的insert的逻辑,插入到重建后的style节点上,这样后续的动态样式就能生效了。

Aeero avatar Aug 21 '23 03:08 Aeero

明白了。 不过这样也只是修复了 styled component 的一个问题,我提到的那个问题会更常见一些,看上去还是没解决。这样的话现阶段更推荐通过关闭 cssom 来规避 styled component 引入的问题了。

kuitos avatar Aug 21 '23 06:08 kuitos

没看错的话切换应用清除dom用的应该是render({ element: null, loading: false, container: remountContainer }, 'unmounted');,然后recordStyledComponentsCSSRules 应该是在unmountSandbox时被触发的。 顺序上unmountSandbox应该始终是早于清除dom的逻辑的 image 什么情况下会复现你说的这个问题呀

Aeero avatar Aug 21 '23 07:08 Aeero

比如主应用也监听了路由,路由变化的时候立马清空了容器 DOM,而 unmount 过程是异步的,这中间就可能出现子应用还在 unmount 执行过程中,但是 DOM 其实都已经被移除 document 文档流了。

kuitos avatar Aug 21 '23 08:08 kuitos

确实会有这样的情况。 如果是把拦截insert的逻辑放到子应用appendChildOrInsertBefore style节点的时候是不是能解决这个问题。

Aeero avatar Aug 21 '23 08:08 Aeero

理论上是可行的,如果 styled component 都是通过 insertRule 在插入样式的话,可以劫持过程中记录一把。

kuitos avatar Aug 21 '23 08:08 kuitos

hi @Aeero 按照前面聊的思路,提前记录一把应该是可行的,有兴趣直接一起修复了吗?

kuitos avatar Aug 30 '23 01:08 kuitos

hi @Aeero 按照前面聊的思路,提前记录一把应该是可行的,有兴趣直接一起修复了吗?

嗯嗯,按照这个思路改过几版,但是还是有一些没有解决的问题,所以暂时还是WIP,大佬有空可以帮忙review一下这几个问题和解决思路

  1. 如果改成 appendChildOrInsertBefore 时提前记录,则没法用isStyledComponentsLike判断是否是styled-components节点,因为style节点刚插入的时候大概率都是一个空节点,在一开始的时候很难判断是否会发展成StyledComponentsLike节点。这个问题想到两个解决方法:a.动态插入的style节点全都做sheet拦截和记录,但是如果是非StyledComponentsLike节点就有点多余(应该不会有sheet.insertRule和children混合用的情况吧)。 b. 使用hasAttribute('data-styled-version')判断是否是styled-components节点,但是相当于范围收窄了,可能会有一点berak change。

  2. https://github.com/umijs/qiankun/issues/1533,同样也是遇到了这个问题。css in js的库通过lastStyleTag.parentNode.insertBefore插入节点,这样不会被appendChildOrInsertBefore拦截,也就不会被记录成动态节点。解决思路是重写qiankunHead.append qiankunHead.insertBefore appWrapper.appendChild 改成 document.head.appendChild document.head.insertBefore document.body.appendChild,这样就能被appendChildOrInsertBefore拦截了

  3. 没有上述修改的情况下,react.Suspense fallback了一个styled-components包裹的dom(无论是texttag还是cssomtag),safari下偶现子应用第一帧样式丢失的问题,可能是我业务的问题,还没头绪,如果大佬有遇到类似的问题可以帮忙贴一下相关信息,非常感谢

Aeero avatar Aug 30 '23 08:08 Aeero

  1. isStyledComponentsLike 里加一个 hasAttribute('data-styled-version') 应该没问题,相当于多了一个判断条件,做成 || 应该就行,不会有 breaking change
  2. 这个不打算在 2.x 里支持了,影响比较大,挪到 3.x 考虑吧
  3. 是子应用二次进入的时候丢失了吗?texttag 模式下不太可能出现这个问题🤔

kuitos avatar Aug 31 '23 03:08 kuitos