陌上华年
陌上华年
  • 发布:2025-11-01 15:04
  • 更新:2025-11-01 15:13
  • 阅读:59

【鸿蒙征文】UniApp(X) 让鸿蒙开发触手可及 —— LimeUI 组件库开发简录

分类:uni-app x

引言:鸿蒙开发,真的有那么难吗?🤔

当鸿蒙系统(HarmonyOS)带着"分布式"、"原生智能"等前沿概念亮相时,许多开发者,尤其是前端开发者,心中难免产生畏惧:学习全新的 ArkTS 语言、掌握 DevEco Studio IDE、理解复杂的系统架构……这些障碍,似乎构筑了一道难以逾越的高墙。

然而,这种复杂的印象可能只是一种误解。如果你已经熟悉 Vue.js,那么惊喜来了——你与鸿蒙应用开发之间的距离,其实只隔了一个 UniApp(X)。本文将结合我开发 LimeUI 组件库的实战经历,向你证明:借助 UniApp(X),鸿蒙应用开发真的可以"有手就行",让前端开发者轻松拥抱鸿蒙生态。

第一章:UniApp(X) —— 鸿蒙开发的高效解决方案 🚀

在着手为鸿蒙生态贡献组件库时,作为一名熟悉UniApp的开发者,我自然选择了UniApp(X)。正是因为它会编译为ArkTS,能保持与原生相同的性能,同时极大地降低了开发门槛:

1. 零门槛的语法亲和性 ✨

对于Vue开发者而言,UniApp(X)的语法几乎是零学习成本的。你可以继续使用熟悉的template-script-style结构,继续运用v-model、v-if等指令。这种无缝衔接的体验,让鸿蒙开发的"陌生感"瞬间烟消云散。

2. 智能的平台差异抹平机制 🔄

UniApp(X) 的核心魅力在于其强大的条件编译系统。在开发 LimeUI 组件库时,我的代码结构通常是这样的:

<template>  
    <view class="l-button" @click="handleClick">  
      <text>{{ text }}</text>  
    </view>  
</template>  
<script setup lang="uts">  
  import { ButtonProps } from './type';  
  const emit = defineEmits(['click'])  
  const props = withDefaults(defineProps<ButtonProps>(), {  
    disabled: false,  
    ghost: false,  
    loading: false,  
    shape: 'rectangle',  
    size: 'medium',  
    type: 'default',  
})   

const handleClick = () => {  
    emit('click')  
    // #ifdef APP-HARMONY  
    // 鸿蒙平台特有的逻辑  
    console.log('Running on HarmonyOS!');  
    // #endif  
    // #ifdef MP-WEIXIN  
    // 微信小程序特有的逻辑  
    console.log('Running on WeChat!');  
    // #endif  
}  
</script>

通过简洁的#ifdef预处理指令,我能够轻松为不同平台(鸿蒙、微信小程序、iOS、Android等)编写差异化代码,同时保持核心业务逻辑的一致性。这种"一次开发,多端部署"的能力,让LimeUI组件库的开发效率得到了质的飞跃。

第二章:LimeUI 开发实战 —— 鸿蒙组件库的"简易模式" 🛠️

空谈理论不如实战演练。下面,我将以 LimeUI 中一个简单按钮组件的开发流程为例,带你体验这份"有手就行"的简单与高效。

步骤 1:环境搭建 —— 极简配置,快速上手 ⚡

环境配置非常简单直观,无需担心复杂的鸿蒙原生开发环境问题。按照官方教程完成几个基本步骤即可快速上手:运行和发行教程

步骤 2:组件开发 —— 用 Vue 的方式写鸿蒙组件 🎨

只需在创建 uni_modules 组件时选择相应的组件类型:

生成的uni_modules 组件目录结构如下:

