当前位置: 首页 > article >正文

鸿蒙ArkUI体验:Hexo博客客户端开发心得


最近部门也在跟进鸿蒙平台的业务开发,自己主要是做 Android 开发,主要使用 Kotlin/Java 语言。,需要对新的开发平台和开发模式进行学习,在业余时间开了个项目练手,做了个基于 Hexo 博客内容开发的App。鸿蒙主要使用ArkTS语言和ArkUI框架进行开发,有使用 Jetpack Compose 和 JavaScript/TypeScript 的开发经验的话,上手会比较的轻松。本文主要介绍做的App功能以及对鸿蒙开发体验的一个总结。

App 简介

后台数据来自 Hexo 生成的博客文章,利用 hexo-generator-wxapi 生成 api .json 文件,再利用 七牛云 提供对图片和 .json 文件 CDN。

实现的功能

  • 博客列表分页加载
  • 文章详情加载
  • 文章按分类/标签展示
  • 文章内容统计
  • 深色/浅色模式切换
  • 数据本地缓存

功能预览

博客列表统计个人文章详情分类标签

依赖项

Hexo
  • Hexo 快速、简洁且高效的博客框架
  • hexo-generator-wxapi 用于将 Hexo 博客内容生成 api 风格的.json文件
  • 七牛云 提供对图片和.json文件 CDN加速
HarmonyOS
  • ArkTS ArkTS在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,同时通过规范定义强化开发期静态检查和分析,提升代码健壮性,并实现更好的程序执行稳定性和性能。
  • ArkUI ArkUI(方舟UI框架)为应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行可视化界面开发。
  • ohos_pull_to_refresh 列表加载/刷新控件(没有’No more’的状态)
  • MMKV 是基于 mmap 内存映射的 key-value 组件

鸿蒙开发总结

ArkTs 语言

ArkTS 是 TypeScript 的超集,TypeScript 又是 JavaScript 的超集,所以对于基本数据类型使用的是 TypeScript 语法。他们三者的关系如下图所示:

相关的差异可以参考社区话题讨论 ArkTS与Typescript的区别?
上图也来自这里的讨论。

这里主要记录一下自己使用过程中踩过的坑:

基本语言类型

Number 和 number 是两个不同的类型,Number 是 JavaScript 中的一个全局对象,可以使用 new Number() 来创建一个 Number 对象。同理对于 String 和 string,Boolean 和 boolean 也是一样的,大写开头的是包装对象类型,小写的是原始类型,这点Java/kotlin也有类似的包装对象比较好理解,但 Object 居然也有大小写之区分相比难理解点,写代码的时候好几次忽略了这个事,Object 是所有对象的基类,object 表示非原始类型(即不是 number、string、boolean、symbol、null 或 undefined 的所有类型)。可以是任何对象、数组、函数、类实例等。

let obj: object;
obj = { a: 1 };         // ✅ 正确:普通对象
obj = [1, 2, 3];        // ✅ 正确:数组
obj = () => {};         // ✅ 正确:函数
obj = new Date();       // ✅ 正确:类实例obj = 42;               // ❌ 错误:原始类型 number
obj = "hello";          // ❌ 错误:原始类型 string

两则的区别

特性objectObject
允许的值仅非原始类型(对象、数组等)任意类型(包括原始值)
原始值处理禁止自动装箱(如 42 → Number)
使用场景明确限制为非原始类型时极少使用(通常用 unknown 或具体类型替代)
Map 等集合类当作普通 JavaScript 对象来操作
let map = new Map<string, object>();
map["key"] = value;            // ❌ 错误用法!
console.log(map.get("biz"));   // ❌ 输出 undefined

最开始挺奇怪的 map 明明设置了值,但是对应的 map size 为0,遍历 map 也没有数据。后来才发现是这种方式 不会 触发 Map 的内部机制,而是绕过了 Map 的方法,直接操作对象的属性,赋值后,键值对 不会 被存入 Map 的真实存储中,而是作为对象的普通属性存在。正确的用法是:

let map = new Map<string, object>();
map.set["key"] = value;        //  ✅ 正确用法!
console.log(map.get("biz"));   //  ✅ 输出 value
struct 的困扰

在 js 里面是没有 struct 这个关键词的,从刚接触到现在它唯一的作用就是:和 @Component绑定声明一个UI控件。例如:

