背景
Hippy-react 官方并没有提供同构小程序的方案;
思考:我们技术栈hippy-react,其他业务也有同构小程序的需求,是否可以支持项目一键转小程序,减少重复开发;
目标:同构
项目仓库:https://git.code.oa.com/melodyren/wx_hippy
基础框架选型
这里调研了业界开源框架;最主要的区别是 DSL;大多数遵循React语法或VUE语法;
我们团队是基于Hippy-React开发,语法是react;
关于vue语法相关框架uiapp, mpvue ,wepy不在我们考虑接入范围内;
我们主要对比了二个React语法框架:alita和taro;
image
- Alita业内首个React Native转微信小程序引擎;Hippy React 基本兼容 React Native 语法;
- 组件标签: alita对齐hippy react是rn标签,taro是小程序标签;
- 样式规范: alita对齐hippy react是stylesheet,taro是sass,less;
-
Alita 编译+运行时处理,(是基于组件的 template,动态 “递归” 渲染整棵树), 对JSX语法限制小;
taro 2.0之前版本对JSX有诸多限制,比如暂不支持render()之外定义JSX;但2.0版本也改为了编译+运行时;
这里之前有输出一篇文章:http://km.oa.com/group/38202/articles/show/415870 有兴趣可以查阅;
- 包大小:alita 非常纯碎,只做了转小程序功能;alita包更小,更有优势;
- alita分包,原生组件,第三方组件都支持;
通过对比,alita更契合我们业务,改动最小;对项目倾入性更小;
当然如果比github start数和稳定性,taro更有优势;但基于taro改造工程量有点大哦;
!!!基于alita进行改造,适配hippy-react;那么如何转呢?
整体架构
image
- 整体要有hippy-react 开发体验;
- 组件和API对齐hippy官方API;
- 支持项目接入,优化webpack构建流程;
- 完成基础库同构;(此处是业务侧逻辑同构,每个业务侧不同);
- 支持第三方组件接入,比如trtc-room 是腾讯云官方提供的音视频组件,支持分平台处理;
- 性能优化;
如何做到组件/API对齐呢?
Alita对Hippy组件支持情况
- [x] View
- [x] Text
- [x] Image
- [x] TextInput
- [x] ScrollView
- [ ] ListView
- [ ] Animation
- [ ] AnimationSet
- [ ] ViewPage
- [ ] Navigato
- [ ] RefreshWrappe
- [x] Modal
其中
View
,
Text
,
Image
,
TextInput
,
ScrollView
,
Modal
,Alita均有提供,属性和方法稍加修改基本可以做到支持;
ViewPager
,
ListView
,
RefreshWrapper
,
Navigator
需要额外扩展@areslabs/wx-react-native 去支持;
Animation
,
AnimationSet
是Hippy提供的动画方案。Hippy的动画则是完全由前端传入动画参数,由终端控制每一帧的计算和排版更新,减少了js端与native端的通信次数,因此也大大减少动画的卡顿。而RN动画是前端驱动,状态值由前端计算,并且通过jsbridge传入终端实现动画。API对前端入门友好,并且方便状态管理。由于二端实现差异有点大,目前还未实现同构;
通过修改后对Hippy组件支持情况
- [x] View
- [x] Text
- [x] Image
- [x] TextInput
- [x] ScrollView
- [x] ListView
- [ ] Animation
- [ ] AnimationSet
- [x] ViewPage
- [x] Navigato
- [ ] RefreshWrapper(包装在listview)
- [x] Modal
Alita对Hippy模块支持情况
常用基本已支持;平台相关的组件,均没有实现
- [x] AsyncStorage
- [ ] BackAndroid 微信小程序限制
- [ ] Clipboard
- [x] Dimensions
- [ ] NetworkModule
- [ ] NetInfo
- [x] PixelRatio
- [x] Platform
- [x] Stylesheet
- [x] Timers
修改后对Hippy模块支持情况
- [x] AsyncStorage
- [ ] BackAndroid 微信小程序限制
- [ ] Clipboard
- [x] Dimensions
- [ ] NetworkModule
- [x] NetInfo
- [x] PixelRatio
- [x] Platform
- [x] Stylesheet
- [x] Timers
由于alita提供的组件和hippy-react内置组件并不完全对齐;我们将alita 开源项目拉取下来到我们仓库地址进行维护,并对wx-react-native模块进行修改,拉齐组件和api;完成同构;
(正常情况下:只要hippy-react 组件和api 有对应的小程序组件和api,我们就可以完成重构);
左侧是需要支持组件,右侧是对应小程序组件;
image
这里是如何做到hippy 组件 和 小程序组件对齐的呢?我大概画了一下流程图:
image
小程序的js文件,无法直接在React层运行,需要提供一个上层Viewpager的代理,这个代理将代替小程序Viewpager组件在React层运行;
第一步:一般需要在对应包的package.json文件的wxComponents 字段指定,建立联系;
第二步:继承自RNBaseComponent,提供上下两层交互数据能力,diff算法拿到变更传给小程序setData;事件回调点击事件触发了小程序事件再传给react运行环境;
Alita框架运行原理
官方文档:https://areslabs.github.io/alita/
image
Alita 转小程序整体流程:webpack打包到js文件,js文件经过alita-loader处理收集信息info, 然后经过babel-loader 处理,最后通过webpack的alita-plugins(多个)生成小程序文件(wxml/wxss等)
Alita 的整体架构借鉴了 ReactNative,其上层存在一个为小程序定制的 mini-react,底层是负责实际渲染的小程序原生代码。而中间存在一个两层互相联系的 bridge。
image
mini-react 负责运行所有 React 代码逻辑,包括递归的构建组件树结构,创建组件实例,执行组件对应生命周期,context 计算等等。其最终将生成一份描述小程序视图的数据。这份数据通过 bridge 模块传递到底层小程序。底层小程序实例调用 setData 方法把数据刷给自身,完成渲染。
如何集成到项目工程呢?
项目目录规范 - 接入方便,只须在之前项目目录新增打包配置文件alita.config.js 和小程序入口文件index.wx.js
image
alita.config.js 说明:
const path = require("path");
module.exports = {
name: "HelloWorldExpo",//生成的微信小程序项目名
appid: "wxc88f41e36c417bff",//调试小程序的appid,由微信申请而来
entry: "./projects/HelloWorldExpo/index.js",//定义页面的入口文件
output: "./output/wx-dist/HelloWorldExpo",//输出目录,小程序生成代码输出目录
include: [ //符合alita规范,直接转化的组件,alita使用的是webpack打包的方式,会使用alita-loader去解析
path.resolve("./projects/HelloWorldExpo/"), // 指定项目
//path.resolve("./library/components/CountPane.js"), //可以引入项目外的通用组局
// path.resolve("node_modules", "@areslabs", "hello-rn")//可以引入npm包
],
resolve: {
alias: { //别名
// "@tencent/hippy-react": "@areslabs/wx-react-native"
},
// symlinks: false
},
miniprogramComponents: { // 第三方组件
// "trtc-room": "/weixin/components/trtc-room/trtc-room"
}
};
复制
符合alita规范,直接转化的组件,可在配置文件include 添加路径, alita使用的是webpack打包的方式,会使用alita-loader去解析;
如果是对小程序内置组件或者对小程序自定义组件的使用,都是只会在小程序平台生效,所以需要平台判断,和ReactNative一样,一般有两种方式: 文件内判断和建立平台文件
-
文件内判断 if (Platoform.OS === 'wx') {
return 小程序的view // <-- 直接使用小程序组件
} else {
return Hippy-React的View // <-- 使用Hippy-React组件
}
-
建立平台文件:(建议差异比较大的)
建立平台文件的方式,即建立单独的小程序.wx后缀文件,比如Map.wx.js
入口文件
React 组件会被转化为 wxml/wxss/js/json 4个文件, 这里有一个例外:入口文件。入口文件里面定义了所有的页面,由于小程序的页面必须预先定义在 app.json 文件,json文件是静态的,无法在运行时处理,因此我们必须在转化的时候就识别出
所有的页面
,所以对于入口文件的文件要求是足够的静态,为了减少错误,尽量不要在入口文件处理其它逻辑,仅将入口文件用来定义路由页面。
详情请看官方文档: https://areslabs.github.io/alita/入口文件.html
import React, { PureComponent } from "react";
import { Router, Route } from "@areslabs/router";
import Room from "./pages/Room";
// import Player from "./components/Room/Player";
export default class App extends PureComponent {
render() {
return (
<Router
wxNavigationOptions={{
// navigationBarTitleText: "",
navigationBarBackgroundColor: "#291B54"
// navigationBarTextStyle: "black"
}}
navigationOptions={{
title: "相亲房间"
}}
>
<Route key="Room" component={Room} />
{/* <Route key="Player" component={Player} /> */}
</Router>
);
}
}
复制
项目运行:
类似wepack执行,接入了yargs,支持三种模式,最终都是用alita-core去打包;
# 开发模式
dating run-wx —dev [projectname]
# 打包模式
dating run-wx --build [projectname]
# 分析包模式
dating run-wx —analyzer [projectname]
复制
const argv = yargs
.command(COMMAND.RUN_WX, "运行 wx 环境", function(yargs) {
var _argv = yargs
.reset()
.option("dev", {
alias: "dev",
demand: false,
describe: "是否开发环境",
type: "boolean"
})
.option("build", {
alias: "build",
demand: false,
describe: "是否编译环境",
type: "boolean"
})
.option("analyzer", {
alias: "analyzer",
demand: false,
describe: "分析包",
type: "boolean"
}).argv;
if (_argv._ && _argv._[1]) {
_argv.entry = _argv.e = _argv._[1];
}
process.argv.push("--config");
process.argv.push("./projects/" + _argv.entry + "/alita.config.js");
require("@tencent/alita-core/lib/index");
})
复制
微信小程序的体积是有限制的,“代码体积小于 2M,分包 8M”。借助webpack的BundleAnalyzerPlugin插件,只需要在执行时候添加 --analyzer 参数即可;
image
总结:Alita是基于RN转小程序,hippy-react和RN 的差异主要是在组件和api,站在巨人的肩膀上,我们很容易实现hippy-react转小程序;集成到工程需要看一下源码,做相对应改造;
目前K歌轻缘相亲做了简单尝试,欢迎大家体验(完整流程是下载相亲APP,打开相亲房间,分享相亲房间到微信,可以在微信内观看直播相亲哦)
image