京东零售技术

开启一码五端,NutUI 组件库助力黄流跨端实现

图片

在最近结束的“京东订单鸿蒙-回跨iOS/安卓”的项目中,NutUI 作为京东移动端的开源组件库,再次承担起一环重要的工作,为布局搭建、设计交互、无障碍、多语言、多模式实现等,提供了完备的基础能力,并出色的完成了三端的工作,基本实现了基于 Taro 框架的一码五端能力,开启了业内组件库跨端能力的新篇章。

01

基于业务与基建能力的重组

1、背景
在2024年到2025年,NutUI 基于鸿蒙和 iOS/安卓做了基本的适配工作,不过整体项目是实验中状态,最早在2024年发布了 DongDesign UI 的一码多端版本,并开放了鸿蒙支持的能力,整合了 Taro Components 及 NutUI 组件库,期望给开发者提供在 Taro 跨端中引用一套组件的便捷方式。
这是一个好的开端,让 Taro Components 和 NutUI 作为整合整体出现,这为在黄流跨端实现中提供了更直接的技术支持,并及时响应黄流业务侧提出的核心诉求——希望可以通过引用一个组件库,让整个项目在使用组件库的能力上更统一、规范、无痛。
很快,这件事达成了一致,并推动了第一版 黄流业务的基础组件库 落地——@hlfe/ui 的第一个版本的出现。@hlfe/ui 主要是为了赋能黄流业务侧,覆盖黄流订单、商详、购物车、结算,在优先考虑适配鸿蒙、iOS和安卓的前提下,初步整合了 Taro Components 公共组件及 NutUI 的 30+ 组件。组件的数量及能力,会继续随着业务侧的需求及基建适配能力陆续开放。
Image
2、组件库间的关系
考虑到这一年多的时间内,突然出现了许多组件库的名字,这里首先对它们的关系做个厘清。
Image
首先,我们可以简单粗暴的把它们的关系理解为上图的包含关系。Taro 作为框架,jdtaro/ui 是 Taro 提供的对外的基础组件,包含容器类、Form表单等基础能力,可看做是原来的 @tarojs/components 在 jd 域内的别名(目前这两个都可以引用,可关注 Taro 官网)。
第二,NutUI 在跨端能力是基于 Taro 框架来实现的, @jdtaro/ui (@tarojs/components) 是整个 NutUI 组件库的实现跨端的基座。
第三,@hlfe/ui 是面向黄流的开发者,包含前端、原生同学,为了让大家用起来省事、统一,将 NutUI 和 Taro 基础组件整合在一起,为大家提供了一个统一的引用入口。
未来,在其他业务场景里,如果实现一码五端,大家可以使用 hlfe/ui 来统一处理;也可以继续保留原引用逻辑。

02

hlfe/ui 的实现

hlfe/ui 的核心诉求是希望在项目中引用组件库的方式统一、集中,不用引多个 UI 组件库。所以,在实现中,也是以核心诉求为准,将项目中涉及的组件库收口处理。
1、双层组件架构
基于 Taro 的跨端项目中,项目依赖的组件主要来源于 Taro 基础组件库 jdtaro/ui 及基于主站实现的高阶组件库 NutUI,需要整合这两个组件库。在整合中,我们主要关注以下能力,以更好的服务业务。
基础组件与通用组件的整合
容器组件与通用组件的割裂,是导入两个组件库的根本原因。所以,在组件结构上,直接将两者可用组件导出。
Image
性能优先
在跨端的交付中,希望在满足交互复杂性的同时,会关注性能最优导出的策略。比如 Image ,我们优先会导出 jdtaro/ui 原组件,确保在各个端上都有实现此类组件的前提下,保障此类组件的性能最优;也会保留 NutUI 组件,并以 NutImage 导,以在基于端的性能优化下,保障组件功能的丰富性。
更多共用能力
1)类型定义:在跨端项目中,基本都使用了 TS,为了更兼容此类支持,在 hlfe/ui 中导出了组件定义相关的类型。
2)公共方法,在组件库中沉淀的一些高效公共方法,也一并为开发者提供,便于大家快速交付。
3)主题定义,Design Token 在组件库中的应用,方便大家可以快速的切换主题,包括正在支持的 京东到家、京犀 等主题;并直接为开发者提供相应的暗黑定义。
2、智能导出组件
通过构建脚本,完成了对组件最新能力的拉取、合并及导出。
// 导出 jdtaro/uiindexContent.push(    `import { ${com.name} as T${com.name} } from '@jdtaro/ui'     const ${com.name} = T${com.name}     export {${com.name}}    `)// 导出 NutUI 公共组件indexContent.push(`export { default as  ${com.name} } from '@nutui/nutui-react-taro/dist/es/packages/${com.name.toLowerCase()}'`)// 导出公共方法等indexContent.push(`export * from '@nutui/nutui-react-taro/dist/es/utils'`)indexContent.push(`export * from '@nutui/nutui-react-taro/dist/es/hooks'`)