├─pages  
│  └─index  
│     └─index.uvue  
└─uni_modules  
│  └─lime-button  
│     │─components  
│     │  └─lime-button  
│     │      └─lime-button.uvue  // 组件实现  
│     │      └─type.ts       // 类型定义

接下来,让我们开始开发 lime-button 组件:

<!-- lime-button.uvue -->  
<template>  
    <view class="l-button" @click="handleClick">  
      <text>{{ text }}</text>  
    </view>  
</template>  
<script setup lang="uts">  
  import { ButtonProps } from './type';  
  const emit = defineEmits(['click'])  
  const props = withDefaults(defineProps<ButtonProps>(), {  
    block: false,  
    disabled: false,  
    ghost: false,  
    loading: false,  
    shape: 'rectangle',  
    size: 'medium',  
    type: 'default',  
    hoverStopPropagation: false,  
    hoverStartTime: 20,  
    hoverStayTime: 70,  
    lang: 'en',  
    sessionFrom: '',  
    sendMessageTitle: '',  
    sendMessagePath: '',  
    sendMessageImg: '',  
    appParameter: '',  
    showMessageCard: false  
})   

const handleClick = () => {  
    emit('click')  
}  
</script>

看到了吗?这完全就是标准的Vue单文件组件!没有任何鸿蒙原生的特定语法。你已掌握的Vue知识,就是开发鸿蒙组件的全部技能储备。这种熟悉感,让开发者能够立即进入高效开发状态。

步骤 3:编译与预览 —— 所见即所得 👀

在HBuilderX中,只需在manifest.json中配置好鸿蒙应用信息,然后点击菜单栏的"运行 > 运行到手机或模拟器 > 运行到鸿蒙"。

接下来,UniApp(X) 编译器会自动将你编写的 .(u)vue 文件,编译转换为标准的鸿蒙原生工程和 ArkTS 代码。你无需关心底层复杂的转换过程,只需静待编译完成,就能在模拟器上看到组件完美运行。

这正是"有手就行"的最佳诠释。你只需用熟悉的语法表达业务逻辑,UniApp(X)则默默处理好所有平台适配的复杂工作。在将整个UI组件库适配到鸿蒙平台的过程中,我几乎没有遇到实质性的技术障碍,这也是UniApp(X)最大的魅力所在。

第三章:进阶挑战与解决方案 —— 当需要调用原生能力时 💪

当然,"有手就行"并不意味着毫无挑战。在开发LimeUI组件库的过程中,我也遇到过需要调用鸿蒙平台特有能力的场景。

挑战场景:我需要开发一个功能强大的lime-svg组件,它不仅要支持颜色修改,还要兼容多种加载方式(路径、base64、XML)。🎨

解决方案:UniApp(X)贴心地提供了native-view机制,让我们能够轻松调用鸿蒙原生能力。只需在创建uni_modules组件时选择"创建uts插件-标准组件":🔌

生成的uni_modules 组件目录结构如下:

├─pages  
│  └─index  
│     └─index.uvue  
└─uni_modules  
│  └─lime-svg  
│     │─components  
│     │  └─lime-svg  
│     │      └─lime-svg.uvue // 组件调用层  
│     └─utssdk  
│         └─app-harmony  
│             └─builder.ets  // 原生组件实现  
│             └─index.uts    // 桥接类导出

在组件调用层 lime-svg.uvue 中,我们这样编写:

<template>  
    <native-view class="l-svg" v-bind="$attrs" @init="onviewinit"></native-view>  
</template>  
<script setup lang="uts">  

import { SvpProps } from './type'  
import { NativeSvg } from "@/uni_modules/lime-svg"; // 导入桥接类  

let nativeSvg : NativeSvg | null = null  
const props = withDefaults(defineProps<SvpProps>(), {  
    src: '',  
    color: ''  
})  

const onviewinit = (e : UniNativeViewInitEvent) => {  
    nativeSvg = new NativeSvg(e.detail.element); // 传入native-view元素  
    nativeSvg?.updateSrc(props.src) // 调用实例方法更新资源  
    nativeSvg?.updateColor(props.color) // 调用实例方法更新颜色  
}  
</script>

