第 1 章 · Kuikly 是什么 — 可视化演示

点上方 tab 切换。本页用动画 / 图示帮助你"看见"Kuikly 与 Flutter / RN 的差异。

Kuikly 一份代码 → 6 个平台原生产物

同一份 Kotlin 业务代码,KMP 编译器针对不同目标平台输出不同形态的二进制。宿主 App 接入 Kuikly 跟接入一个普通的 SDK 没区别。

🤖
Android
.aar
📱
iOS
.framework
🌸
HarmonyOS
.so
🌐
Web (H5)
.js
💬
微信小程序
.js
🖥️
macOS
.framework
关键认知:Kuikly 不是"独立 App",它是"嵌入到原生 App 里的 UI 模块"。一个原生 App 可以有 0 到 N 个 Kuikly 页面,新老页面共存。

跟 Flutter / RN / Compose MP 的平台覆盖对比

平台KuiklyFlutterRNCompose MP
Android
iOS
HarmonyOS Next✅ 一等公民⚠️ 第三方⚠️ 第三方
Web
微信小程序
macOS / Win / Linux⚠️ macOS Alpha⚠️

四种框架的渲染流水线对比

同一个用户操作(比如点击按钮),从你的代码到屏幕上像素的链路对比。步骤越少、桥接越少 = 性能越好

★ Kuikly 2 棵树 + 原生渲染
Kuikly DSL(你写的代码)
BuildTree 原型树
RenderTree 渲染树
Native View(系统原生控件)

✅ 无桥接、无序列化、产物即原生模块

React Native JS Bridge + 原生渲染
JSX(你写的代码)
Virtual DOM
JS ↔ Native Bridge
Shadow Tree
Native View

⚠ 高频调用时桥接序列化打爆主线程

Flutter 3 棵树 + Skia 自绘
Widget(你写的代码)
Element Tree
RenderObject Tree
Skia 自绘引擎
GPU Texture(自己画像素)

✅ 性能强,但包大、跟系统原生有差异

Compose MP Composable + Skia 自绘
@Composable(你写的代码)
Composition Slot Table
LayoutNode Tree
Skia 自绘

✅ 跟 Compose 同语法,体验类似 Flutter

记忆口诀:原生派性能稳,自绘派一致强;Kuikly 走原生但又解决了一致性,靠的是"两棵树 + 轻原生层"。

SDK 体积对比(接入到宿主 App 的增量)

体积是跨端框架的关键指标。包越大,用户下载等待时间越长,应用商店转化率越低。

Android 端体积增量

iOS 端体积增量

为啥 Kuikly 这么小?
因为它不带渲染引擎,直接复用各平台已有的原生 UI 系统(Android View / iOS UIView),渲染层只负责"翻译指令"。 Flutter 要带 Skia 引擎(>5MB),自己画所有像素,所以包必然大。

包体小,用户感知是怎样的?

用户场景Kuikly +1MBFlutter +6MB
4G 下首次下载耗时+1 秒+6 秒
Wi-Fi 下首次下载+0.1 秒+0.6 秒
渠道包体积红线(如 Google Play 50MB 限制)压力小压力大
下载转化率影响几乎无每多 6MB ≈ 转化率 -1%

"桥接成本"是什么意思?动手感受一下

下面有两个按钮,模拟 Kuikly 的"直调"和 RN 的"桥接调用"。点击它们,观察响应速度和操作流畅度。

未运行
未运行
说明:这只是用 JS 模拟"序列化/反序列化"开销示意,实际框架的桥接成本更复杂。但趋势是真实的:
- Kuikly 直调 = 普通函数调用(纳秒级)
- RN 桥接 = 序列化 → 跨线程消息 → 反序列化(微秒到毫秒级)

同一个需求,三大框架代码长啥样

需求:屏幕中央显示 "Hello {计数}",右下角放一个按钮,点击让计数 +1。

Hello Kuikly!
0
不管哪种框架,最终视觉效果都长这样 👆
Kuikly · Kotlin 25 行
@Page("Counter")
class CounterPage : Pager() {
  private var count by observable(0)

