<p>相关学习推荐:javascript视频教程<p>众所周知,刚刚开源的「鸿蒙 2.0」以 JavaScript 作为 IoT 应用开发的框架语言。这标志着继 SpaceX 上天之后,JavaScript 再一次蹭到了新闻联播级的热点。这么好的机会,只拿来阴阳怪气实在太可惜了。作为科普,这篇文章不会拿着放大镜找出代码中的槽点来吹毛求疵,而是希望通俗地讲清楚它所支持的 GUI 到底是怎么一回事。只要对计算机基础有个大概的了解,应该就不会对本文有阅读上的障碍。 <p>我们已经知道在「鸿蒙 2.0」上,开发者只需编写形如 Vue 组件式的 JavaScript 业务逻辑,即可将其渲染为智能手表等嵌入式硬件上的 UI 界面。这个过程中需要涉及哪些核心的模块呢?这些模块中又有哪些属于自研,哪些使用了现成的开源项目呢?这里将其分为自上而下的三个抽象层来介绍:
<!-- hello.hml --><text onclick="boil">{{hello}}</text>复制代码
// hello.jsexport default { data: { hello: 'PPT' }, boil() { this.hello = '核武器'; } }复制代码
boil
方法,让 PPT
变成 核武器
。
<p>这背后发生了什么呢?熟悉 Vue 2.0 的同学应该会立刻联想到下面这几件事:
onclick
事件时能执行相应回调。this.hello
赋值时能执行相应回调。onclick
属性转换为 JS 对象的属性字段。onclick
属性会在 C++ 中被检查和注册,相当于全部组件均为原生。Object.defineProperty
的(几百行量级的)ViewModel。document.createElement
式的标准化 API。ace_lite_jsfwk
仓库下的 core/index.js
、observer.js
和 subject.js
),相当于有且只有这么一个功能:
<p>一个可以 watch 的 ViewModel。
<p>至于纯 JS 框架部分的实现复杂度和质量,客观地说如果是个人业余作品,可以当作校招面试中不错的加分项。
setTimeout
到 document.getElementById
到 console.log
再到 fs.readFile
,这些能执行实际 IO 操作的功能,都需要由「将引擎 API 和平台 API 胶合到一起」的运行时提供。运行时本身的原理并不复杂,譬如在个人的文章《从 JS 引擎到 JS 运行时》中,你就可以看到如何借助现成的 QuickJS 引擎,自己搭建一个运行时。
<p>那么在「鸿蒙 2.0」中,JS 运行时是如何搭建出来的呢?有这么几条重点:
<text>
和 <p>
的 XML 标签组件,都对应一个绑定到 JerryScript 上的 C++ Component 类,如 TextComponent
和 pComponent
等。@system
为前缀的 built-in 模块,它们提供了 JS 中可用的 Router / Audio / File 等平台能力(参见 ohos_module_config.h
)。router_module.cpp
、js_router.cpp
和 js_page_state_machine.cpp
)。简单说来这个「路由」是这样实现的:
router.replace
原生方法,走进 C++。pages/detail
)加载新页面 JS,新建页面状态机实例,将其切换至 Init 状态。graphic_lite
仓库了。可以认为,这里才是真正执行实际绘制的 GUI。像之前的 TextComponent
等原生组件,都会对应到这里的某种图形库 View。它以一种相当经典的方式,在 C++ 层实现并提供了「Canvas 风格的立即模式 GUI」和「DOM 风格的保留模式 GUI」两套 API 体系(对于立即模式和保留模式 GUI 的区别与联系,可参见个人这篇 IMGUI 科普回答)。概括说来,这个图形子系统的要点大致如下:
UIView
这个 C++ 控件基类,其中有一系列形如 OnClick
/ OnLongPress
/ OnDrag
的虚函数。基本每种 JS 中可用的原生 Component 类,都对应于一种 UIView 的子类。DrawLine
/ DrawCurve
/ DrawText
等命令式的绘制方法。FillArea
矩形单色填充能力。libpng
和 libjpeg
做图像解码,然后即可使用内存中的 bitmap 图像做绘制。
<p>然后对于路径,这个图形库自己实现了各种 CPU 中的像素绘制方法,典型的例子就是这个贝塞尔曲线的绘制源码:
void DrawCurve::DrawCubicBezier(const Point& start, const Point& control1, const Point& control2, const Point& end, const Rect& mask, int16_t width, const ColorType& color, OpacityType opacity) { if (width == 0 || opacity == OPA_TRANSPARENT) { return; } Point prePoint = start; for (int16_t t = 1; t <= INTERPOLATION_RANGE; t++) { Point point; point.x = Interpolation::GetBezierInterpolation(t, start.x, control1.x, control2.x, end.x); point.y = Interpolation::GetBezierInterpolation(t, start.y, control1.y, control2.y, end.y); if (prePoint.x == point.x && prePoint.y == point.y) { continue; } DrawLine::Draw(prePoint, point, mask, width, color, opacity); prePoint = point; } }复制代码
INTERPOLATION_RANGE
)作为插值输入,逐点计算出曲线表达式的 XY 坐标,然后直接修改像素位置所在的 framebuffer 内存即可。这种教科书式的实现是最经典的,不过如果要拿它对标 Skia 里的黑魔法,还是不要勉为其难了吧。
<p>最后对于文字的绘制,会涉及一些字体解析、定位、RTL和折行等方面的处理。这部分实际上也是组合使用了一些业界通用的开源基础库来实现的。比如对于「牢」这个字,就可以找到图形库的这么几个开源依赖,它们各自扮演不同的角色:
harfbuzz
- 用来告诉调用者,应该把「牢」的 glyph 字形放在哪里。freetype
- 从宋体、黑体等字体文件中解码出「牢」的 glyph 字形,将其光栅化为像素。icu
- 处理 Unicode 中许多奇葩的特殊情况,这块个人不了解,略过。this.hello = 'PPT'
之类的代码,触发依赖追踪。<p>特别声明:本部分主观评论仅针对「鸿蒙 2.0」当前的 GUI 框架部分,请勿随意曲解。<p>对于「鸿蒙 2.0」在 GUI 部分的亮点,个人能想到这些:
<p>当然,汽车厂商也不会说自己造的是飞机,对吧?<p>总之这确实是一盘自己做的麻婆豆腐,但不是某些人口中的满汉全席。 <p>最后是个人的主观评论: <p>首先,这套 GUI 技术栈达到了组装和借鉴开源产品时所能获得的主流水平。但论性能和表现力上限,其核心模块距离微软 MakeCode 这类业界 cutting-edge 级的产学研结合前沿方案,仍然有数量级的代际差距。 <p>其次,不必把它当作需要海量专家精密计算的 Rocket Science——不是贬低自主研发,而是真心地希望大家能明白,「这件事我也可以实际参与进来!」操作系统和 GUI 没有那么神秘,已有很多国产的成熟开源产品可供学习、使用与贡献(这里顺便推荐极易体验且同为国产的 RT-Thread 作为尝鲜入门之用)。毕竟只有真正搞懂了某个产品在技术上到底是怎么一回事,才不容易被别有用心的人带节奏,对吧? <p>最后,对于所有熟悉 JavaScript 的前端开发者们,你们为什么还要阴阳怪气地嘲笑鸿蒙呢?鸿蒙就是 JavaScript 在中国的财富密码啊!JavaScript 被鸿蒙这样的「国之重器」采用,可以大大增强前端的道路自信、理论自信、文化自信和技术栈自信。只要以这种形式结合拼接与自研,就可以一举在全国上下获得崇高的声望,这条路真是太让人心驰神往了呀(小声) <p>我们要团结起来,大力弘扬和宣传 JavaScript 在大国竞争中的核威慑级地位,争取上升到只要说自己会写 JavaScript,大家就会对你肃然起敬的高度——只要你是前端程序员,买票可以插队,搭车可以让座,开房可以白嫖……好时代,来临了! <p>想成为国之栋梁吗?来写 JavaScript 吧! <p>不多说了,我要去实干兴邦啦!
<p>想了解更多编程学习,敬请关注php培训栏目!
以上是一起看看 鸿蒙 JavaScript GUI 技术栈的详细内容。更多信息请关注PHP中文网其他相关文章!