03

hlfe/ui 的使用

创建项目
使用 jd-init搭建项目后,可选用 NutUI-React 模板,在完成项目后,进入项目配置。
taro jd-init my-taro-project
这时,需要关注几点:
确认包引入
1、在 package.json 中,确保已包含以下内容:
"@hlfe/ui": "0.0.3-cpp.ios.18-beta.10","@jdtaro/ui": "4.0.3-beta.34","@nutui/icons-react-taro": "3.0.2-cpp.3.beta.4","@nutui/nutui-react-taro": "3.0.19-cpp.23",
确认配置项
2、回跨到 iOS/安卓 项目时,需要关注 config/index.ts 中的配置,避免编译时异常:
esnextModules: [    'jdtaro',    '@hlfe/ui',    '@nutui/nutui-react-taro',    '@nutui/icons-react-taro',],
组件使用
3、组件使用
import { Popup } from '@hlfe/ui'return (    <Popup      visible={visible}      title={title}      position={position || `${isBigScreen ? 'right' : 'bottom'}`} // 大屏处理      closeable={parameter?.closeable ?? true}      {...rest} // 透传原组件属性      style={{ height, ...parameter.style, ...bigScreenRect, maxHeight: '100%' }}      closeAriaLabel={intl.formatMessage({ id: 'xxx_id' }) || '关闭'} // 无障碍读取配置      onClose={() => {        closePop()        parameter?.onClose?.(parameter?.outProps)      }}>      {content}    </Popup>)

04

核心能力的优化

hlfe/ui 的核心能力,是落在 Taro 的基础组件与 NutUI 的跨端适配上,本篇主要关注 NutUI 的跨端适配及能力建设。
在鸿蒙和回跨到 iOS/安卓项目中,最大的问题在于曾经在 web 和小程序端表现良好的布局与样式,会以各种奇怪的方式呈现,比如从中间弹出的弹框,会从右下方弹出;比如 Tabs 切换时,TabPane 成了白屏;比如主站不支持 SVG,Checkbox 就无法展示。这样的问题在项目中层出不穷,就需要一个一个的修改,并push上下游建设。
1、五端适配
区别于黄流业务的鸿蒙、iOS、安卓适配,作为基建,需要更多的关注在兼容已有能力的基础上实现新的适配。
Taro 基建为无法支持的属性与API 提供了工具,可以快速的识别到当前项目中,在 iOS 与 安卓上无法识别的能力。在运行以下命令后,需要特别关注 errors 信息,这些会阻塞业务项目的编译。
pnpm run build:dynamic --disable-upgrade --pages=pages/index/index --atom-collection --generate-source
Image
在解决这些无法支持的属性与API时,主要考虑几点:
1)基础适配,如将原来的 div、span 等 web 端标签改为 Taro 标签。
2)兼容五端,通过判断端来做不同的处理。
/* #ifdef harmony dynamic*/width: 80px;/* #endif *//* #ifndef harmony dynamic*/width: auto;/* #endif */
3)改进实现,规避不支持的属性或API。
// 不建议left: -var(--nut-spacing);align-items: start;// 建议:left: calc(-1 * var(--nut-spacing));align-items: flex-start;
4)废弃 API 移除,如  getAppBaseInfo等。
2、能力扩充
在交付黄流的项目中,会对组件库提出更精确的需求,为此,对部分组件进行了功能扩充。
1)Popup 组件:支持拖拽变更半弹层的高度、支持 top 属性可供展示营销位等;
2)SearchBar 组件:增加 inputProps 属性,暴露更多的 input props 内置属性;
3)Input 组件:增加 style 属性透传至 native Input 组件;
4)Dialog 组件:底部按钮增加角标;
5)CountDown 组件:支持千分位展示;
6)Avatar 组件:支持图片填充模式;
7)NoticeBar 组件:优化宽度获取逻辑。
3、无障碍支持
为了更好的回跨,APP侧也对无障碍输出了统一的规范,基于整体规范,NutUI 也对无障碍进行了完善。
1)统一规范:
Image
2)读取顺序:
ariaLabel→value→ariaRole→hint的顺序组合朗读。
3)当前实现:
在大部分的组件中,View 和 Text 读取是正常的,新增了如有交互行为的组件的无障碍属性,如 CheckBox、Radio等,支持 ariaLabel 的自定义。
4)已知问题及待解决:
在无障碍的实现中,iOS/安卓的表现相对来说较好。鸿蒙端,如弹出弹层时,无法自动获取焦点,需要手动触发可读取内容。类似问题会影响 Dialog、Toast、Popup 等弹层。
4、Icon 库升级
Icon 是组件中不可获取的部分,在实现鸿蒙/iOS/安卓的适配中,采用了 Image src 引入 svg 链接的方式。
import React from "react";import "@tarojs/components";const IconSVG = (props) => {  const realProps = { ...defaultProps, ...props };  return React.createElement(Icon2, { ...realProps, name: realProps.name || "Add", svgSrc: "https://storage.360buyimg.com/imgtools/81adb71e35-159c76f0-a824-11f0-8300-eb1b11b7ce6f.svg" }));};
在实现中,难点在于推动主站 SVG 的基建,比如 无法展示 SVG,SVG 无法变更颜色,SVG 对于导出图片大小有要求。可喜的是,主站基建 SVG 的能力,目前正在向真实的 SVG 落地,对于图片库导出方来说,成本会显著降低。
Image
同时,Icon 库也补齐了无障碍的能力,这样方便了开发者在使用 icon 时,可以做读取屏蔽或读取设置,大大方便了在项目中处理无障碍的设置。
比如在 CheckBox 组件中,对于 Icon 需要禁用可读。设置如下:
<CheckedclassName={color()}ariaHidden />
而在组件维度,可以这样设置,保障组件的无障碍的支持:
<View      className={classNames(        classPrefix,        className      )}      {...rest}      onClick={handleClick}      ariaRole="checkbox"      ariaLabel={ariaLabel}      ariaChecked={innerIndeterminate ? 'mixed' : innerChecked}    >      <CheckedclassName={color()}ariaHidden />    </View>
在项目内,可以这样设置:
<Checkbox    className={styles.checkbox}    checked={shipShopData?.venderShipmentVO?.selected}    ariaLabel='读取文案' // 或读取文案    // ariaHidden={true} // 或禁用掉/>

05

回顾与总结

如今,@hlfe/ui 已开放了 30+ 组件,持续支持了 订单鸿蒙、订单iOS/安卓回跨、商详鸿蒙、结算鸿蒙、购物车鸿蒙;当前还在对商详、结算和购物车的回跨交付中,未来会对更多组件进行开放。
回顾这段时间,是与业务侧、Taro侧、基建能力紧密协作的一段历程。对于组件库与业务来说,都是一个快速迭代过程,其中也遇到了一些痛点,为此,我们希望可以在“提交issue-评审-提交PR-发布版本”的共建模式下,加快整个流程的执行落地,以快速响应业务侧的诉求,并给开发者提供更多共建的可能性。
邀请共建:欢迎更多的伙伴们,加入我们,一起共建,在一码五端的世界里遨游~
Github: https://github.com/jdf2e/nutui-react