@Component
export struct ToolBar{}

@Componentstruct 两则缺一不可,既然必须有 @Component来标注这是一个UI控件,为什么不能下面这样呢?能省掉一个关键字。

@Component
export class ToolBar {}

同样困扰的人还有很多,这里有一份讨论定义组件时的stuct关键字是什么?
官方也有一份聊胜于无的介绍

struct和class的区别是什么?

struct只在自定义组件中使用,@Component装饰的struct就是自定义组件,自定义组件和class是两个概念,自定义组件没有类型,也不能等同于class。如果开发者需要使用组件作为参数在组件之间传递,可以使用自定义占位节点。

我猜测这样是为了省掉对@Component装饰器编译的工作量,如果使用 class 声明,那么声明的UI控件就有“面向对象”的能力,实际上只希望它是一个UI控件声明,不需要它有其他的能力。难道不能对 @Component 装饰过的对象收回“面向对象”的能力么?当然能啊,估计要做很多编译检查的事儿。另外,从开发理解的层面上来讲,它确实也已经不是"对象"了,它只是一个干巴巴的一个UI结构,所以干脆就搞了一个新的关键词 struct。

ArkUI 框架

整体框架使用的方式和 Jetpack Compose 类似,都是声明式UI框架。compose 里面使用 @Composable来标记某个方法这个方法便成了UI控件,控件里面的状态管理使用 remember+ mutableState来控制。而 ArkUI 通过 @State、@Link、@Prop 等装饰器来控制。了解了这些个装饰器的用法,基本上就能理解 ArkUI 的开发流程了。

构建 UI 的 @Component @Builder

@Component 和 @Builder 组合起来实现的差不多就是 Compose 里面使用 @Composable 装饰某个方法的作用,用于构建 UI 或可复用的逻辑单元。
@Component
用于创建一个自定义组件,组件可以包含独立的 UI 结构、状态管理和生命周期。

@Builder
定义可复用的 UI 片段,用于创建一个UI 构建函数,封装一段可复用的 UI 代码块。不是独立组件,而是嵌入到其他组件或布局中执行,主要作用是复用和逻辑隔离,例如:关于页面,里面的文本是差不多的样式,只是内容不一样,那么只需要保留一个 text 属性出来接收参数。或者某块UI比较复杂,可以抽离一部分UI成为一个独立的UI逻辑模块。

构建 UI 的状态控制装饰器

@State
比较常用的装饰器,和 Compose 里面 remember+mutableStateOf 的作用差不多,对应的值改变之后,对相关的使用到该属性UI的地方进行刷新。

@Prop
@Prop 装饰的变量和父组件建立单向的同步关系,@Prop变量允许在本地修改,但修改后的变化不会同步回父组件。

也就是在某个 @Component 的组件内有一个 @State 装饰的属性,传递到子 @Component 组件 @Prop 修饰的属性。子控件对这个属性修改之后,父控件不会对这个改变感知,父控件UI不会改变。

@Link
子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。
跟 @Prop 的作用类似,不过是双向的,子控件对这个属性修改之后,父控件会感知这个变化,父控件UI会随着这个属性改变而改变。

@BuilderParam
主要用于动态注入 UI 构建逻辑(即 @Builder 函数),实现父组件向子组件传递可定制的 UI 片段,也就是向子控件传递 UI 参数。

基本上比较常用到的就这些,还有很多例如:@LocalBuilder @StorageLink @Styles等,都是为了解决开发过过程中遇到的问题,但是只要掌握了 ArkUI UI组件的声明周期和状态管理的基本原理理解其他装饰器还是比较简单的。

总结

总体开发体验下来,鸿蒙开发学习成本并不是特别高,比较快能上手,但设计的 api 更像一个缝合怪,且使用上不太收敛。很多库还需要再建设,例如音视频开发对应的支持库还不是特别成熟。不过,作为一个从头搞的生态来说能实现成这样已经很不错了,就像此前武磊登陆西甲,以及目前被看好的青年新星王钰栋,都是"自己的孩子",需要迈出第一步。现在,很多公司也在适配鸿蒙了,期待未来能从 Android 跟 iOS 的生态中争夺出一片大市场。

工程相关源码:
blog_harmony