在桥接类 index.uts 中,我们实现与原生能力的对接:

import { BuilderNode } from "@kit.ArkUI"  
import buffer from '@ohos.buffer';  
import { fileIo } from '@kit.CoreFileKit';  
// 导入混编实现的声明式UI构建函数  
import { buildSvg } from "./builder.ets"  
import { getEnv } from '@dcloudio/uni-runtime';  

export class NativeSvg {  
    private $element : UniNativeViewElement;  
    private builder : BuilderNode<[NativeSvgOptions]> | null = null  
    private svgMap : Map<string, string> = new Map<string, string>()  
    // 初始化 buildSvg 默认参数  
    private params : NativeSvgOptions = {  
        src: '',  
        onError: (message) => {  
            this.$element.dispatchEvent(new UniNativeViewEvent("error", { message }))  
        },  
        onComplete: (event : ESObject) => {  
            this.$element.dispatchEvent(new UniNativeViewEvent("load", {  
                width: event.width,  
                height: event.height  
            }))  
        },  
    }  

    constructor(element : UniNativeViewElement) {  
        // 绑定 wrapBuilder 函数  
        this.builder = element.bindHarmonyWrappedBuilder(wrapBuilder<[NativeSvgOptions]>(buildSvg), this.params)  
        this.$element = element  
        // 绑定当前实例为自定义的controller,方便其他地方通过 element 获取使用  
        this.$element.bindHarmonyController(this)  
    }  

    updateSrc(src : string) {  
        if (src.startsWith('data:image') || src.startsWith('<svg')) {  
            if (this.svgMap.has(src)) {  
                this.params.src = this.svgMap.get(src)!  
            } else {  
                // 处理临时文件路径  
                const tempFileName = `${Date.now()}.svg`  
                const tempDirPath = `${getEnv().TEMP_PATH}/svg`  
                const tempFilePath : string = `${tempDirPath}/${tempFileName}`  

                // 确保目录存在  
                if (!fileIo.accessSync(tempDirPath)) {  
                    fileIo.mkdirSync(tempDirPath, true)  
                }  

                // 创建并写入文件  
                const file = fileIo.openSync(tempFilePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE);  
                // 根据不同格式保存SVG内容  
                if (src.startsWith('<svg')) {  
                    fileIo.writeSync(file.fd, src); // 直接写入XML文本  
                }  
                // 获取资源文件的原生路径  
                const path = UTSHarmony.getResourcePath(tempFilePath)  
                this.svgMap.set(src, path) // 缓存已处理的资源  
                this.params.src = path  
            }  
        }  
        else {  
            // 处理普通资源路径  
            this.params.src = UTSHarmony.getResourcePath(src)  
        }  
        this.builder?.update(this.params) // 更新渲染  
    }  

    updateColor(color : string) {  
        this.params.color = color  
        this.builder?.update(this.params) // 更新渲染  
    }  
}

看到代码中我导入了一些原生库,有小伙伴可能会好奇这些库是如何知道的。实际上,通过查阅华为开发者文档,搜索"如何创建临时文件"等相关问题,华为的智能小助手就能直接提供相关代码参考。我们可以基于这些参考代码,根据实际需求进行适当修改和调整,轻松实现临时文件创建等功能。


(cv大师就是我)

最后,在原生渲染层 builder.ets 中,我们定义实际的渲染逻辑:

@Builder  
export function buildSvg(params: ESObject) {  
    Image(params.src)  
        .width('100%')  
        .height('100%')  
        .objectFit(ImageFit.Contain)  
        .fillColor(params.color) // 支持动态修改颜色  
        .onComplete((event)=>{  
            params.onComplete(event)  
        })  
        .onError((error) =>{  
            params.onError(error.message)  
        })  
}

