十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Taro Harmony 平台 Solid 运行时解析:从 Reconciler 原理到 Babel 配置与 React 迁移实战

Taro Harmony 平台 Solid 运行时解析:从 Reconciler 原理到 Babel 配置与 React 迁移实战 Taro Harmony 平台 Solid 运行时解析从 Reconciler 原理到 Babel 配置与 React 迁移实战【免费下载链接】taro开放式跨端跨框架解决方案支持使用 React/Vue 等框架来开发微信/京东/百度/支付宝/字节跳动/ QQ 小程序/H5/React Native 等应用。项目地址: https://gitcode.com/gh_mirrors/tar/taro导读本文以 Taro 开源仓库中 Solid 运行时文档 为核心深入讲解 Taro 鸿蒙Harmony/OpenHarmony平台如何基于solid-js/universal构建小程序版 Solid 渲染器reconciler并围绕「新增 Babel 配置接入 Solid 框架」「Solid 渲染器的内部实现机制」「React 代码迁移到 Solid 的差异清单」三个实战维度展开。读完本文你将掌握在 Taro Harmony 工程中启用 Solid 框架的完整配置方法、理解 reconciler 连接tarojs/runtimeDOM 实例的底层原理并获得一份可直接指导 React 存量代码改造为 Solid 的 API 对照表。一、背景Solid 在 Taro Harmony 平台中的定位Taro 是开放式跨端跨框架解决方案支持使用 React/Vue 等框架开发微信、京东、百度、支付宝、字节跳动、QQ 小程序、H5 与 React Native 等应用。在鸿蒙平台tarojs/plugin-platform-harmony-ets源码位于 packages/taro-platform-harmony中除了主流的 React、Vue3 运行时之外还提供了基于SolidJS的运行时支持。SolidJS 以其「无 Virtual DOM、细粒度响应式」的设计著称其编译期 JSX 转换和运行时信号signal机制与 React 有着本质差异因此在 Taro 中接入 Solid 需要一套独立的运行时桥接层。这套桥接层的核心代码位于 packages/taro-platform-harmony/src/runtime-framework/solid目录结构如下runtime-framework/ ├── react/ # React 运行时app/connect/page/hooks 等 └── solid/ ├── reconciler/ # Solid 渲染器h.ts / render.ts / props.ts / use.ts / index.ts ├── utils/ # 工具函数index.ts / is.ts ├── README.md # 本文对应的原始文档 ├── app.ts # createSolidApp应用入口创建 ├── connect.ts # setReconciler桥接 Taro 运行时 hooks ├── constant.ts # 生命周期常量 ├── hooks.ts # useDidShow/useLoad 等 Taro 生命周期 hooks ├── index.ts # 运行时入口向 Taro 注入 native API └── page.ts # createPageConfig页面生命周期桥接从源码结构可以清晰看出Solid 运行时与 React 运行时保持了一一对应的模块划分app、connect、page、hooks、utils这保证了在 Harmony 平台上不同框架具备一致的使用体验。二、接入配置Babel 插件与 Preset 组合2.1 基础配置文档核心示例在原文档中接入 Solid 需要新增babel-presets-solid插件配置用于转换 SolidJS 的 JSX 语法。完整配置示例如下module.exports { presets: [ [taro, { framework: react, ts: true, }], [babel-preset-solid, { // 指向 SolidJS 的 reconciler moduleName: tarojs/plugin-framework-react/dist/runtime/reconciler, generate: universal, }] ] }其中moduleName将编译产物中 JSX 运行时函数的导入路径改写为tarojs/plugin-framework-react/dist/runtime/reconcilergenerate: universal则指示 babel-preset-solid 生成面向solid-js/universal渲染器的通用代码。2.2 配置与源码实现的一致性佐证需要特别说明的是上述配置中moduleName指向的路径字符串tarojs/plugin-framework-react/dist/runtime/reconciler是原文档中的写法。而从当前仓库源码看Harmony 平台实际使用的包名为tarojs/plugin-framework-solidHarmony 平台 ArkTS 入口 中当framework solid时会将tarojs/plugin-framework-solid/dist/reconciler声明为外部依赖并把其解析目标重定向到runtime-framework/xxx/reconciler即本平台的 Solid reconciler同时把solid-js/universal一并外部化if (that.framework solid) { that.externalDeps.push([ tarojs/plugin-framework-solid/dist/reconciler, /^tarojs\/plugin-framework-solid\/dist\/reconciler$/, path.join(this.runtimeFrameworkLibrary, reconciler) ]) that.externalDeps.push([ solid-js/universal, /^solid-js\/universal$/, ]) }框架别名映射arkTS.ts中将solid映射为目录名solid随后runtimeFrameworkLibrarygetterarkTS.ts会拼出./runtime-framework/solid这正是上述 Solid 运行时源码目录的落点const frameworkAlias { solid: solid, vue3: vue3, } // ... get runtimeFrameworkLibrary() { return path.resolve(__dirname, ./runtime-framework/${this.aliasFramework}) }编译期 loader metaarkTS.ts为 Solid 场景注入了入口创建函数createSolidAppcase solid: compiler.loaderMeta.importFrameworkStatement compiler.loaderMeta.mockAppStatement function App(props) { return null } compiler.loaderMeta.frameworkArgs config compiler.loaderMeta.creator createSolidApp break从上述代码可以推断原文档中的moduleName: tarojs/plugin-framework-react/dist/runtime/reconciler是文档撰写早期Solid 运行时尚未拆分独立包的历史写法在当前仓库中对应的包名已演化为tarojs/plugin-framework-solid位于 packages/taro-framework-solid其 reconciler 源码在 src/reconciler。实践时请以你项目实际安装的 Taro 版本与tarojs/plugin-framework-solid包为准将moduleName指向该包导出的 reconciler 即可。注意配置中framework字段在原文档示例中写的是react这同样属于文档早期示例的遗留写法。接入 Solid 时config/index.js中的framework应配置为solidHarmony 平台在 arkTS.ts 中默认framework || react并通过frameworkAlias将solid解析到runtime-framework/solid目录。同时 Harmony 平台的 package.json 在 devDependencies 中声明了solid-js: ^1.8.16作为本平台 Solid 运行时的依赖版本基线。三、Reconciler 源码剖析小程序版的solid-js/web3.1 渲染器工厂与 DOM 桥接Harmony 平台的 Solid reconciler 位于 packages/taro-platform-harmony/src/runtime-framework/solid/reconciler/render.ts。其核心是基于solid-js/universal的createRendererTaroNode将 Solid 渲染指令映射到tarojs/runtime的 DOM 实现上实现机制与solid-js/web的 DOM 渲染一一对应Solid 渲染器接口Harmony 实现说明createElement(type)document.createElement(type)创建 Taro 元素节点createTextNode(text)document.createTextNode(text)创建文本节点replaceText(textNode, value)textNode.textContent value更新文本内容setProperty(node, name, value, prev)自定义setProperty见 3.3属性、事件、样式设置insertNode(parent, node, anchor)parent.insertBefore(node, anchor)插入节点支持锚点isTextNode(node)字符串或instanceof TaroText判断文本节点removeNode(parent, node)parent.removeChild(node)删除节点getParentNode/getFirstChild/getNextSibling委托 Taro 节点树遍历父子兄弟关系基于该渲染器render.ts 对外导出了一组与solid-js/web保持一致的 APIrender、effect、memo、createComponent、createElement、createTextNode、insertNode、insert、spread、setProp、mergeProps这正是原文档中列出的 API 清单。此外它还补充实现了Fragment、jsx、jsxDEV、jsxs等 JSX 运行时入口供 babel-preset-solid 的universal生成模式调用。值得注意的细节是createComponent的类型分派逻辑render.tsexport const createComponent (type, props?) { if (isString(type)) return h(type, props) // 字符串标签 走 h() 自定义实现 if (isFunction(type)) return renderer.createComponent(type, props) // 函数组件 原渲染器 return renderer.createComponent(Fragment, props) // 其他 Fragment 包裹 }3.2h函数元素创建、ref 与响应式 propsreconciler/h.ts 实现了h(tagName, props, children)其处理流程包括拆分 props通过splitProps(props, [ref, children])将ref与children从普通属性中分离ref 处理在createRenderEffect中执行——若 ref 是函数则调用local.ref(ele)否则直接赋值local.ref ele等价于把ref转换为回调赋值getter 子节点如果children是 getter描述符存在get用 Solid 的solidChildren包装为响应式子节点属性设置setProps将普通属性直接setProp到元素上对于带 getter 的响应式属性如 JSX 中的get value()则放入effect中跟踪值变化时增量更新并在onCleanup时清理子节点插入insertNodes递归处理字符串、数字、布尔值、Date、RegExp、数组、函数视为动态子节点走insert以及TaroNode实例其中字符串与标量会先经createTextNode转为文本节点再插入。这套h实现保证了 Solid 的细粒度响应式模型signal 驱动的动态子节点与动态属性能正确作用于 Taro 的 DOM 节点树。3.3 属性、事件与样式setProperty的实现细节reconciler/props.ts 是属性更新的核心其分支处理逻辑与 taro-framework-solid 通用 reconciler 的 props.ts 一脉相承同时针对鸿蒙做了专门增强className 归一化className自动转换为class内部关键字过滤key、children、ref不参与 DOM 属性设置事件绑定onXxx识别on前缀支持Capture捕获后缀在非 Harmony 平台上会将组件上的click归一化为tapHarmony 平台保持click语义函数值通过dom.addEventListener/removeEventListener管理style 处理字符串样式写入style.cssText对象样式按 key 逐项 diff通过setStyle设置——数字值且属性名不匹配IS_NON_DIMENSIONAL正则时经convertNumber2PX转为 pxHarmony 平台下_前缀键已处理的鸿蒙样式直接赋值__hmStyle特殊处理鸿蒙样式对象直接写入dom._st.hmStyle其中::after/::before伪类通过setPseudo调用dom.set_pseudo_after/set_pseudo_before设置dangerouslySetInnerHTML比较新旧__html后写入dom.innerHTML非函数值value null时removeAttribute否则setAttribute。此外props.ts 还导出了updateProps/updatePropsByPayload/getUpdatePayload用于批量 diff 新旧 propskey/value 成对供页面更新流程复用。3.4 与通用 reconciler 的关系Harmony 平台这套 reconciler 与 packages/taro-framework-solid/src/reconciler 中的通用实现保持 API 兼容通用版在TARO_PLATFORM web时直接复用solid-js/web/dist/web的原生render/createComponent/insert其余平台则同样走solid-js/universal的createRenderer并额外实现了Portal、Dynamic、flushSync、unstable_batchedUpdates等组件/API见 reconciler/index.ts 与 reconciler/use.ts。Harmony 版则在通用版基础上针对鸿蒙的样式、事件与伪类语义做了定制二者互补。四、运行时接线应用创建、页面桥接与生命周期 Hooks4.1createSolidAppSolid 应用入口Harmony 平台的 app.ts 导出createSolidApp(App, config)其职责包括调用setReconciler()注册 Taro 运行时 hooks见 4.3创建PageContextcreateContextstring()用于向页面组件传递页面 id通过render(AppWrapper, root)渲染应用壳AppWrapper用For信号列表渲染所有页面每个页面以taro-page元素className: taro_page包裹内部由PageContext.Provider提供页面 id并通过injectPageInstance注册页面实例返回符合AppInstance契约的对象mount(component, id, cb)/unmount(id, cb)管理页面信号的增删onLaunch/onShow/onHide/onError/onPageNotFound等生命周期通过triggerAppHook派发onLaunch中会把app.taroGlobalData的 getter/setter 同步到全局 app 对象上并触发app.onCreate。4.2createPageConfig页面生命周期桥接page.ts 的createPageConfig(component, pageName?, pageConfig?)生成鸿蒙 Page 构造器所需的配置对象页面唯一标识$taroPath getPath(id, uniqueOptions)并维护instancesMap 用于页面实例存取生命周期映射onLoad挂载页面组件Current.app.mount、onReady/onShow/onHide/onUnload依次触发对应的safeExecute并借助eventCenter通知子组件生命周期上下文管理通过window.trigger(CONTEXT_ACTIONS.INIT / DESTROY / RECOVER / RESTORE, ...)管理页面上下文信息通用生命周期从getMiniLifecycleImpl动态读取LIFECYCLES与SIDE_EFFECT_LIFECYCLES批量注册后者如onShareAppMessage/onShareTimeline仅当组件或配置显式声明时才注册避免影响小程序右上角菜单选项。4.3setReconciler与生命周期 Hooksconnect.ts 的setReconciler()向 Taro 全局 hooks 注册了两个关键行为batchedEventUpdates用 Solid 的batch(cb)包装事件更新保证信号批量刷新mergePageInstance将子组件通过 hooks 注册的生命周期回调合并到页面实例排除Object.create(null)等意外产生的 prev。hooks.ts 则基于createTaroHook工厂内部使用createRenderEffect注册回调、onCleanup清理导出了完整的 Taro 生命周期 hooks页面/AppuseDidShow、useDidHide、useLoad、useReady、useUnload、useError、useLaunch、usePageNotFound、usePageScroll、usePullDownRefresh、usePullIntercept、useReachBottom、useResize小程序专属useAddToFavorites、useOptionMenuClick、useKeyboardHeight、useSaveExitState、useShareAppMessage、useShareTimeline、useTitleClick、useTabItemTap路由useRouter(dynamic)默认返回createMemo(() Current.router)响应式路由对象、useScope。五、React 代码迁移 SolidAPI 对照与不可兼容项清单原文档附录完整保留了社区对 React → Solid 迁移的差异分析本节将其整理为可直接查询的对照表并结合当前仓库源码给出实践注解。5.1 常用 API 映射表附录 2 精华React 写法Solid 写法说明useStatecreateSignal细粒度响应式的核心返回[get, set]useEffectcreateEffect行为上不能完全等价于每次重渲染都执行的 effectuseReducercreateSignal 一个 reducer 函数把 reducer 逻辑封装成 set 函数useRef{ current: value } 一个变量ref{myRef}需改为ref{myRef.current}React.useRefcreateSignal需要响应式场景时的等价替换React.createRefcreateSignal同上React.useLayoutEffect[createRenderEffect, onCleanup]用渲染副作用 清理函数组合模拟React.useMemocreateMemo语义相近的派生计算ReactDOM.unstable_batchedUpdatesbatch批量更新入口React.Componentrenderer.createComponent组件实例化ReactDOM.renderrenderer.render渲染入口h/React.createElementrenderer.createElement元素创建在 Taro 场景下这些renderer.xxxAPI 均可以从 Harmony 平台 reconcilerrender.ts或tarojs/plugin-framework-solid的导出中获得batch可直接从solid-js导入。5.2 样式差异内联样式属性名从camelCasemarginRight改为kebab-casemargin-right。这与 Harmony reconciler 的 style 处理对象逐 key 写入dom.style一致CSS 属性名需符合 Web/CSSOM 规范。5.3 React 专属 API可能无法转换/兼容的清单附录 1原文档附录 1 完整收录了社区 solid-codemod issue 中关于「无法直接转换/兼容的 React API」的讨论结论以下逐条列出标注了各 API 在 Solid 中的处理建议react 命名空间cloneElement大概率可用Element.cloneNode代替但这高度依赖具体用法且只对 DOM 节点有效、对组件无效createFactory无直接对应isValidElement由于 Solid 没有 VDOM该 API 无法实现instanceof Element在部分场景是可行的替代方案createRef可通过库 shim 保留该 API 并返回对象同时把ref属性用法转换为回调赋值例如(el) ref.current elforwardRef可以移除或提供库 shimmemoSolidJS 已有memo但语义不同createMemo面向派生值而非组件记忆化编译期可直接移除 React 的memo包裹因为它没有必要useReducer提供库 shimuseRef可移除并改写VariableDeclaration但这同时要求处理所有.current属性访问useLayoutEffectcreateRenderEffect是否能完全替代尚无定论useImperativeHandle库 shimuseCallback可直接移除Solid 的函数天然稳定useDebugValue对应语句可移除useDeferredValue与createDeferred差异很大需要 Solid 团队Ryan的介入咨询Mutable Sources库 shimuseInsertionEffect社区仍在讨论涉及style相关的工作组方案useSyncExternalStore库 shimFragment库 shim社区已建议将其纳入 SolidJS 核心。react-dom 命名空间findDOMNode无直接对应unmountComponentAtNode不可实现Solid 无 VDOM无从卸载组件树flushSync用途不明社区推测用于冲刷更新unstable_renderSubtreeIntoContainer曾是 React Server Components 内部用法因无 VDOM 同样不可实现unstable_scheduleHydration无对应unstable_flushControlled无对应。实践建议迁移时优先使用 5.1 与 5.2 的映射改造业务代码对于 5.3 中标注库 shim的 API可在项目内建立兼容层逐步替换标注不可实现的 API 必须重构业务逻辑。另外可以留意通用 reconciler 已在 use.ts 中提供了flushSyncbatch(noop)与unstable_batchedUpdatesbatch(fn)的 shim 实现说明 Taro 的 Solid 运行时已在逐步消化部分 React API 兼容问题。六、常见问题与排查建议moduleName指向的包不存在原文档示例中的tarojs/plugin-framework-react/dist/runtime/reconciler属于早期命名当前仓库对应包为tarojs/plugin-framework-solidpackages/taro-framework-solid。请核对项目依赖中实际安装的框架插件版本与包名后修正配置。框架未生效 / 页面空白确认config/index.js中framework: solid否则 Harmony 平台默认走react分支arkTS.tsexternalDeps与 loader meta 都不会按 Solid 逻辑注入。solid-js/universal解析失败Harmony 构建期要求该模块可被外部化处理arkTS.ts请确保项目安装了与tarojs/plugin-framework-solid兼容的solid-js版本Harmony 平台 devDependencies 基线为^1.8.16见 package.json。生命周期 hooks 不触发子组件注册的生命周期依赖mergePageInstance合并机制connect.ts注意不要在组件外Object.create(null)上下文意外注册否则会被跳过合并。事件 click 变 tap 的问题在 Harmony 平台TARO_PLATFORM HARMONY时click不会被改写为tapprops.ts跨端迁移时需注意事件名差异。结语Taro Harmony 平台的 Solid 运行时本质上是以solid-js/universal的createRenderer为底座将 Solid 的细粒度响应式渲染完整映射到tarojs/runtime的 DOM 实现上并通过createSolidApp/createPageConfig/setReconciler三驾马车与鸿蒙的 App/Page 生命周期打通。配合babel-preset-solid的generate: universal编译模式开发者可以获得与solid-js/web一致的使用体验。对于 React 存量代码本文提供的 API 映射表与不可兼容清单可以作为迁移评估与改造的起点Solid 无 VDOM 的模型决定了迁移不是机械替换而是一次对响应式思维的重新校准。【免费下载链接】taro开放式跨端跨框架解决方案支持使用 React/Vue 等框架来开发微信/京东/百度/支付宝/字节跳动/ QQ 小程序/H5/React Native 等应用。项目地址: https://gitcode.com/gh_mirrors/tar/taro创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表