相关文章:

鸿蒙ArkUI体验:Hexo博客客户端开发心得

最近部门也在跟进鸿蒙平台的业务开发&#xff0c;自己主要是做 Android 开发&#xff0c;主要使用 Kotlin/Java 语言。&#xff0c;需要对新的开发平台和开发模式进行学习&#xff0c;在业余时间开了个项目练手&#xff0c;做了个基于 Hexo 博客内容开发的App。鸿蒙主要使用Ark…...

鸿蒙NEXT开发动画案例10

1.创建空白项目 2.Page文件夹下面新建Spin.ets文件&#xff0c;代码如下&#xff1a; interface TranslateOffset {x?:numbery?:number } /*** SpinKit动画组件 - SpinTen* author: CSDN-鸿蒙布道师* since: 2025/05/16*/ ComponentV2 export struct SpinTen {Require Para…...

Python 的 os 库常见使用方法(操作目录及文件)

前言&#xff1a; os 模块是 Python 标准库中用于与操作系统交互的核心模块&#xff0c;提供了许多操作文件和目录的功能。以下是常见的使用方法&#xff1a; 1. 目录操作 方法功能说明示例os.getcwd()获取当前工作目录print(os.getcwd())os.chdir(path)切换当前工作目录os.ch…...

【Linux】Linux安装并配置Redis

目录 1.安装 2.启动服务 3.配置 3.1.绑定地址 3.2.保护模式 3.3.持久化选项 3.3.1.RDB 持久化 3.3.2.AOF 持久化 3.3.3.如何选择 1.安装 Redis 可以从默认的 CentOS 软件仓库中安装。运行以下命令来安装 Redis sudo dnf install redis -y 响应如下 2.启动服务 安装完成后&…...

【11408学习记录】考研英语辞职信写作三步法:真题精讲+妙句活用+范文模板

应聘信 英语写作2005年考研英语真题小作文写作思路第一段第二段妙句7 9妙句11补充3补充4 第三段 妙句成文 每日一句词汇第一步&#xff1a;找谓语第二步&#xff1a;断句第三步&#xff1a;简化主句原因状语从句 英语 写作 2005年考研英语真题小作文 Directions:​​ Two m…...

数据库(一):分布式数据库

定义 分布式数据库&#xff08;Distributed Database&#xff09; 是指&#xff1a; 数据分布在多个物理位置&#xff0c;但对用户透明&#xff0c;表现为一个统一逻辑数据库的系统。 结构模式&#xff08;三层模式扩展&#xff09; 层次作用对应实体用户层提供统一视图&…...

Java并发编程-线程池(三)

文章目录 线程池实现原理addWorker(Runnable firstTask, boolean core)1. 状态检查&#xff1a;校验线程池是否允许添加线程2. 工作线程数调整&#xff1a;CAS保证并发安全3. 初始化变量4. 创建 Worker 对象并获取线程5. 加锁保证线程安全6. 启动工作线程7. 异常处理核心作用 线…...

《黑马前端ajax+node.js+webpack+git教程》(笔记)——node.js教程+webpack教程(nodejs教程)

黑马程序员前端AJAX入门到实战全套教程&#xff0c;包含学前端框架必会的&#xff08;ajaxnode.jswebpackgit&#xff09;&#xff0c;一套全覆盖 文章目录 Node.js与Webpack-01.Node.js入门定义和作用什么是前端工程化&#xff1f;&#xff08;离不开node.js&#xff09;Node.…...

Flink 快速入门

本文涉及到大量的底层原理知识&#xff0c;包括运行机制图解都非常详细&#xff0c;还有一些实战案例&#xff0c;所以导致本篇文章会比较长&#xff0c;内容比较多&#xff0c;由于内容太多&#xff0c;很多目录可能展示不出来&#xff0c;需要去细心的查看&#xff0c;非常适…...

高效管理多后端服务:Nginx 配置与实践指南

在现代的 Web 开发和运维中&#xff0c;一个系统往往由多个后端服务组成&#xff0c;每个服务负责不同的功能模块。例如&#xff0c;一个电商网站可能包括用户服务、订单服务和支付服务&#xff0c;每个服务都运行在独立的服务器或容器中。为了高效地管理这些服务并提供统一的访…...

