React项目无入侵转译小程序

导语:存量业务在使用第三方框架的时候,是不是一定得按框架的规则进行大规模重构呢?本分享为大家介绍,兔小巢业务是如何在最小改动的情况下,利用 Taro 能力实现小程序多端同构


一.背景介绍

1.项目背景

兔小巢是反馈互动社区, 大家可以在上面对产品进行反馈,提出自己的意见或者建议,对产品功能进行交流。兔小巢的业务场景覆盖PC端、移动H5、微信小程序。其中移动端H5主要是嵌入用户app中进行交互。在一年多的时间里由于人力有限,小程端功能落后了非常多个版本,为了保证各个渠道功能体验一致,我们决定对兔小巢移动端进行一次同构。因为兔小巢使用的是React框架,所以本次同构主要针对React原生项目进行分析、同构。


2.同构需求

如果你也有以下需求,那我们一起来看看吧:
1.希望同构能尽可能减少对现有代码、构建流程的影响
2.后续开发对小程序的关注尽可能少
3.如果可以希望能有更多小程序端支持

二.同构方案

1.框架选择

由于我们最大问题是人力不足,那么我们在考虑框架的时候尽可能的选择接近的语法,或者改动较小的方案,比如 Taro、Remax、Kbone。
Kbone是一个重运行时的方案,他实现了一个适配器,模拟出了浏览器环境,能让web代码在小程序中运行,使用kbone的话代码只需要增加一套构建流程,以及少量代码改动即可,缺点就是只支持微信小程序构建,以及重运行时方案会在节点非常多时有一定渲染性能问题。
Taro 静态编译框架,将react代码编译成小程序代码。优点是社区繁荣,迭代、维护都比较稳定。
Remax 采用的方法是通过 react-reconciler 实现的一个小程序端的渲染器,理论上来说应该是最全的react语法支持。
我个人是比较倾向于remax的解决思路。但考虑到方案和社区都比较新,并且主要支持是支付宝小程序,遇到的一些微信小程序bug可能没那么快响应。
所以最终选择使用Taro去做同构。

2.同构问题

1.虽然Taro是类 React DSL,但在很DOM、BOM api上都不支持,需要整个项目重构,工量较大

2.现有功能还在迭代,停掉功能迭代 or 重构完再补

3.由于兔小巢的宿主环境是各种第三方APP,重构后测试工作量、上线风险都挺大

能不能像kbone一样小改动,甚至不改动原有代码, 同时使用上Taro的多端编译能力?

3.同构方案分析

我们的诉求变成了希望使用Taro的构建多端能力,但也希望能像kbone一样引入后不作大量修改,同构过程中不影响功能上线。
目前市面上工具都仅支持 由自身DSL 去编译 H5、小程序代码 例如 Taro-h5、Taro-weapp,我们自己去开发这么一套工具也不太现实,因为我们本身的问题也是人力的问题。
考虑到原生react应用 到 taro dsl的路径会比原生react应用直接到小程序的路径短得多。
于是我们开始尝试将Taro作为一个编译工具去使用,而不是作为核心开发框架
需要克服的2个核心问题:
1.DOM/BOM API
2.HTML 标签,原生是div,span,Taro是View、Text
分别对应了逻辑层 和 视图层的不一致,只要解决了这两个问题,我们就可以直接可以对原生react项目使用Taro – build和taro生态工具以及插件。
解决思路是我们通过对代码进行一定规则的映射,在映射规则中解决这两个问题,生成新的代码,让新的代码能够使用上Taro相关的工具和生态。
原有代码和原有编译流程不变的情况下,能把测试工作量和上线风险降至最低


4.解决方案

我们需要去实现这个目的,我整理了一下 总共需要5个步骤。
React项目无入侵转译小程序
在对方法展开说明之前先介绍一个会用到的多平台支持插件插MultiPlatformPlugin这个插件主要是提供一个根据编译环境读取对应后缀名文件的能力。
在移动端H5构建是 对于Home/index这个路径 读取的是Home/index.jsx这个文件
在使用Taro进行小程序构建时 对于Home/index这个路径 读取的是Home/index.weapp.jsx这个文件


5.逻辑层

