7

web container:web build web

 3 years ago
source link: https://zhuanlan.zhihu.com/p/374221863
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.

web container:web build web

✅跪在床上娇喘,❎隔着网线叫唤

今天被这篇文章给刷屏了:

Introducing WebContainers: Run Node.js natively in your browser​blog.stackblitz.com图标

我们群里在讨论它背后的实现,最后也算是有了一些思路,其实这在好多年前就有人想搞了

https://github.com/parcel-bundler/parcel/issues/1253

大概分为这四步:

1. 在 sw 中模拟文件系统
2. 使用 wasm 编译 node/deno/go 的模块
3. 模拟模块用到的底层 API,比如 http 模块用到的 TCP 模块
4. 开发环境通过 socket 将结果转发到中心 server,进而分配和映射二级域名
5. 生产环境直接将运行结果存到数据库即可
v2-b0e11e0dda2c00c30f9c387585a10809_720w.jpg

总的来说,这种实现大约有几个比较合适的场景:

  1. ssr,用户不再需要关注 server 了,一切都在浏览器完成
  2. webIDE,就像文章里那样
  3. vite/snowpack,

以上,这个玩法确实蛮有意思的,可能会改变游戏规则,所以我大约冥思了一下

1. 文件系统

这块应该不难实现,github 上有很多类似的仓库,可以存内存可以存缓存可以存indexdb

值得一提的是 vite,因为 vite 实际上是利用浏览器自带的 native module 机制,所以其实它本质上是不需要走这个文件系统的

要走这个文件系统的是 esbuild,这也是可行的,只是 esbuild 的 wasm 版本会慢 10 倍

esbuild wasm vs binary · Issue #219 · evanw/esbuild

这是 wasm 单线程和无 GC 的限制导致的,实际上 esbuild 这种工具,它本身默认是快的,最终到底快慢看的是其他依赖是否“拖慢”,大概是这么个意思:

1. only esbuild 0.1s
2. node esbuild 0.3s
3. rollup with esbuild 3s
4. only rollup 13s

所以 vite 使用这个思路,关键还是看 esbuild

2. wasm 编译其他语言的模块

这可能是这种思路最大的好处,因为 wasm 不限语言,这意味着不只是 node

尤其是 deno,他们一直想删掉 native plugin API,然后往 wasm 的标准靠拢

Remove unstable native plugins · Issue #8490 · denoland/deno

所以其实 deno 比 node 更适合这个思路

3. http 模块和模拟 TCP stack

有个很大的问题,那就是,是真的 no server 吗?

根据观察,至少 demo 里有 server

https://http-server-csbclh--8080.local.webcontainer.io

那么就很容易理解了,在 demo 里,用户在浏览器跑完了 http 的模块,它会通过 sw 映射到真实 server 上,比如这样:

// sw.js
const str = renderToString(<App/>)
ws.postMessage("message", str)

// cloudflare worker
addEventListener("message", (e) => {
  event.respondWith(
    new Response(e.data.str, {
      status: 200,
      headers: {
        server: "denosr",
        "content-type": "text/plain",
      },
    }),
  )
})

请注意,我这里用了 cloudflare worker 的 API,你用你自己的 server 也是可以的,但是我觉得 cloud server 这类 WASS 是最适合的

4. 生产环境:流式渲染

那么问题来了,开发环境我们用 socket 传字符串是没有毛病啦

可是生产环境需要 seo 啊,必须同步返回一个字符串的呀,肿么办?

重点来了,流式渲染:

async function streamDirectlyIntoDOM() {
  const response = await fetch('data/commits.include',
                               {
                                 mode: 'same-origin',
                                 headers: {
                                   'Cache-Control': 'no-cache, no-store'
                                 }
                               });

  await response.body
      .pipeThrough(new TextDecoderStream())
      .pipeTo(targetDiv.writable);
}

loadButton.onclick = () => {
  loadButton.disabled = true;
  streamDirectlyIntoDOM();
}

这是唯一支持 seo 的 js 操作了,这样就形成闭环了,生产环境,worker 完全可以往客户端发送数据流

然后客户端解析流并渲染,就完美了

这是 react 正在搞的 fizz 的核心:

Basic Fizz Architecture by sebmarkbage · Pull Request #20970 · facebook/react

很可惜,目前也只是个半成品,像 vue,fre 都不支持

总结

真的是个伟大的 idea,整个ssr闭环就这样打通了……

  1. 开发环境全程在 service sorker 中完成
  2. 生产环境通过 cloudflare(severless) worker 部署
  3. 客户端通过 web stream 交互(Suspense)

这个链路真的非常非常赞……我真的很想很想搞,但是,太超前了,而且我一个人说,别人也听不懂,所以……

还是没法搞,或者你想搞的话,我可以提供一些帮助

给公司搞这么一套,你公司就成为东半球第二了(据说蚂蚁体验部是第三)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK