Labor,首个 web container 开源实现

7,247 阅读4分钟

halo,大家好,俺是 132,今天给大家带来的是一个很有意思的东西

起因是 stackblitz 团队在 Chrome I/O 上分享了一篇文章,可以将 node 跑到浏览器中

blog.stackblitz.com/posts/intro…

然后惊艳四座,国内外都在猜测内部的可能实现……因为他们也不开源,于是……

经过我好几天的研究,我特么终于搞懂了o(╥﹏╥)o

github.com/yisar/labor

先放 github 地址,欢迎大家提 pr,点 star,蟹蟹大家啦

然后接下来我简单阐述一下这个的底层原理

基本实现原理

code => service worker(js runtime) => go wasm API

一句话就是,在 server worker 中,借助 wasm 实现一个 js runtime

举例子:

const a = readFileSync('a.js')

首先可以肯定的是,这段 js 是在 service worker 中跑的,service worker 充当了 v8 的角色

new Function('global', `with(global){
    const a = readFileSync('a.js')
}`)

到这里,我们执行会提示缺少 readFileSync API,剩下的工作,我们就是要使用 wasm 实现这个 API,然后注入到 global 上

wasm 充当了 deno 中的 rust 角色

js.Global().Set("readFileSync", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
    readFile(args[0].String())
    return nil
}))

大功告成!基本原理就是这么简单的啦

文件系统

根据 stackblitz 的 demo 可以看到,他们是可以读文件系统的

const a = readFileSync('./a.js')

实际上如果我们没有文件系统的话,访问 ./a.js 是拿不到内容的,所以重点来了

我们要在首次加载的时候,把所有文件都存到内存里,也就是需要构建一个 dependency graph

如果你有写过打包工具的经验,肯定会对 dependency graph 比较熟悉了,我之前也有一次技术分享详细讲过

简化就是这样的:

new Map([
    ['./a.js', buffer], 
    ['./b.go', buffer]
])

在我们初始化的时候,就是要生成一张这样的图,这是我们实现文件系统的关键

http client

除了文件系统,另一个重点就是 http

好消息是 go 的 net/http 包对 WASI 的支持程度还不错,但有很多东西还是没办法用

func Download(url string) interface{} {
    res, _ := http.Get(url)
    buffer, _ := ioutil.ReadAll(res.Body)
    body := js.Global().Get("Uint8Array").New(len(b))
    js.CopyBytesToJS(body, b)
    return body
}

一直报错,气死我了,所以为了抹平,我们只能重新重新实现一个 http client 了

基本原理是借助 net 的其他包,polyfill 不支持的包

比如 net/http 的 http.Get() 方法不支持,我们就用 net/http/httputil 包模拟

这块内容比较多,等我有时间了

web container 的场景

  1. webIDE

这次的 stackblitz demo 就是 webIDE,因为可以直接在浏览器中直接跑 js runtime,直接启一个 server,所以节省了通往 server 的链路

类比的场景比如组件化实施预览,lowcode 的预览,物料市场的预览等等

  1. better worker

实际上 web container 也可以理解为,借助 wasm 能力进行增强的 worker,你可以用它做很多以前 worker 里做不了的事情

比如我在公司里是负责小程序底层架构的,小程序的模拟器是需要启动一个 server 的,然后每次重新编译就得重启 server,现在有了 labor,我就可以全部在浏览器中完成工作,可以说非常完美了

  1. 双 worker 同构

workers.cloudflare.com

前阵子 cloudflare worker 的形态令人耳目一新,我当时觉得这个形态的 serverless 真的是太赞了

但是 cloudflare 是 server 端的 js runtime,它一直缺少一个本地调试的终端

labor 就可以做这件事情,我们在本地通过 labor 开发,线上通过 cloudflare 运行,两个 worker API 完全相同,最终做到同构

这可以说是 serverless 的最绝美形态了

  1. ssr/esr

esr 是最近比较火的,就是在 serverless 平台下做 ssr,和上面的双 worker 同构一样,想象一下开发环境走 labor 的 ssr 会多么爽

web container 的优势

  1. 不再需要开发者关注 server

尤其是对于 ssr 的场景,非常非常赞

  1. 调试更好

Chrome devtools 是最好的调试工具

  1. 更省钱

咳咳咳。。。

web container 的限制

因为 labor 是在浏览器中实现 js runtime,所以主要限制来自于浏览器

  1. http 不能启端口

也就是 go 的 ListenAndServe 不能用

  1. 不能访问本地文件

web.dev/file-system… 这个 API 看着不太靠谱

  1. wasm 性能差

实测,go 的 wasm 版本,平均比 native go 慢 30 倍,比 js 慢 10 倍

这主要是 go 的 GC 导致的,所以社区内有一些替代方案,比如 tinygo,可以扳回一局

总结

社区内很多人对 web container 评价比较不好,比如太花里胡哨,比如性能差限制多

但是我问,当年双线程前端框架也是花里胡哨来着?

但最终还是成就了小程序这么一个无敌的产品

web container 也是同理,最终肯定有绝美的产品落地的

不多说啦

github.com/yisar/labor

快点来点赞吧,以后有机会再来分享啊哈