通过这种方式,即使是需要调用鸿蒙特定功能,也能通过UniAppX的UTS标准组件机制轻松实现。这种优雅的桥接设计,让我们既能享受Vue开发的便捷,又能在需要原生能力的关键地方获得与原生开发完全一致的能力。而这只是UniApp(X)调用鸿蒙生态能力的一种方式,在下一章中,我们将探索如何通过UTS API灵活调用OpenHarmony三方库中心仓的第三方库和鸿蒙系统自带的原生库,实现更灵活的功能扩展。

第四章:进阶实战 —— 用UTS API调用OpenHarmony三方库中心仓的第三方库 🚀

如果说组件开发是"有手就行",那么直接调用OpenHarmony生态库就是"如虎添翼"。UniApp(X)提供的UTS(Uni TypeScript)能力,不但能调用鸿蒙系统自带的原生能力,还能加载(ohpm,类似npm的包管理平台)的第三方库,让我们可以在页面中轻松访问各种系统API和三方库功能,为LimeUI组件库的功能扩展提供了无限可能。

案例背景:lime-crypto加密库开发 🔐
在开发过程中,我需要一个强大的加密功能库。虽然在传统UniApp中可以使用crypto-js库,但在UniAppX中并不支持。因此,我决定通过UTS机制直接调用发布到OpenHarmony三方库中心仓的@ohos/crypto-js第三方库来实现加密功能。

实现步骤:简单三步走 📋
只需在创建uni_modules组件时选择"创建uts插件-API插件":

生成的uni_modules组件目录结构如下,与lime-svg类似,它也遵循了UniApp(X)的插件规范:

├─pages  
│  └─index  
│     └─index.uvue  
└─uni_modules  
│  └─lime-crypto  
│     └─utssdk  
│         └─app-harmony  
│             └─config.json // 原生依赖配置  
│             └─index.uts   // UTS桥接层实现

第一步:配置原生依赖 📦
在lime-crypto/utssdk/app-harmony/config.json中声明依赖:

{  
    "dependencies": {  
        "@ohos/crypto-js": "2.0.4"  
    }  
}

就是这么简单!UniApp(X)会自动处理依赖管理。
第二步:编写 UTS 桥接层 🌉
在lime-crypto/utssdk/app-harmony/index.uts中:

import { CryptoJS } from '@ohos/crypto-js'  
export class CryptoImpl {  
    constructor() {  
        // 初始化逻辑  
    }  

    // 获取编码器  
    get enc() {  
        return {  
            Utf8: CryptoJS.enc.Utf8,  
            Hex: CryptoJS.enc.Hex,  
            Base64: CryptoJS.enc.Base64,  
            // ... 其他编码器  
        }   
    }  

    // 加密算法  
    get AES() : CryptoJS.CipherHelper {  
        return CryptoJS.AES  
    }  

    get DES(): CryptoJS.CipherHelper {  
        return CryptoJS.DES  
    }  

    // 哈希函数  
    MD5(message: string) {  
        return CryptoJS.MD5(message)  
    }  

    SHA256(message: string) {  
        return CryptoJS.SHA256(message)  
    }  

    // HMAC 签名  
    HmacSHA256(message: string, secretKey: string) {  
        return CryptoJS.HmacSHA256(message, secretKey)  
    }  
}  

export function useCrypto() {  
    return new CryptoImpl()  
}

有细心的小伙伴可能会发现,为何我要封装一个CryptoImpl类,直接导出CryptoJS不香吗?实际上,虽然直接导出CryptoJS是可行的,但封装一个专门的实现类可以更好地控制API暴露范围,提供更符合业务需求的接口,并为后续可能的功能扩展和维护提供便利(借口,凑字数而已)。

export function useCrypto() {  
    return CryptoJS //全网最简单的加密库实现(搬运工的日常)  
}

