16

berial:更精致的微前端框架

 3 years ago
source link: https://zhuanlan.zhihu.com/p/301283431
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

halo,大家好,今天抽时间对 berial 进行了大重构,这次重构还是非常重要的,我准备重新写一篇文章来详细介绍

先前的文章不用看啦,微前端看这一篇就足够了

异步渲染管线

微前端的本质就是前端路由,将多个不同技术栈的 app(berial 中叫做实例) 通过前端路由的方式组合在一起

当一个路由匹配多个实例的时候,berial 之前是抄的 single spa,渲染是同步串行的

但是实际上,微前端多个实例其实没有什么依赖关系,是完全可以独立不行渲染的

所以新版本的 berial 则利用 web component 设计了一套异步渲染管线

path : /a/b/c

<sandox-a>
  <sandox-b>
    <sandox-c></sandox-c>
    <sandox-c></sandox-c>
  </sandox-b>
</sandox-a>

sandox-a - load
sandox-b - load
sandox-c - load sandox-c - load
sandox-c - mount
sandox-b - mount
sandox-a - mount sandox-c - mount

如上,大概的实现思路很简单,就是

1. 使用 web component 制造一棵树,
2. 这棵树就有了顺序,自上而下 load,自下而上 mount
3. 与此同时,load 阶段是同步的,用来确定顺序,后续的 mount/unmout 是异步的

这种有序的异步渲染,和 react Fiber 是类似的,都是子树渲染完,再冒泡到父亲

值得一提的是,qiankun(single-spa)等同类框架,是没有机会安排这种调度的,这也是 berial 的优势了

全面拥抱 web component

上面提到的异步渲染管线,其实最大的功臣是 web component,如果不是借用了 web component 的 runtime,是很难搞定这种调度的

所以 berial 的 API 也发生了变化

<router-view>
  <router-view slot="a">
    <router-view slot="b"></router-view>
    <router-view slot="c"></router-view>
  </router-view>
</router-view>

<script type="module">
  import { Entity } from 'berial'
  // 注册所有实例
  window.customElements.define('reuter-view', Entity)
  // 切换路由,自动匹配实例到 slot 中
  window.history.push('/a/b')
</script>

从之前手写命令式的 js 路由表,到现在使用 html 声明式创建路由视图,有没有一种 react-router 的感觉,哈哈哈

这里使用了 web component 的 slot 机制,开发者只需要和 react-router 一样声明 slot,berial 会根据路由自动插入到合适的位置

path: /a/c

<slot name="a">
  <slot name="c"></slot>
</slot>

使用 web component 还有一个好处,那就是可以使用继承了,不再需要之前的 mixin

import { Entity } from 'berial'

class MyEntity extends Entity{
  constructor(){
    super()
  }
  connectCallback(){
    // do something
    super.connectCallback()
  }
}

window.customElements.define('reuter-view', MyEntity)

就问你刺不刺激,berial 还是希望能够提供最核心的机制,比如对 manifest 的支持,比如 react 事件转移,这些全都可以使用继承做成插件

沙箱

berial 沙箱上个版本其实做的还行了,主要思路就是使用 Proxy 和 eval

const run = function (window){
  eval(`
    window.a = 1;
    console.log(window.a);
  `)
}
run(new Proxy({}))

只不过 berial 新版本还使用了 MutationObserver 对沙箱外部的世界进行观察,一旦发现逃逸,就塞回原沙箱

new MutationObserver((mutations) => {
    mutations.forEach(async (m: any) => {
      switch (m.type) {
        case 'childList':
          if (m.target !== host) {
            for (let i = 0; i < m.addedNodes.length; i++) {
              const node = m.addedNodes[i]
              if (node instanceof HTMLScriptElement) {
                // 塞回去
              }
            }
          }
          break
        default:
      }
    })
  }).observe(document, { childList: true, subtree: true })

以上,通过 Proxy 劫持内部 + MutationObserver 侦测外部,berial 的沙箱还是很 OK 的

html-loader

本次重构改动最小的模块

感谢源妹,代码很稳,html-loader 主要是匹配出 html 中的 script 链接和 style

可以理解为手动实现了一个 import()

import('a.html').then(mods=>{
  console.log(mods) // { scripts, styles }
})

总结

以上就是 berial 重构的全部内容,Do more with less

不得不嘴一下,重构后的 berial 代码量变得更少了,但是整个架构水平却提升了一个层次

未来 berial 也其他做到更精致,异步渲染管线,web component,沙箱,html-loader……

业务上的东西争取靠插件机制搞定√

最后放一下 berial 的地址:

berialjs/berial

有兴趣的欢迎来玩呀


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK