天天看点

解读鸿蒙源码: 鸿蒙“小程序”工作原理研究笔记

自从微信小程序出现以来,各种“小程序”如雨后春笋一般出现。事实证明小程序这种开发方式非常好,鸿蒙 JS UI 框架采用类似的方式也是在意料之中的。

一个小程序(在鸿蒙 OS 中,也就是 Ability)由多个页面组成,每个页面由三部分组成:

.hml 用来描述界面的元素

.css 用来描述界面的风格

.js 用来编写处理事件逻辑

我们来看个例子:

index.hml

index.css

index.js

2. 工作原理

要理解它的工作原理,先研究一下编译之后的代码是非常重要的。上面的三个文件,编译之后会生成一个文件,其位置在:./entry/build/intermediates/res/debug/lite/assets/js/default/pages/index/index.js

index.hml 变成了创建函数:

index.css 变成了 JSON 文件。

这种处理方式很妙,把 JS 不擅长处理的 XML/CSS 转换成了 JS 代码和 JSON 对象,这个转换由工具完成,避免了运行时的开销。

在没有研究编译之后的代码时,我尝试在 ace/graphic 两个包中寻找解析 HML 的代码,让我惊讶的是没有找到相关代码。看了这些生成的代码之后才恍然大悟。

在 C++的代码中,创建控件的过程如下:

调用 JS 的 _c 函数时,进入 C++的函数 RenderModule::CreateElement

RenderModule::CreateElement 的实现如下:

把 控件的 tag 转换成控件的 ID
调用 ComponentFactory::CreateComponent 创建控件
调用控件的 Render 创建真正的控件和子控件。

那么第一个_c 函数是在哪里调用的呢?答案很明显,进入一个页面的时候,会调用 RenderPage 渲染页面:

这里会调用 JS 的 render 函数:

这里的 renderFunction 又是哪个函数呢?这需要看看 ViewModel 的构造函数:

这里的$render 是由 options 提供的,而 options 又是从哪里来的呢?再来看看编译后的代码:

把 options.render 打印出来,可以看到

正是前面那个创建控件的函数。

从前面的代码,很容易理解初始渲染时数据的绑定过程。比如 text 的 value 属性从一个匿名函数获取,它包装了 xml 中些的表达式:

而运行时,数据变化时,需要监控匿名函数的值变化,变化时会调用下面这个函数,它负责更新控件的属性。

在 initState 函数中,关注了 data 的变化:

条件渲染由_i 函数处理,它有两个参数:

第一个参数是一个函数,决定是否进行渲染。

第二个参数是一个函数,决定如何进行渲染。

如:

编译成:

C++中对应这个函数:

数据变化时,重新进行根据条件进行渲染,执行下面的函数:

关注数据变化的回调函数。

这个做法我觉得很有创意:

不需要搞虚拟 DOM 进行比较。

不需要重新建立整个 DOM。

继续阅读