大家好,欢迎来到IT知识分享网。
本人很荣幸成为Taro适配鸿蒙开发的共建者,下面也是我在开发时一些心得体会。也是遇到了不少坑爬过来的。
Taro 对于鸿蒙的适配原理
HarmonyOS是一款面向万物互联时代的、全新的分布式操作系统。
Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发 微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ 小程序 / H5 / RN 等应用
HarmonyOS开发有三类:Java、Js、Native。鸿蒙Js的开发跟Taro的开发相似。可以按照我们Taro的代码解析成鸿蒙的代码。主要原理如下:
原理
整体原理
graph LR
A(taro cli) --> B(根据不同的type解析不同的插件)
B --> C(利用webpack去编译各端的代码)
C --> D(再利用各端自己的插件配置磨平各端的代码)
D --> E(生成harmony代码)
编译
整体原理
graph
A(taro代码) --> B(通过tarojs/cli开始编译)
B --> C(tarojs/service判断不同端使用的包)
C --> D(使用tarojs/mini-runner进行打包)
D --> E(通过plugin-platform-harmony插件抹平鸿蒙的代码)
E --> F(生成harmony的代码)
@tarojs/mini-runner的原理
graph LR
A(taro代码) --> B(AST解析)
B --> C(解析Render代码)
C --> D(利用脚本转换)
D --> E(生成代码)
编译鸿蒙时,CLI 会调用 @tarojs/mini-runner
包,这个包主要是对代码进行编译。mini-runner
主要做了这些事情:
- 负责根据开发者的编译配置调整 Webpack 配置
- 注入自定义的 PostCSS 插件。(如 postcss-pxtransform)
- 注入自定义的 Webpack 插件。
- 注入自定义的 Webpack Loaders。(Loaders 位于
@tarojs/taro-loader
包中) - 调用 Webpack 开启编译。
- 修改 Webpack 的编译产物,调整最终的编译结果。
再根据@tarojs/plugin-platform-harmony
进行调整,对于组件、API、路由会在这个插件进行磨平
对mini-runner
进行分析
├── src
| ├── config 项目配置文件
| ├── dependencies 项目依赖文件
| ├── loader 自定义loader
| | ├── quickappStyleLoader 快应用
| | └── miniTemplateLoader 小程序模版解析器
| ├── plugins 自定义插件
| ├── prerender 自定义的代码结构
| ├── template 自定义模版
| | ├── comp 组件模版
| | ├── component 自定义组件模版
| | └── custom-wrapper 自定义小程序插件
| ├── utils 公共配置
| ├── webpack webpack配置
| | ├── base.conf 基础配置
| | ├── build.conf 打包配置
| | ├── chain 插件和解析器
| | └── postcss.conf 对css转换的配置
| └── index webpack的启动
└── index 入口文件
注:babel的使用插件
babel使用 | 说明 |
---|---|
@babel/core | 解析代码获取 AST |
@babel/types | AST 节点定义,生成节点 |
@babel/traverse | 递归遍历操作 AST |
@babel/generator | AST 生成源码 |
源代码 –> 端代码
<View className="flex items-center h-44 pb-40">
<SlPrice price={ price } />
{
tags.length !== 0 &&
tags.map(tag =>
<View className="px-20 py-6 mx-12 bg-red-light text-yellow text-22 rounded-20" key={ tag }>{tag}</View>
)
}
</View>
(_tarojs_components__WEBPACK_IMPORTED_MODULE_1__[/* View */ "d"], {
className: "flex items-center h-44 pb-40",
children: [/*#__PURE__*/Object(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__["jsx"])(_jd_selling_c_ui__WEBPACK_IMPORTED_MODULE_2__[/* SlPrice */ "e"], {
price: price
}), tags.length !== 0 && tags.map(function (tag) {
return /*#__PURE__*/Object(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__["jsx"])(_tarojs_components__WEBPACK_IMPORTED_MODULE_1__[/* View */ "d"], {
className: "px-20 py-6 mx-12 bg-red-light text-yellow text-22 rounded-20",
children: tag
}, tag);
})]
})
模版处理
运行
为了让 React、Vue 等框架直接运行在鸿蒙端,我们需要在小程序的逻辑层模拟浏览器环境,包括实现 DOM、BOM API 等。
@tarojs/runtime
是 Taro 的运行时适配器核心,它实现了精简的 DOM、BOM API、事件系统、Web 框架和小程序框架的桥接层等。此包主要是对小程序和h5运行进行适配,因为 ReactDOM 体积较大,且包含很多兼容性代码。因此 Taro 借助 react-reconciler 实现了一个自定义渲染器用于代替 ReactDOM。渲染器位于 @tarojs/react 包中。
Web 框架就可以使用 Taro 模拟的 API 渲染出一颗 Taro DOM 树,但是这一切都运行在小程序的逻辑层。而小程序的 xml 模板需要提前写死,Taro 选择了利用小程序 <template>
可以引用其它 <template>
的特性,把 Taro DOM 树的每个 DOM 节点对应地渲染为一个个 <template>
。这时只需要把 Taro DOM 树的序列化数据进行 setData,就能触发 <template>
的相互引用,从而渲染出最终的 UI。
而鸿蒙这边没有<template>
的概念,我们用自定义组件element
代替。
运行时我们会将上述说的Taro DOM 树以一个整体变量 root
去运行代码。再根据root.cn
下的数组去遍历和递归我们自定义组件
页面内容
<element name="container" src="../../container/index.hml"></element>
<element name="navbar" src="../../container/components-harmony/navbar/index.hml"></element>
<element name="tabbar" src="../../container/components-harmony/tabbar/index.hml"></element>
<div class="container"> <navbar title="{{taroNavBar.title}}" background="{{taroNavBar.background}}" text-style="{{taroNavBar.textStyle}}" st="{{taroNavBar.style}}"></navbar> <div class="body" style="padding-top: 44px;padding-bottom: {{isShowTaroTabBar ? '56px' : '0'}}"> <refresh if="{{enablePullDownRefresh}}" type="pulldown" refreshing="{{isRefreshing}}" onrefresh="onPullDownRefresh"> <container root="{{root}}"></container> </refresh> <container else root="{{root}}"></container> </div> <tabbar if="{{isShowTaroTabBar}}" data="{{taroTabBar}}" selected="{{selected}}"></tabbar> </div>
root数据
{
"root": {
"cn": [
{
"cl": "layout bg-gray-lightbg",
"cn": [
{
"cn": [
{
"nn": "#text",
"v": "订单"
}
],
"nn": "view",
"uid": "_n_885"
},
{
"cl": "layout-area-slot w-full h-210 area-slot",
"cn": [],
"nn": "pure-view",
"uid": "_n_888"
}
],
"nn": "pure-view",
"uid": "_n_886"
}
],
"uid": "pages/tab-bar/order/index?$taroTimestamp=1636613920366"
},
}
container的内容
<element name="container" src="./index.hml"></element>
<element name="taro-textarea" src="./components-harmony/textarea/index.hml"></element>
<block for="{{i in root.cn}}"> <block if="{{i.nn == 'view'}}"> <div hover-class="{{i.hoverClass===undefined?'none':i.hoverClass}}" hover-stop-propagation="{{i.hoverStopPropagation===undefined?false:i.hoverStopPropagation}}" hover-start-time="{{i.hoverStartTime===undefined?50:i.hoverStartTime}}" hover-stay-time="{{i.hoverStayTime===undefined?400:i.hoverStayTime}}" @touchstart="{{eh}}" @touchmove="{{eh}}" @touchend="{{eh}}" @touchcancel="{{eh}}" @longtap="{{eh}}" animation="{{i.animation}}" @animationstart="{{eh}}" @animationiteration="{{eh}}" @animationend="{{eh}}" @transitionend="{{eh}}" style="{{i.st}}" class="{{i.cl}}" @click="{{eh}}" id="{{i.uid}}"> <container root="{{i}}"></container> </div> </block> <block if="{{i.nn == 'textarea'}}"> <taro-textarea value="{{i.value}}" placeholder="{{i.placeholder}}" placeholder-style="{{i.placeholderStyle}}" placeholder-class="{{i.placeholderClass===undefined?'textarea-placeholder':i.placeholderClass}}" disabled="{{i.disabled}}" maxlength="{{i.maxlength===undefined?140:i.maxlength}}" auto-focus="{{i.autoFocus===undefined?false:i.autoFocus}}" focus="{{i.focus===undefined?false:i.focus}}" auto-height="{{i.autoHeight===undefined?false:i.autoHeight}}" fixed="{{i.fixed===undefined?false:i.fixed}}" cursor-spacing="{{i.cursorSpacing===undefined?0:i.cursorSpacing}}" cursor="{{i.cursor===undefined?-1:i.cursor}}" selection-start="{{i.selectionStart===undefined?-1:i.selectionStart}}" selection-end="{{i.selectionEnd===undefined?-1:i.selectionEnd}}" @focus="{{eh}}" @blur="{{eh}}" @linechange="{{eh}}" @input="{{eh}}" @confirm="{{eh}}" name="{{i.name}}" headericon="{{i.headericon}}" showcounter="{{i.showcounter===undefined?false:i.showcounter}}" menuoptions="{{i.menuoptions===undefined?[]:i.menuoptions}}" softkeyboardenabled="{{i.softkeyboardenabled===undefined?true:i.softkeyboardenabled}}" @translate="{{eh}}" @share="{{eh}}" @search="{{eh}}" @optionselect="{{eh}}" @selectchange="{{eh}}" style="{{i.st}}" class="{{i.cl}}" @click="{{eh}}" id="{{i.uid}}"> </taro-textarea> </block> ... </block>
组件
Taro 在 鸿蒙 端实现了遵循微信小程序规范的基础组件库。
默认会使用 @tarojs/components
提供的 Web Components 组件库。
目前需要将Taro的组件库跟鸿蒙自带的组件库对接,将组建内属性和事件一一对齐。对于鸿蒙没有的组件,开发自定义组件对齐。因为鸿蒙的标签跟web的类似,目前需要对所有标签进行重新封装。主要有两类情况:
- 是将基本组件转换成我们目标组件 基本组件就是将Taro的基本组件直接适配到鸿蒙的基础组件库,如下代码所示:
buildStandardComponentTemplate (comp) {
let nodeName = ''
switch (comp.nodeName) {
case 'slot':
case 'slot-view':
case 'cover-view':
case 'view':
case 'swiper-item':
nodeName = 'div'
break
case 'static-text':
nodeName = 'text'
break
case 'static-image':
nodeName = 'image'
break
default:
nodeName = comp.nodeName
break
}
return this.generateComponentTemplateSrc(comp, nodeName)
}
generateComponentTemplateSrc (comp, nodeName?): string {
const children = this.voidElements.has(comp.nodeName)
? ''
: `<block for="{{i.cn}}"> <container i="{{$item}}"></container> </block>`
if (!nodeName) {
nodeName = comp.nodeName
}
if (this.nativeComps.includes(nodeName)) {
nodeName = `taro-${nodeName}`
}
const res = ` <block if="{{i.nn == '${comp.nodeName}'}}"> <${nodeName} ${this.buildAttrs(comp.attributes, comp.nodeName)} id="{{i.uid}}"> ${children} </${nodeName}> </block> `
return res
}
- 鸿蒙和Taro组建相同,但是属性不一样。对于这种情况我们将组件重新写成自定义组件,自定义组件中会把组件的属性和鸿蒙的对齐,如果鸿蒙实在没有相应属性,可以舍弃。对于鸿蒙的组件中有的属性在Taro中没有,在组件配置文件
/components/index.ts
中加入相应属性。
// program.ts
platform = 'harmony'
globalObject = 'globalThis'
runtimePath = `${PACKAGE_NAME}/dist/runtime`
taroComponentsPath = `${PACKAGE_NAME}/dist/components/components-react`
fileType = {
templ: '.hml',
style: '.css',
config: '.json',
script: '.js'
}
// components-harmony文件夹下
├── components-harmony
├──image
├──input
└──...
API
开发者从 @tarojs/taro
中引用 Taro 对象并使用它提供的 API。
在 鸿蒙 环境,@tarojs/taro
会从 @tarojs/api 取与平台无关的 API,从 @tarojs/plugin-platform-harmony
中取遵循鸿蒙规范实现的 API,最终集合成一个 Taro 对象暴露给开发者。
对于鸿蒙系统的开发,API跟Taro的API功能差不多,但是名称,属性都不一样。通过调用initNativeApi
方法我们将taro内部的的API方法直接替换掉,使用自定义的API方法去实现鸿蒙API的方法。
// /apis/index.ts
import { noop, current } from './utils'
import * as apis from './apis'
export function initNativeApi (taro) {
current.taro = taro
taro.initPxTransform = noop
Object.assign(taro, apis)
}
// apis文件夹下
├── apis
├── interactive
├── navbar
├── ...
└── index.ts
路由
依据app.config
里面pages进行页面划分,目前生成的代码是在default。需要手动将鸿蒙项目的config.json
修改
插件说明
公共
路径 | 说明 |
---|---|
@tarojs/cli | CLI 工具(init/build) |
@tarojs/taro-loader | Webpack loaders |
@tarojs/helper | 工具库,主要供 CLI、编译时使用 |
@tarojs/runner-utils | 工具库,主要供小程序、H5 的编译工具使用 |
@tarojs/shared | 工具库,主要供运行时使用 |
@tarojs/taro | 暴露各端所需要的 Taro 对象 |
@tarojs/api | 和各端无关的 Taro API |
babel-preset-taro | Babel preset |
eslint-config-taro | ESLint 规则 |
postcss-pxtransform | PostCSS 插件,转换 px 为各端的自适应尺寸单位 |
鸿蒙
路径 | 说明 |
---|---|
@tarojs/plugin-platform-harmony | 鸿蒙的编译适配 |
@tarojs/mini-runner | 小程序编译 |
开发
Taro 对鸿蒙的适配工作正在开发中,目前可以通过拉取 feat/harmony
分支的代码并编译后进行使用。
1. 下载 Taro 源码并编译
需要对鸿蒙的适配进行link
# 下载源码
$ git clone git@github.com:NervJS/taro.git
$ git checkout feat/harmony
# 安装依赖
$ yarn
# 编译
$ yarn build
# yarn link (harmony 基于 Taro v3.4,但 v3.4 暂时还未发布,所以需要把 3.4 改动的包都进行软链)
$ cd packages/taro-cli
$ yarn link
$ cd ../taro-runtime
$ yarn link
$ cd ../taro-harmony
$ yarn link
$ cd ../taro-plugin-react
$ yarn link
$ cd ../taro-plugin-vue2
$ yarn link
$ cd ../taro-plugin-vue3
$ yarn link
$ cd ../taro-mini-runner
$ yarn link
$ cd ../taro
$ yarn link
$ cd ../taro-api
$ yarn link
$ cd ../shared
$ yarn link
$ cd ../taro-loader
$ yarn link
2. 安装 @tarojs/cli,创建 Taro 项目,建立软链
安装 @tarojs/cli(如本地已安装过,可跳过这步)
# 使用 npm 安装 CLI
$ npm install -g @tarojs/cli
# OR 使用 yarn 安装 CLI
$ yarn global add @tarojs/cli
# OR 安装了 cnpm,使用 cnpm 安装 CLI
$ cnpm install -g @tarojs/cli
创建 Taro 项目,建立软链
taro init myApp
cd myApp
yarn link @tarojs/runtime
yarn link @tarojs/plugin-platform-harmony
yarn link @tarojs/plugin-framework-react
yarn link @tarojs/plugin-framework-vue2
yarn link @tarojs/plugin-framework-vue3
yarn link @tarojs/mini-runner
yarn link @tarojs/taro
yarn link @tarojs/api
yarn link @tarojs/shared
yarn link @tarojs/taro-loader
# OR
yarn link @tarojs/runtime @tarojs/plugin-platform-harmony @tarojs/plugin-framework-react @tarojs/plugin-framework-vue2 @tarojs/plugin-framework-vue3 @tarojs/mini-runner @tarojs/taro @tarojs/api @tarojs/shared @tarojs/taro-loader
3. 编译配置
// config/index.js
config = {
// 配置输出路径,输出到鸿蒙 JS FA 的对应目录下。(支持绝对路径、相对路径)
// 例如鸿蒙应用名称为 MyApplication,JS FA 名称为默认的 default,那么 outputRoot 需要设置为:
// 'MyApplication/entry/src/main/js/default'
outputRoot: '...',
// 配置使用插件
plugins: ['@tarojs/plugin-platform-harmony'],
mini: {
// 如果使用开发者工具的预览器(previewer)进行预览的话,需要使用 development 版本的 react-reconciler。
// 因为 previewer 对长串的压缩文本解析有问题。(真机/远程真机没有此问题)
debugReact: true,
// 如果需要真机断点调试,需要关闭 sourcemap 的生成
enableSourceMap: false
}
}
4. 鸿蒙项目配置
鸿蒙配置文件的详细文档:配置文件的元素
4.1 配置路由
假设有鸿蒙项目 MyApplication,如果需要配置路由,可以修改 MyApplication/entry/src/main/config.json
的 modules.js
配置。
4.2 关闭系统自带的顶部导航栏
修改 MyApplication/entry/src/main/config.json
的 modules
配置,增加以下内容:
"metaData": {
"customizeData":[
{
"name": "hwc-theme",
"value": "androidhwext:style/Theme.Emui.NoTitleBar"
}
]
}
5. 编译
taro build --type harmony --watch
6. 运行
6.1 创建一个鸿蒙 JS 项目,如 MyApplication
华为开发者工具:developer.harmonyos.com/cn/develop/… 鸿蒙开发文档:developer.harmonyos.com/cn/document…
6.2 使用鸿蒙开发者工具的 previewer、真机、远程真机进行预览
previewer文档:developer.harmonyos.com/cn/docs/doc…
sudo launchctl unload /Library/LaunchDaemons/com.terminal.plist
关掉公司电脑自动删除 adb 服务的启动项
安装 adb 工具:
brew install android-platform-tools
demo
鸿蒙Taro项目:内容
在此感谢Taro的作者们对我的帮助和悉心教导。Taro适配鸿蒙的开发版本会在这个月底开放出来,希望大家多多指正。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/13912.html