方案探索
在 React Native 中可以使用零 Bundle 大小的 React 服务器组件吗?
由于需要适应快速的产品模块发布请求,要求在App不发版的场景下,对首页的Banner进行动态更新。
当下RN所支持的热更新已经可以满足大部分需求,但是也存在两个问题
- 强制更新影响用户体验
- 静默更新不能及时触达
基于上述问题,我门需要一套类似服务端渲染(SSR)的方式来解决.
服务端渲染简单的可以理解成在服务端进行页面元素结构的下发(json 字符串),在前端对其解析生成对应的元素树。同样,如果使用字符串来呈现 React Native 视图,则可以创建 0kb JavaScript 的原生 React Server 组件(即不需要提前编译进客户端)。
实现
1. 定义支持渲染的前端组件
const ComponentEnum = {'View': View,'Text': Text,'Image': Image,'Button': Pressable,'TextInput': TextInput,
};
2. 定义组件树解析结构
// 组件
type Component {static type: stringprops: anychildren?: Component
}// 点击事件
type Press {"@clientFn": trueevent: stringpayload: any
}
3. 解析
Object.entries(props).reduce((acc, [key, value]) => {if (value['@clientFn']) {return {...acc,[key]: () => {if (isFunction(callbacks[value.event])) {callbacks[value.event](value.payload);}}}}return {...acc,[key]: value};
}, {});
4. 创建
const ComponentView = ComponentEnum[type] ?? ComponentEnum['View'];return createElement(ComponentView,newProps,isArray(children) ? Children.map(children, (child) => configToComponent(child, callbacks)) : children);
5. 生成
export default new Proxy({}, {get: function (target, key, receiver) {// first time: target[key] = undefined;if (typeof target[key] === 'undefined') {// return function, ...args => Component propsreturn function (...args) {const {data, ...props} = args[0];// data => server jsx// props => { buttonPress: [Function buttonPress]}return configToComponent(data, props);};} else {// cache componentreturn Reflect.get(target, key, receiver);}}
});
6. 声明
<SC.HomeBannerdata={{type: 'Text',props: {style: {width: 100,height: 100,backgroundColor: '#f0f'},onPress: {"@clientFn": true,event: 'buttonPress',payload: "123"}},children: '123'}}buttonPress={(data) => console.log('button pressed', data)}
/>
缺陷
需要提前定义可支持的组件以及内置Function代码,例如埋点、点击事件的处理等。