第三步:在Vue组件中使用 🎯
现在,我们可以在普通的 Vue 组件中直接使用这个原生加密库了:

<template>  
    <view class="demo-container">  
        <lime-button @click="encryptData">加密测试</lime-button>  
        <text>{{ encryptedText }}</text>  
    </view>  
</template>  

<script setup lang="uts">  
import { useCrypto } from '@/uni_modules/lime-crypto'  

const crypto = useCrypto()  
const encryptedText = ref('')  

const encryptData = () => {  
    // 使用鸿蒙原生加密库进行 AES 加密  
    const encrypted = crypto.AES.encrypt(  
        'Hello HarmonyOS',   
        'secret-key-12345',   
        {   
            mode: crypto.mode.CBC,  
            padding: crypto.pad.Pkcs7   
        }  
    )  

    encryptedText.value = encrypted.toString()  
    console.log('加密结果:', encryptedText.value)  

    // 使用 SHA256 哈希  
    const hash = crypto.SHA256('需要哈希的数据')  
    console.log('SHA256 结果:', hash.toString())  
}  
</script>

通过lime-crypto的实现,我们可以看到UniApp(X)的UTS能力真正实现了:"用前端熟悉的语法,调用原生API的便利"。就这?就这么简单!我上我也行了!成为鸿蒙开发大佬,从此走向人生的鼎峰!

开发者既能利用Vue框架的开发便捷性快速构建UI界面,又能在需要原生能力的关键地方直接调用鸿蒙原生API。这种灵活的技术架构,为鸿蒙应用开发提供了高效且强大的解决方案。

不过,当前的开发体验仍有一些可以改进的空间:ArkTS引擎在代码修改后需要重新构建、签名和安装,这增加了开发过程中的等待时间;另外,首次编译所需的时间相对较长。希望未来的版本能够优化这些方面,进一步提升开发效率。

结语:鸿蒙开发,触手可及 🌈

通过LimeUI组件库的开发实践,我深刻体会到:UniApp(X)极大地降低了鸿蒙应用开发的技术门槛。作为开发者,你完全可以利用已掌握的Vue技术栈,以熟悉的开发方式快速进入鸿蒙开发领域。

鸿蒙生态正处于高速发展阶段,对于开发者而言,这是一片充满机遇的蓝海。UniApp(X)作为连接Vue技术栈与鸿蒙生态的桥梁,为开发者提供了一条低门槛、高效率的技术路径。

对于正在观望鸿蒙开发的开发者来说,现在正是借助UniApp(X)进入鸿蒙生态的理想时机。带着你熟悉的技术积累,你会发现鸿蒙开发并非想象中那样困难,而是可以通过现有技能平稳过渡的技术领域。

毕竟,能用熟悉的技术拥抱未来,这本身就是一件超酷的事,不是吗?💻✨

正是借助UniApp(X)的强大生态与开发便利性,我开发的LimeUI组件库中每个组件都作为独立插件上传到UniApp市场,目前已成功开源发布超过100款组件插件,为开发者社区贡献自己的一份力量!🚀

LimeUI组件库还同时支持UniApp和UniAppX双框架,让开发者可以在两个技术栈中无缝使用同一套组件,极大地提升了开发效率和代码复用性!

如果您在使用过程中发现组件库有任何不完善或缺少的组件,请随时在插件市场留言您的需求和建议。每一条反馈都是促使LimeUI不断完善的宝贵动力,我会认真对待并持续优化组件库,为开发者提供更好的使用体验!

如果您觉得我写的内容对您有所帮助,欢迎点赞加关注,一键三连支持!我第一次写文章,如果有不足之处,还请各位轻喷,我会不断学习和进步!

欢迎访问我的插件市场主页:https://ext.dcloud.net.cn/publisher?id=242774

1 关注 分享
2***@qq.com

要回复文章请先登录注册

2***@qq.com

2***@qq.com

lime-request也十分好用
2025-11-01 15:13