上个月组里分享的一个东西,记录在博客里吧,主要是看的掘金上的这篇文章:
参考链接:
https://juejin.cn/post/7031826512126935076
https://github.com/NervJS/taro/discussions/9639

———————————————————————————————————————————————————————————

Taro 是一个开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发 微信 / 京东 / 百度 / 支付宝 / 字节跳动 / QQ 小程序 / H5 / RN 等应用。

解决微信小程序的一系列痛点

小程序开发没有融入目前主流的工程化开发思想,很多业界开发模式与工具没有在小程序开发中得到相应体现。

开发方式上:

  • 没有自定义文件预处理,无法直接使用 Sass、Less 以及较新的 ES.Next 语法;
  • 字符串模板太过孱弱,小程序的字符串模板仿的是 Vue,但是没有提供 Vue 那么多的语法糖,当实现一些比较复杂的处理时,写起来就非常麻烦,虽然提供了 wxs 作为补充,但是使用体验还是非常糟糕;
  • 缺乏测试套件,无法编写测试代码来保证项目质量,也就不能进行持续集成,自动化打包。

代码规范上:

小程序的规范有很多不统一的地方,例如内置组件的属性名,有时候是全小写,有时候是 CamelCase (驼峰)格式,有时候又是中划线分割的形式,这样就导致编码的时候得不时查阅文档才能确定写法。

taro支持多端

Write once, run anywhere

一次编译,到处运行。

多端转换原理

Taro 是基于 React 语法来进行开发的,把一份类React源码,通过“编译”转换成兼容目标端的形式。

也就是对代码文件进行一系列转换操作,最终获得可以在小程序运行的代码。而 React 最开始就是为了解决 Web 开发而生的,所以对代码稍加改动,也可以直接生成在 Web 端运行的代码,而同属 React 语法体系下的 React Native,也能够很便捷地提供支持。同理其他平台,如快应用、百度小程序等,将源码进行编译转换操作,也能获得该平台下的对应语法代码。

img
抹平多端差异

通过上面可以将 Taro 源码编译成不同端上可以运行的代码了,但是不同的平台都有自己的特性,每一个平台都不尽相同,这些差异主要体现在不同的组件标准不同的 API 标准以及不同的运行机制上。

以小程序和web进行对比:

image-20211124232425084

小程序和 Web 端上组件标准API 标准有很大差异,这些差异仅仅通过代码编译手段是无法抹平的。

例如不能直接在编译时将小程序的 <view /> 直接编译成 <div />,虽然看上去有些类似,但是他们的组件属性有很大不同的,仅仅依靠代码编译,无法做到一致,同理,众多 API 也面临一样的情况。

**Taro 的解决方案:**采用了定制一套运行时标准来抹平不同平台之间的差异。

这一套标准主要以三个部分组成,包括标准运行时框架标准基础组件库标准端能力 API,其中运行时框架和 API 对应 @taro/taro,组件库对应 @tarojs/components,通过在不同端实现这些标准,从而达到去差异化的目的。

img

起初,Taro团队是想重新制定一套标准规范,发现在所有端都得实现这个标准成本过于高昂。

后来就以微信小程序的组件库和 API 来作为 Taro 的运行时标准,另外百度小程序以及支付宝小程序都是遵循的微信小程序的标准,这样一来,Taro 也实现了在这两个平台上的转换。

img
taro搭配鸿蒙

最近看到他们的动态是在研究让Taro搭配鸿蒙系统,我这边也运行了一下,不过关键插件没发布并不能运行起来,哭~

整体原理:

taro cli ->根据不同的type解析不同的插件 -> 利用webpack编译各端的代码 -> 利用各端自己的插件配置磨平各端的代码 -> 生成harmony代码

image-20211124234919563

image-20211124223903895

其中@tarojs/mini-runner的原理是这样的:

taro代码 ->AST解析 -> 解析Render代码 -> 利用脚本转换 -> 生成代码

image-20211124235349922

编译鸿蒙时,CLI 会调用 @tarojs/mini-runner 包,这个包主要是对代码进行编译。mini-runner 主要做了这些事情:

  1. 负责根据开发者的编译配置调整 Webpack 配置
  2. 注入自定义的 PostCSS 插件。(如 postcss-pxtransform)
  3. 注入自定义的 Webpack 插件。
  4. 注入自定义的 Webpack Loaders。(Loaders 位于 @tarojs/taro-loader 包中)
  5. 调用 Webpack 开启编译。
  6. 修改 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                           入口文件

为了让 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>

开发运行步骤:

  1. 下载taro的源码编译、对鸿蒙的适配进行link。
image-20211125003847309
  1. 另外再创建一个taro项目,编译配置

image-20211125004113404

  1. 配置一下鸿蒙

taro build --type harmony --watch

使用鸿蒙开发者工具的 previewer、真机、远程真机进行预览

image-20211124192502357

打开鸿蒙编辑器内部是这样的:

项目的配置主要在MyApplication/entry/src/main/config.json文件夹里面,有三个部分组成:

属性名称 含义 数据类型 是否可缺省
app 表示应用的全局配置信息。同一个应用的不同HAP包的app配置必须保持一致。 对象
deviceConfig 表示应用在具体设备上的配置信息。 对象
module 表示HAP包的配置信息。该标签下的配置只对当前HAP包生效。 对象
image-20211125004847753

由于关键配置会在本月底开源,目前没办法完整把项目跑起来,所以介绍就到这里啦,有后续感兴趣的可以一起看看呀~在这里插入代码片

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