Ray 开发框架实现原理、使用和升级介绍

实现原理

Ray 本质上是站在 Remax 的基础上完成的运行时改造,本质是一个通过 react-reconciler 实现的一个小程序端的渲染器。关于 react-reconciler 和 React 渲染器相关的内容推荐观看这个视频,这里不再赘述。

众所周知,小程序屏蔽了 DOM,我们的代码运行在一个 worker 线程中,无法直接去操作视图层的 DOM。Ray 通过引入 VNode,让 React 在 reconciliation 过程中不是直接去改变 DOM,而先更新 VNode

VNode 的基本结构如下:

interface VNode {
  id: number;
  container: Container;
  children: VNode[];
  mounted: boolean;
  type: string | symbol;
  props?: any;
  parent: VNode | null;
  text?: string;
  appendChild(node: VNode): void;
  removeChild(node: VNode): void;
  insertBefore(newNode: VNode, referenceNode: VNode): void;
  toJSON(): RawNode;
}

👉 立即免费领取开发资源,体验涂鸦 MiniApp 小程序开发。  

  • id - 节点 id,这是一个自增的唯一 id,用于标识节点。
  • container - 类似 ReactDOM.render(<App />, document.getElementById('root') Ray 中会把组件渲染到一个容器中,容器的作用是保存 VNode 的引用。
  • children - 子节点。
  • mounted- 标识节点是否已经显示到视图层上。
  • type - 节点的类型,也就是小程序中的基础组件,如:viewtext等等。
  • props - 节点的属性。
  • parent - 父节点。
  • text - 文本节点上的文字。

可以看到,VNode 也是一个树结构,我们在 VNode 上实现了类似 DOM 中的节点操作方法。在 React 的更新完成后,我们会调用节点上的 toJSON 方法,把这个 VNode 变成一个 JSON 对象。

举个例子,假设我们有这样一个页面组件:

import React from 'react';
import { View, Text } from '@ray-js/ray';
const IndexPage = () => {
  return (
    <View className="greeting">
      <Text>Hello Ray</Text>
    </View>
  );
};
export default IndexPage;

Ray 在渲染这个组件时,会把它渲染成如下的 VNode 结构:

{
  "id": 0,
  "type": "root",
  "children": [
    {
      "id": 1,
      "type": "view",
      "props": {
        "className": "greeting"
      },
      "children": [
        {
          "id": 2,
          "type": "text",
          "props": {},
          "children": [
            {
              "type": "plain-text",
              "text": "Hello Ray"
            }
          ]
        }
      ]
    }
  ]
}

其中 root 节点是由 Ray 内部创建的,这个渲染出来的 VNode 数据就会成为小程序 Page 的 data

在视图层显示界面

上面讲到我们的 React 组件最终会被渲染成一个我们称之为 VNode 的 JSON 对象,并且这个对象会作为小程序 Page 的 data。现在我们要做的就是在小程序的模板里怎么把这个 data 给显示出来了。

我们在构建 Ray 应用时,会生成一个页面模板显示这个 VNode,这个模板大概是下面这个样子:

<block a:for="{{root.children}}" a:key="{{item.id}}">
  <template is="{{'REMAX_TPL_' + item.type}}" data="{{item: item}}" />
</block>
 
<template name="REMAX_TPL_view">
  <view class="{{item.props['className']}}">
    <block a:for="{{item.children}}" key="{{item.id}}">
      <template is="{{'REMAX_TPL_' + item.type}}" data="{{item: item}}" />
    </block>
  </view>
</template>
 
<template name="REMAX_TPL_text">
  <text>
    <block a:for="{{item.children}}" key="{{item.id}}">
      <template is="{{'REMAX_TPL_' + item.type}}" data="{{item: item}}" />
    </block>
  </text>
</template>
 
<template name="REMAX_TPL_plain-text">
  <block>{{item.text}}</block>
</template>

可以看到,我们会先去遍历根节点的子元素,再根据每个子元素的类型选择对应的模板来渲染子元素,然后在每个模板中我们又会去遍历当前元素的子元素,以此把整个节点树递归遍历出来。

以上就是 Ray 实现的基本原理,在具体实现上我们还会去做一些优化,想深入了解的同学可以直接看代码。

 👉 立即免费领取开发资源,体验涂鸦 MiniApp 小程序开发。

 

Ray 使用与升级管理

使用

必须引入 @ray-js/ray 与 @ray-js/cli 作为框架基础包,并建议保持最新版本。

对于老旧版本可能存在@ray-js/api @ray-js/components @ray-js/framework的基础包依赖,建议在 package.json 中删除其他ray的运行时基础包的依赖,只依赖 @ray-js/ray 即可,@ray-js/ray 已经对各种运行时依赖做了聚合。

 

升级

建议在升级 ray 的运行时依赖 @ray-js/ray 与 编译时依赖 @ray-js/cli时保持最新版本,我们会将最新的优化和功能保持同步,并且建议让这两个包的版本依赖保持一致。

"devDependencies": {
    "@ray-js/cli": "x.x.x"
},
"dependencies": {
    "@ray-js/ray": "x.x.x"
},

优化使用

对于项目中使用 import { location, router, usePageEvent, ... } from 'ray'; 是等同于import { location, router, usePageEvent, ... } from '@ray-js/ray'; 我们建议您修改下 tsconfig.json 文件,这样就可以保证开发中明确清晰的 ts 代码提示,方便开发。

{
    "paths": {
        "ray": ["./node_modules/@ray-js/ray"]
    }
}

  👉 立即免费领取开发资源,体验涂鸦 MiniApp 小程序开发。

  • 25
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IoT砖家涂拉拉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值