阻塞队列:线程安全与生产者消费者模型解析

一、阻塞队列 阻塞队列就是基于普通队列做出扩展 1.线程安全的 如果针对一个已经满了的队列进行入队列&#xff0c;此时入队列操作就会阻塞&#xff0c;一直阻塞到队列不满&#xff08;其他线程出队列元素&#xff09;之后 如果针对一个已经空了的队列进行出队列&#xff0c…...

【入门|Docker】基础知识扫盲:什么是 Docker?

文章目录 基础知识扫盲&#xff1a;什么是 Docker&#xff1f;Docker 是什么&#xff1f;Docker 核心组件Docker 与虚拟机的区别Docker 在现代开发中的核心角色Docker 的局限性 基础知识扫盲&#xff1a;什么是 Docker&#xff1f; 最近打算开始系统性地学习与云计算相关的技术…...

如何利用 Java 爬虫获得某书笔记详情:实战指南

在知识分享和学习的领域&#xff0c;许多平台提供了丰富的书籍笔记和学习资源。通过 Java 爬虫技术&#xff0c;我们可以高效地获取这些笔记的详细信息&#xff0c;以便进行进一步的分析和整理。本文将详细介绍如何利用 Java 爬虫获取某书笔记详情&#xff0c;并提供完整的代码…...

【MYSQL】基本查询,表的增删查改

&#x1f4da; 博主的专栏 &#x1f427; Linux | &#x1f5a5;️ C | &#x1f4ca; 数据结构 | &#x1f4a1;C 算法 | &#x1f152; C 语言 | &#x1f310; 计算机网络 |&#x1f5c3;️ mysql 摘要&#xff1a;本文详细介绍了MySQL中的CRUD操作&#xff08;创…...

在嵌入式系统中, 一般链路层断开多久,断开TCP为好

一、典型场景与推荐策略 1. 实时性优先&#xff08;工业控制、自动化设备&#xff09; 需求&#xff1a;快速释放资源&#xff0c;避免因等待重传浪费内存或阻塞任务。 策略&#xff1a; 立即断开&#xff1a;在lwip_netif_link_callback中检测到链路断开后直接关闭TCP连接&a…...

Android Studio 日志系统详解

文章目录 一、Android 日志系统基础1. Log 类2. 日志级别 二、Android Studio 中的 Logcat1. 打开 Logcat2. Logcat 界面组成3. 常用 Logcat 命令 三、高级日志技巧1. 自定义日志工具类2. 打印方法调用栈3. 打印长日志4. JSON 和 XML 格式化输出 四、Logcat 高级功能1. 自定义日…...

基于matlab的D2D 功率控制仿真

基于MATLAB的D2D&#xff08;Device-to-Device&#xff09;功率控制仿真示例&#xff0c;包含系统建模、功率控制算法实现和性能分析。该仿真以蜂窝网络为背景&#xff0c;重点关注D2D用户间的干扰管理和功率优化。 1. 系统模型与参数设置​ clc; clear; close all;%% 参数配置…...

互联网大厂Java面试:从基础到复杂场景的技术挑战

互联网大厂Java面试&#xff1a;从基础到复杂场景的技术挑战 场景描述 在一家知名互联网大厂的会议室里&#xff0c;面试官严肃地坐在桌子的一侧&#xff0c;而对面则是一位充满喜感的应聘者——谢飞机。面试官准备了一系列关于Java技术栈的提问&#xff0c;涵盖了从基础到复…...

使用Redission来实现布隆过滤器

简述布隆过滤器 布隆过滤器是一种概率型数据结构&#xff0c;它可以用来判断一个元素是否在一个集合中。我们当时使用的是Redisson实现的布隆过滤器。它的底层原理是&#xff0c;先初始化一个比较大的数组&#xff0c;里面存放的是二进制0或1。一开始都是0&#xff0c;当一个k…...

为 Windows 和 Ubuntu 中设定代理服务器的详细方法

有时下载大模型总是下载不出来&#xff0c;要配置代理才行 一、Windows代理设置 ① 系统全局代理设置 打开【设置】→【网络和Internet】→【代理】。 在【手动设置代理】下&#xff0c;打开开关&#xff0c;输入&#xff1a; 地址&#xff1a;10.10.10.215 端口&#xff1a;…...

