小松的技术博客

六和敬

若今生迷局深陷,射影含沙。便许你来世袖手天下,一幕繁华。 你可愿转身落座,掌间朱砂,共我温酒煮茶。

下一代 React Native 会是什么样的?— Recos

我们团队很早就接入了 React Native,其动态化下发面对我们这个三天两头改界面的团队是挺契合的。虽然 Flutter 大火,但我们现在并没有去尝试,一个是动态化对于我们太重要了, 另外一个是我们跨平台选择了“业务逻辑使用KMM, 音视频用 C++,界面用RN”的路子,可以满足团队的需求,还有一个原因是,现在即使是跨平台技术了,业务太多,客户端的人也太少了,已经被困在业务里走不动了,心有点累。

但是 RN 被诟病的主要还是性能太差,一堆不知原因的 Native Crash。我们不能因为它存在问题,那就换另一套技术,而是要去优化它,解决它的问题,这也是我们技术走向深度的一个路子。

随着时代的发展,Native UI 已经有了新的产物, Android 这边诞生了 Compose, iOS 诞生了 SwiftUI, 都是采用声明式的语法,可以说 React 开创了 UI 的新时代, Native 已经有新的产物,那么 React Native 是否也应该与时俱进?不过 React Native 官方团队的开发节奏有点慢啊,一年前提出了 JSI 去解决通信问题,然后就一直卡在 TurboModule 里走不出来了。 所以去年我就有一个想法,我只去用 React, 重新用 Compose 实现一个 React Native,完全采用 JSI 与 TurboModule 的形式, 因为没有历史包袱,也只去支持 Hermes 引擎,而且只去支持我们用到组件,理论上会简单很多, 业余时间,我也的确去这么做了,也初步做出了 Demo, 事件流程、基础组件、Flex 布局等都实现出来,看似有点搞头,但是在长列表上,我的想法是 js 渲染出所有的 virtual dom, 然后到了 Compose 端, 将它们作为数据传递给 LazyColumn 进行渲染, 可实现发现,js 去创建成千上万个 virtual dom 时依旧很耗时, 这个路子走不通,想了一些其它的路子,但是需要去魔改下 React,不想这么做,最终好像又要回到 React Native 社区提供的 RecyclerListView 的形式, 有点不爽。

不过想着想着,又有了新的思路, React 是声明式的语法, SwiftUICompose 也是生命式的语法,那么我们可不可以把它从 React 的写法翻译到 ComposeSwiftUI 上呢。 基于静态代码翻译,肯定可行, 但是我们想要的是动态下发,那该怎么搞呢?在与同事的讨论中,我想到了一个方案: 一个语言,在编译前端的呈现是什么呢? 是 AST 结构,那么我们是不是可以序列化这个 AST,然后在 Compse 环境去解释执行这个 AST 呢? 那我们就来试一下。

JS 的 AST 需要我们自己去解析文件生成吗? 不,前端有强大的 babel,所以我们能轻松拿到 js 的 ast 结构, 我们可以适当的简化下,渠道多余的信息,减小生成文件的大小, 因为流程就变成这样子了:

想法有了,那就去做做看,新建个项目, 命名为: Recos

Github 地址:https://github.com/cgspine/Recos

目前 Compose 的 Demo 已经写出来了,开篇就是一个长列表, 可以无任何依赖的执行下面代码:

function Item1(item, onItemClick){  
    const onClick = useCallback(() => {
        onItemClick(item)
    })

    return <Text style={{ color:'#fff' }} onClick={ onClick }>偶数:{item.name}, {item.count}</Text>

}

function Item2(item, onItemClick){  
    const onClick = useCallback(() => {
        onItemClick(item)
    })

    return <Text style={{ color:'#fff' }} onClick={ onClick }>奇数:{item.name}, {item.count}</Text>
}

function HelloWorld(current){

    const [data, setData] = useState([])

    useEffect(() => {
        let ret = []
        for(let i = 0; i < 1000; i++){
            ret.push({
                name: 'item' + i,
                index: i,
                count: 0
            })
        }
        setData(ret)
    }, [current])

    let render = function (i) {
        let item = data[i]
        if(item.index % 2 == 0){
            return Item1(item, (it) => {
                it.count = it.count + 1
                setData(data)
            })
        }else{
            return Item2(item, (it) => {
                it.count = it.count + 2
                setData(data)
            })
        }
    }

    return <RecyclerView count = { data.length } render = { render }>Hello World!</RecyclerView>
}

React 里的 useState, useCallback, useEffect 我都去实现了下,感觉努努力,就可以用到生产环境了。

如果去实现下 React Native 的所有接口, 那就可以与之相兼容了。

目前看来,这个就完全两个方面的能力:

  1. 编译原理的前端知识,我们要去写 js 解析器了, 现在我写的这些代码,只是能工作,效率什么都需要逐步优化,还有很多语法解析没支持到。
  2. 考察 js 的基本功,什么作用域、闭包、变量提升,对象、原型链, 都需要从根本去搞懂它。

一些想法的产生、落地都需要很多基本功的落地,而不是只会调用某些框架就能搞定的,继续努力吧, 有兴趣的并且认可这个思路的同学,可以参与到这个项目来,努努力,让我们再次重铸JS昔日辉煌,干掉 ReactNative,干掉 Flutter。

PS:

缺客户端啊,快来加入我们: https://careers.tencent.com/jobdesc.html?postId=1308383157862539264

有时间,一起来聊聊技术:

←微信← →支付宝 →