  override fun body(): ViewBuilder {
    val ctx = this
    return {
      attr {
        allCenter()
        backgroundColor(Color.WHITE)
      }
      Text {
        attr {
          text("Hello ${ctx.count}")
          fontSize(20f)
          fontWeightBold()
        }
      }
      Button {
        attr {
          absolutePosition(bottom=30f, right=30f)
          size(80f, 80f)
          borderRadius(10f)
          backgroundColor(Color.BLUE)
        }
        event { click { ctx.count++ } }
      }
    }
  }
}
Flutter · Dart 30 行
class Counter extends StatefulWidget {
  @override _S createState() => _S();
}
class _S extends State<Counter> {
  int count = 0;
  @override
  Widget build(c) => Scaffold(
    body: Stack(children: [
      Center(
        child: Text('Hello $count',
          style: TextStyle(fontSize: 20))),
      Positioned(right: 30, bottom: 30,
        child: GestureDetector(
          onTap: () =>
            setState(() => count++),
          child: Container(
            width: 80, height: 80,
            decoration: BoxDecoration(
              color: Colors.blue,
              borderRadius:
                BorderRadius.circular(10))))),
    ]),
  );
}
RN · JavaScript 28 行
import { useState } from 'react';
import { View, Text,
  TouchableOpacity }
  from 'react-native';

export default function() {
  const [c, setC] = useState(0);
  return (
    <View style={{flex:1,
      justifyContent:'center',
      alignItems:'center'}}>
      <Text style={{fontSize:20}}>
        Hello {c}
      </Text>
      <TouchableOpacity
        onPress={() => setC(c+1)}
        style={{
          position:'absolute',
          bottom:30, right:30,
          width:80, height:80,
          borderRadius:10,
          backgroundColor:'blue'}}/>
    </View>
  );
}
重点:行数差不多,但 语言不同 —— Kuikly 用 Kotlin,团队转型成本最低(如果你已经会 Kotlin / Compose)。

"两棵树 vs 三棵树" 是啥意思?

所谓"几棵树"指的是从 DSL 到原生控件之间,要经过多少层中间表示。层数越多,开销越大

Flutter 三棵树

Widget Tree  →  Element Tree  →  RenderObject Tree
   (描述)        (生命周期)        (布局 + 绘制)
   不可变         可变               可变
   每次重建       Diff 复用          实际工作

Kuikly 两棵树

BuildTree                    →  RenderTree
(原型树:组件 + 布局节点)          (渲染树:仅可见节点)

  ┌─ Pager                           ┌─ Pager
  ├─ View (布局容器)            ──►  ├─ Text (可见)
  │   ├─ Text (可见)                 └─ Button (可见)
  │   └─ Button (可见)
  └─ ScrollView (布局容器)

  ※ 布局容器节点不渲染               ※ Diff 时只对比这一棵
  ※ 测量、布局都在这棵完成           ※ 跟 Native View 1:1 映射
Kuikly 的两个关键设计
1. UI 扁平化:BuildTree 中的"纯布局节点"(如 flex 容器)不会下沉到 RenderTree,避免渲染层冗余
2. O(1) 精细化 diff:响应式字段精确追踪到具体属性,更新时只发"setProp(text, '新值')"指令,不重建整棵树

渲染指令长啥样?

跨端层和 Native 层之间通过指令通信(不是直接函数调用),这是 Kuikly 区别于 Compose MP 的关键:

// 跨端 Kotlin 层发出渲染指令
createNode(id=1, type="Text")
setProp(1, "text", "Hello 5")
setProp(1, "fontSize", 20)
addChild(parent=0, child=1)

// Android Native 层接收并执行
TextView tv = new TextView(ctx);
tv.setText("Hello 5");
tv.setTextSize(20);
parentView.addView(tv);

这种"指令通信"的好处是:跨端层可以独立打包、独立动态化下发,不直接依赖原生层的具体实现。这是 Kuikly 动态化的基础