Feign异步模式丢失上下文问题

Feign异步模式丢失上下文问题 问题描述 当我们使用异步对我们代码进行操作优化时&#xff0c;代码中使用了RequestContextHolder去获取上下文的数据&#xff0c;当我们执行原来可以执行的业务时发现报了空指针异常或数据为空&#xff0c;这是为什么呢&#xff1f; 原理解释 …...

OpenCV阈值处理完全指南:从基础到高级应用

引言 阈值处理是图像处理中最基础、最常用的技术之一&#xff0c;它能够将灰度图像转换为二值图像&#xff0c;为后续的图像分析和处理奠定基础。本文将全面介绍OpenCV中的各种阈值处理方法&#xff0c;包括原理讲解、代码实现和实际应用场景。 一、什么是阈值处理&#xff1…...

【AWS入门】Amazon SageMaker简介

【AWS入门】Amazon SageMaker简介 [AWS Essentials] Brief Introduction to Amazon SageMaker By JacksonML 机器学习(Machine Learning&#xff0c;简称ML) 是当代流行的计算机科学分支技术。通常&#xff0c;人们在本地部署搭建环境&#xff0c;以满足机器学习的要求。 AWS…...

ArcGIS Pro 3.4 二次开发 - 内容

环境&#xff1a;ArcGIS Pro SDK 3.4 .NET 8 文章目录 内容1 工程1.1 创建一个空工程1.2 使用指定名称创建新工程1.3 使用Pro的默认设置创建新工程1.4 使用自定义模板文件创建新工程1.5 使用 ArcGIS Pro 提供的模板创建工程1.6 打开现有工程1.7 获取当前工程1.8 获取当前工程的…...

如何在 MongoDB 中设计文档结构?与关系型数据库的表结构设计有何不同?

在 MongoDB 中设计文档结构是一个核心且重要的环节&#xff0c;它直接影响应用的性能、可扩展性和可维护性。 MongoDB 文档结构设计原则与方法 MongoDB 的核心思想是数据如何被应用访问&#xff0c;就如何存储它。 嵌入 (Embedding / Denormalization) vs. 引用 (Referencing…...

MYSQL 故障排查与生产环境优化

目录 一.前置知识点 1. 案例需求 &#xff08;1&#xff09;mysql 常见故障解决 &#xff08;2&#xff09;mysql 性能优化 2.案例实施思路 &#xff08;1&#xff09;单库常见故障分析 &#xff08;2&#xff09;主从常见故障分析 &#xff08;3&#xff09;从几个不同…...

解决使用@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss“, timezone = “GMT+8“)时区转换无效的问题

前言 对于一些时间的字段&#xff0c;我们从数据库查询出来通常需要转换后返回给前端展示&#xff0c;前端需要的格式一般为yyyy-MM-dd HH:mm:ss&#xff0c;可以通过JsonFormat注解来作转换和时区转换。 问题场景 原因 LocalDateTime类本身不带时区信息所以转换无效 解决办…...

计算机网络概要

⽹络相关基础知识 协议 两设备之间使⽤光电信号传输信息数据 要想传递不同信息 那么⼆者ᳵ就需要约定好的数据格式 层 封装 继承 多态是计算机的性质 它们⽀持了软硬件分层的实现 同层协议可以ᳵ接通信 同层协议ᳵ不直接通信 是各⾃调⽤下层提供的结构能⼒完成通信 分层…...

Word压缩解决方案

Word压缩解决方案&#xff1a;基于图片压缩的 .docx 优化实践 &#x1f4cc; 背景 在日常科研写作或项目文档整理中&#xff0c;Word 文档&#xff08;.docx&#xff09;往往因为插入大量高清图表、扫描图像、公式图等导致文件体积过大&#xff0c;或者毕业学位论文查重要求上…...

Spring Boot开发—— 整合Lucene构建轻量级毫秒级响应的全文检索引擎

文章目录 一、为什么选择 Lucene?轻量级搜索的底层密码二、核心原理:Lucene 的倒排索引2.1 倒排索引:速度之源2.2 段合并优化策略三、Spring Boot集成Lucene实战3.1 依赖配置3.2 实体与索引设计3.3 核心索引服务(含异常处理)3.4 使用示例(测试类)四、高级优化技巧4.1 索…...