业务逻辑继承
我们以一个简单的首页Feed流页面逻辑作为讲解基础
1.渲染Loading组件
2.请求接口获取Feed流数据
3.移除Loading组件,渲染列表
// Home.jsximport React, { Component } from 'react';export default class Home extends Component {  componentDidMount() {    this.loadData()  }
loadData(){ // ... }
render(){ // ... }}

小程序根据刚介绍的MultiPlatformPlugin插件功能,去创建在同目录想的同名不同后缀文件作为小程序入口,这样可以保证小程序有自己的逻辑开发空间,不污染原代码。

小程序入口我们只需要做一个简单的继承,小程序页面就获得了父类的所有业务逻辑
// 小程序Home页面 Home.weapp.jsximport React from 'react';import HomeBase from './Home.jsx';
class Home extends HomeBase {}


业务逻辑重写

小程序端如果和H5端存在差异业务逻辑的地方需要重写处理
通常有以下三种情况

1.方法内部实现逻辑不一致

解决方法: 重写同名方法。重写时注意参数和返回值需要保持一致
// 小程序Home页面 Home.weapp.jsx// loadData方法不一致时import React from 'react';import HomeBase from './Home.jsx';
class Home extends HomeBase { loadData(){ // 小程序独有的loadData方法 // ...doSomething }}
2.移动端冗余逻辑
解决方法: 和上面类似,重写时置空就好了
// 小程序Home页面 Home.weapp.jsx// loadData方法小程序不需要时import React from 'react';import HomeBase from './Home.jsx';
class Home extends HomeBase { loadData(){}}

3.小程序需要定制业务逻辑

解决方案,入口处或对应生命周期增加方法引入,在小程序页定制

// 小程序Home页面 Home.weapp.jsx// 小程序需要定制业务逻辑import React from 'react';import HomeBase from './Home.jsx';
class Home extends HomeBase { componentDidMount() { super.componentDidMount && super.componentDidMount(); this.foo(); }
foo() { // ... }}

api处理
为了保证对原代码影响尽可能小,原代码仅做替换引用
生命一个Txc/index.js文件
去存放映射逻辑
// Txc/index.jsconst Txc = {  ...window,  ...document}
export default Txc

在相同位置对小程序端进行单独处理

// Txc/index.weapp.jsimport Taro from '@tarojs/taro';const Txc = {  localStorage: {    getItem(key){      return Taro.getStorageSync(key)    },    // ...  },location: {  // ...  set href(url) {    Taro.navigateTo({ url });  },  // ...},// ...,};

这个过程有点像monkey patch ,有一些地方需要单独封装像 location.href。有些地方需要hack处理,比如屏幕尺寸获取小程序需要调用特有api,还有设备信息获取 等等,基本就是一些苦力活, 这里我是以页面为粒度去做patch,把这个页面用到的dom、bom api封装完了就提测一个页面然后上线,因此对于移动端来说基本是无影响的。

使用方式:

1.引入工具

2.替换window、document的引用
// Home.jsx// 兼容dom、bom apiimport Txc from 'components/txc'// ...  foo(){    // window.localStorage.getItem('foo')    Txc.localStorage.getItem('foo')  }// ...

6.视图层

节点处理
逻辑层的问题处理完了,还需要解决视图层的问题。Taro和小程序一样有自己的视图层标签,无法识别HTML 原生。就如我们开始说的,这次同构相当于实现一个映射规则,帮助Taro相关工具识别我们代码。关于节点的映射规则按照HTML元素分类进行拆分为 块元素、行内元素,以及一些功能性的元素
1.块元素
2.行内元素
3.特殊功能元素

React项目无入侵转译小程序

使用方法:
考虑到将改动降低到最小,希望在使用时只做2件事
1.引入工具
2.批量替换标签
// Home.jsximport React, { Component } from 'react';import Txc from 'components/txc'
export default class Home extends Component { render(){ return ( <Txc.div >home</Txc.div> ) }}

节点映射实现
还是分移动端和小程序端去实现,在移动端我们依然是只改引入,不改具体逻辑,讲移动端测试工作量、上线分享降低到最小
// Txc/index.js// 存放块元素const blockElements = ['div', 'p','h1','section']// 存放行内元素const inlineElements = ['span','i','em']// 所有元素const allElements = ['button', 'input', 'img', ...blockElements, ...inlineElements]// Txc/index.js
const Txc = { ...window, ...document}
allElements.forEach(type => { Txc[type] = React.forwardRef((props, ref) => { return React.createElement(type, { ...props, ref }) });});
export default Txc

小程序端需要按照上面设计的映射规则做实现, 以下做一些简单的代码演示

// Txc/index.weapp.jsimport React from 'react'import { View, Image, Text } from '@tarojs/components';// 存放块元素const blockElements = ['div', 'p','h1','section']// 存放行内元素const inlineElements = ['span','i','em']// 所有元素const allElements = ['button', 'input', 'img', ...blockElements, ...inlineElements]
const Txc = {...}allElements.forEach(type => { Txc[type] = React.forwardRef((props, ref) => <TxcItem type={type} {...props} ref={ref} />);});
const TxcItem = React.forwardRef(({ type, ...props }, ref) => {const params = { ...props, ref, classNames: `txc-${type}`}if (blockElements.some(item => item === type)) { return <View {...params} />}
if (inlineElements.some(item => item === type)) { return <Text {...params} />}
if ('img' === type) { return <Image {...params} />}...
})export default Txc
样式处理
1.小程序页面单独引入样式
2.元素选择器建议改成类选择
3.添加注释,在小程序编译时忽略部分代码
import React from 'react';import HomeBase from './Home.jsx';// 小程序单独引入样式文件处理import './style.weapp.css'class Home extends HomeBase {}


7.方案回顾

以上完成了我们对逻辑层和视图层的映射,最终的结果是移动端原代码仅仅修改引用不用去改变逻辑就可以对我们现有代码使用Taro相关工具进行小程序的构建,最终获得小程序产物,可以在小程序中运行。

8.未来展望

因为目前该方案只满足了兔小巢项目的使用,对于一些极端情况,和复杂操作其实是不能处理的,希望未来可以一起去完善,把这个功能做成一个云服务,提交H5代码,自动构建出小程序产物,省去中间改动。



React项目无入侵转译小程序


快乐工作,快乐生活
Happy work , Happy life
/
Join us
React项目无入侵转译小程序

本篇文章来源于微信公众号: 腾讯CDC体验设计

UI/UX

「 泛政务设计 」可视化色彩体系的配色方法探索

2021-6-18 12:14:40

UI/UX

Windows 11 越来越像 Mac,看得我不敢升级了

2021-6-20 8:20:00

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索