We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
【多端】指 Web H5 & 小程序生态
我是农有宝。2011年末加入虾米网开始前端之旅,2013年加入阿里巴巴 (花名:宝码) 淘宝前端技术团队,负责中台研发,参与建设飞冰(ICE)项目,早期 iceworks 开发负责人。2018年末离职加入初创团队,创业未果。2020年初加入微拍堂,目前负责多端体系的研发与架构。
目前 Facejs 多端研发体系 http://facejs.org 已正式投入生产使用,微拍堂主站与小程序已逐步迁移上线。
会在适合的时机开源
在2018年末第一次接触微信小程序,抛开平台的一些限制不谈,能很快的创建一个小程序出来,正因为相似的 css、html 等语法,几乎零门槛上手,我也很快的创建了一个自己的小程序并发布。基于职业的本能就会想是否有开发框架能提高小程序的开发效率,先后了解到的有:mpvue uniapp wepy taro 等开发框架。由于业务发展尚未达到规模,就暂时没有深入的了解。
直到后来加入了微拍堂团队,才开始真正的去关注多端的生态,微拍堂的前端业务涵盖了:PC、H5、微信小程序、头条小程序、百度小程序四大平台,不排除未来新的平台出现的可能。也让我真正的开始去深入了解小程序。
在聊多端之前,我想先聊聊在原生微信小程序中如何渲染一个富文本的。举个例子,比较热门的是 https://github.com/icindy/wxParse 其原理是转换为 JSON 数据后再通过模板的能力渲染出来,这就是一个通用的模式。只要有一份 JSON 数据约定,就可以通过平台的模板能力渲染出来。当时基于业务的简单需求,我也创建了一个 https://github.com/noyobo/html-to-nodes 的库,用于渲染 html 富文本的。这些都是某种意义上的数据驱动视图的方式,通过 JSON 节点描述页面结构,与 React 虚拟 DOM 有相似之处。
使用真正的 React 构建跨平台小程序
React 的虚拟 DOM 模式,就能产生一个页面级别 JSON 数据。恰好 Remax 也抓住了这一点,并在渲染性能上优化到了极致。这正是我理想中的多端运行基础,开始着手基于 Remax 思考如何打造适合团队需要的多端研发体系。
详细参阅:Remax 的实现原理 https://remaxjs.org/guide/implementation-notes
团队内部基于 React 生态已经有完善的开发流程,在这个前提下,如何保证原有模式不变,同时支持多端,是一个比较有意思的挑战。
目前体系主要包含:
微拍堂的无线主站是一个 Web App,最大的特点是:单页面渲染机制,多页面实例共存。不同于普通的 SPA、MPA,主站使用起来更像是一个 App 一般,有进出场效果,页面数据缓存,页面堆栈管理等,使用起来就如同一个“小程序”般。
保障业务平滑升级,首先得了解现有的页面加载流程,梳理的加载流程大致如下:
每个页面构建后的产物是一个 bundle.js,不管是历史页面,还是新的多端页面,都是一个 bundle.js。
为了抹平小程序生命周期的平台差异,Web 页面中模拟了 onShow onLoad onHide 所以在【渲染实例】这一步需要对不同的路由使用不同的渲染器,以响应新页面内部的生命周期。
onShow
onLoad
onHide
主要判断是新的路由地址,就使用 Racejs Renderer 进行渲染,此处引进一个路由声明文件:
// face.config.js export default { routes: ['/detail/:uri'] }
在主应用【路由解析】阶段进行标记,迁移一个页面就增加一条新的记录,实现渐进平滑迁移。
只有一套代码,那么代码中也就只存在一个路由调用,例如
import { Link } from '@facejs/router'; <Link to="/detail/10000">商品标题</Link>
这样的地址需要在小程序中跳转到对应的页面,这里引入一个 route.alias.js 配置文件。
route.alias.js
export default { '/detail/:uri': function (params) { return `/pages/detail/index?uri=${params.uri}`; }, }
Link 组件内调用的 router.to 方法。
Link
router 提供一个绑定 alias 的接口,只在小程序的运行环境下执行,绑定后 router 就具备路由映射的能力,可实现一套路由标准全平台跳转:
// app.js 小程序的启动文件 import router from '@facejs/router'; // 单例模式 import alias from './face.alias.js'; router.init({ alias }); router.to(url, options);
各平台之间的差异是客观存在的,无法真正意义的一套代码在各端执行,但可以通过底层的渲染基础,能让上层应用、业务逻辑只写一份即可。实现面向业务开发体验上的「 Write once run anywhere」。
基本架构如下:
基础 UI 与 API 通过文件同构的方式,实现各平台的 UI 表现,依靠 webpack extensions 的能力,基础 UI 组件的文件会存在多个文件:
├── View.toutiao.tsx ├── View.tsx ├── View.wechat.tsx ├── index.less ├── index.ts └── props.ts # 统一组件 props
在构建时通过构建参数,会有不同的 webpack config 配置:
// 微信小程序 module.exports = { resolve: { extensions: ['.wechat.js', '.js'] } }; // 头条小程序 module.exports = { resolve: { extensions: ['.toutiao.js', '.js'] } };
从而屏蔽掉业务中编写的差异,这部分的能力已经包含是 @remax/build 中。
为什么要做 DOM / Event 的抹平?在浏览器中与小程序的事件响应是两种不同的内容。
例如 Input 的输入回调,web 通过 event.target.value、小程序通过 event.detail.value 获取。此类需求通过定义统一的 Interface 类型返回即可:
event.target.value
event.detail.value
interface Event { value?: string; VNode?: Record<string, any>; }
一些动画计算难免使用到后去 DOM 元素的位置 getBoundingClientRect,则通过统一接收 ref 回调作为参数,内部去实现抹平。
getBoundingClientRect
为什么选择使用 ref 作为 DOM 接口的入参呢?
xx.createSelectorQuery
由于小程序双进程渲染机制,此类 API 都设计为 Promise 的形式。
无线开发适配方案已经非常成熟了,这里没什么特别的,选择一个适合团队的就可以。目前最佳的实践是 web 模式下将 html.fontSize 设置为 100px (可根据分辨率动态变更)。
统一样式单位 rem 将开发习惯保持与 web 一致。
rem
${n * 100}rpx
1rem
100rpx
需要额外处理的是内敛样式,需要将 style={{width: 500}} => style={{width: '5rem'}},针对内敛样式可通过底层基础 UI 组件来完成,所有基于基础组件的封装都会具备内敛转换的能力。
style={{width: 500}}
style={{width: '5rem'}}
px 单位则不转换,保留绝对单位能力。
px
通过基础差异抹平,搭配扩展封装,Api 实现,在业务使用上已经不存在差异,可扫码体验:
web 演示: https://face-js.github.io/web/extension.html
扫码查看 Facejs 组件示例
业务框架 + 组件生态已准备就绪,如何将多端模式与现有业务结合,是一个真正决定这套方案能否落地的事情,业务的马车已经开动起来了,既要保证业务稳定运行的同时,又要进行逐步的迁移,将马车打造成成一辆超级跑车?
这里着重聊下应用的混合方案,针对这个问题我引入一个【多端微应用】的概念。
每个页面(entry)构建产物分两种,1. 小程序的页面文件;2. 浏览器的 bundle.js & 路由关系表。
小程序通过混合操作将构建产物与原声小程序进行合并,得到一个可运行的混合小程序。混合操作没 remax 文档中的那么简单,针对原生小程序,或多或少存在一些过去的逻辑,还需要将 app.js 文件进行合并,app.config.js 文件合并等操作,remax 也提供了对应的解决方案途径,具体可查看使用插件 章节。通过混合方式逐个迁移小程序的页面。
这块进一步扩展我编写了一套混合操作命令,执行结果示例如下:
info [hybrid] 开始合并混合项目 info [hybrid] 多端输入目录 /Users/noyobo/home/wpt/wpt-miniapp/face-dist/wechat info [hybrid] 原生输入目录 /Users/noyobo/home/wpt/wpt-miniapp/pre_dist info [hybrid] 混合输出目录 /Users/noyobo/home/wpt/wpt-miniapp/dist info [hybrid] 正在清除目录 /Users/noyobo/home/wpt/wpt-miniapp/dist WARN [plugin-hybrid] 使用原生配置 app.json WARN [plugin-hybrid] 使用原生配置 project.config.json WARN [plugin-hybrid] 使用原生配置 sitemap.json WARN [plugin-hybrid] 多端应用覆盖文件,请回归测试!!! - pages/bbs/index/index.js - pages/bbs/index/index.json - pages/bbs/index/index.wxml - pages/bbs/index/index.wxss info [hybrid] 混合项目合并完成! [22:04:29] Finished '<anonymous>' after 1.63 s
将多端构建结果与原生应用合进行合并。
web 端就没那么复杂了,可以理解为一个微前端体系,目前比较热门的微前端方案有:
它们都是可以支持跨框架运行的,而我当下的场景就更简单了。由于是统一技术栈的,不需要关心跨框架的问题,只要明确路由与 bundle.js 的关联关系,加载时获取对应的 bundle.js 执行即可。主应用负责加载,以及页面堆栈缓存的操作,将更多的精力放到了优化框架体验上。
主应用这个可以简单讲讲,我称之为 MSPA (MultiInstance SinglePage Web Application) 多实例单页面应用。
https://w.weipaitang.com 可点击体验
看起来是一个单页面应用,它内部还管理着多个页面的实例,可实现 bundle.js 预加载,转场动画,数据预加载等,有效的提高页面的交互体验。
关于 MSPA 可以在后续再进行详细的介绍。
Write once run anywhere 是一个追求的方向,期间会遇到种种让你抓狂的问题,或由于平台特定不得不做出妥协、降级、并存的解决方案,都在不断的朝着我们的目标前行;也不仅仅局限于 Web & 小程序。
这篇文章粗略的聊聊大致的实现过程和落地到实际业务中去,由于篇幅关系不能深入详细的讲解。 也只是 Facejs 多端研发体系中的一小部分,项目创建、开发调试、性能优化、组件生态、自动化测试、发布部署、性能监控等等,都在稳步进行中,微拍堂前端团队也在寻找有一同志向的人员加入,如果你也感兴趣欢迎与我联系。
感谢
The text was updated successfully, but these errors were encountered:
No branches or pull requests
个人介绍
我是农有宝。2011年末加入虾米网开始前端之旅,2013年加入阿里巴巴 (花名:宝码) 淘宝前端技术团队,负责中台研发,参与建设飞冰(ICE)项目,早期 iceworks 开发负责人。2018年末离职加入初创团队,创业未果。2020年初加入微拍堂,目前负责多端体系的研发与架构。
成果
目前 Facejs 多端研发体系 http://facejs.org 已正式投入生产使用,微拍堂主站与小程序已逐步迁移上线。
![截屏2020-09-23 上午12 02 01](https://user-images.githubusercontent.com/1292082/93965499-d93f1a80-fd94-11ea-8742-243c41c9c341.png)
前言
在2018年末第一次接触微信小程序,抛开平台的一些限制不谈,能很快的创建一个小程序出来,正因为相似的 css、html 等语法,几乎零门槛上手,我也很快的创建了一个自己的小程序并发布。基于职业的本能就会想是否有开发框架能提高小程序的开发效率,先后了解到的有:mpvue uniapp wepy taro 等开发框架。由于业务发展尚未达到规模,就暂时没有深入的了解。
直到后来加入了微拍堂团队,才开始真正的去关注多端的生态,微拍堂的前端业务涵盖了:PC、H5、微信小程序、头条小程序、百度小程序四大平台,不排除未来新的平台出现的可能。也让我真正的开始去深入了解小程序。
启蒙
在聊多端之前,我想先聊聊在原生微信小程序中如何渲染一个富文本的。举个例子,比较热门的是 https://github.com/icindy/wxParse 其原理是转换为 JSON 数据后再通过模板的能力渲染出来,这就是一个通用的模式。只要有一份 JSON 数据约定,就可以通过平台的模板能力渲染出来。当时基于业务的简单需求,我也创建了一个 https://github.com/noyobo/html-to-nodes 的库,用于渲染 html 富文本的。这些都是某种意义上的数据驱动视图的方式,通过 JSON 节点描述页面结构,与 React 虚拟 DOM 有相似之处。
起点 - Remax
React 的虚拟 DOM 模式,就能产生一个页面级别 JSON 数据。恰好 Remax 也抓住了这一点,并在渲染性能上优化到了极致。这正是我理想中的多端运行基础,开始着手基于 Remax 思考如何打造适合团队需要的多端研发体系。
体系的目标
团队内部基于 React 生态已经有完善的开发流程,在这个前提下,如何保证原有模式不变,同时支持多端,是一个比较有意思的挑战。
目前体系主要包含:
1. 业务框架 - 平滑迁移
微拍堂的无线主站是一个 Web App,最大的特点是:单页面渲染机制,多页面实例共存。不同于普通的 SPA、MPA,主站使用起来更像是一个 App 一般,有进出场效果,页面数据缓存,页面堆栈管理等,使用起来就如同一个“小程序”般。
渲染模式共存
保障业务平滑升级,首先得了解现有的页面加载流程,梳理的加载流程大致如下:
每个页面构建后的产物是一个 bundle.js,不管是历史页面,还是新的多端页面,都是一个 bundle.js。
为了抹平小程序生命周期的平台差异,Web 页面中模拟了
onShow
onLoad
onHide
所以在【渲染实例】这一步需要对不同的路由使用不同的渲染器,以响应新页面内部的生命周期。主要判断是新的路由地址,就使用 Racejs Renderer 进行渲染,此处引进一个路由声明文件:
在主应用【路由解析】阶段进行标记,迁移一个页面就增加一条新的记录,实现渐进平滑迁移。
路由映射
只有一套代码,那么代码中也就只存在一个路由调用,例如
这样的地址需要在小程序中跳转到对应的页面,这里引入一个
route.alias.js
配置文件。Link
组件内调用的 router.to 方法。router 提供一个绑定 alias 的接口,只在小程序的运行环境下执行,绑定后 router 就具备路由映射的能力,可实现一套路由标准全平台跳转:
2. 跨平台组件 & API
各平台之间的差异是客观存在的,无法真正意义的一套代码在各端执行,但可以通过底层的渲染基础,能让上层应用、业务逻辑只写一份即可。实现面向业务开发体验上的「 Write once run anywhere」。
文件同构基础
基本架构如下:
![](https://camo.githubusercontent.com/f1a3d29551aeea980726ef901252792b49cae9311e2b79f31a8b4eba4fa70506/68747470733a2f2f70362d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f32623863383534363537373034383935383337636538643462613264323264657e74706c762d6b3375316662706663702d7a6f6f6d2d312e696d616765)
基础 UI 与 API 通过文件同构的方式,实现各平台的 UI 表现,依靠 webpack extensions 的能力,基础 UI 组件的文件会存在多个文件:
在构建时通过构建参数,会有不同的 webpack config 配置:
从而屏蔽掉业务中编写的差异,这部分的能力已经包含是 @remax/build 中。
DOM / Event 抹平
为什么要做 DOM / Event 的抹平?在浏览器中与小程序的事件响应是两种不同的内容。
例如 Input 的输入回调,web 通过
event.target.value
、小程序通过event.detail.value
获取。此类需求通过定义统一的 Interface 类型返回即可:一些动画计算难免使用到后去 DOM 元素的位置
getBoundingClientRect
,则通过统一接收 ref 回调作为参数,内部去实现抹平。为什么选择使用 ref 作为 DOM 接口的入参呢?
xx.createSelectorQuery
由于小程序双进程渲染机制,此类 API 都设计为 Promise 的形式。
统一平台样式
无线开发适配方案已经非常成熟了,这里没什么特别的,选择一个适合团队的就可以。目前最佳的实践是 web 模式下将 html.fontSize 设置为 100px (可根据分辨率动态变更)。
统一样式单位
rem
将开发习惯保持与 web 一致。rem
转换为${n * 100}rpx
。1rem
=>100rpx
需要额外处理的是内敛样式,需要将
style={{width: 500}}
=>style={{width: '5rem'}}
,针对内敛样式可通过底层基础 UI 组件来完成,所有基于基础组件的封装都会具备内敛转换的能力。px
单位则不转换,保留绝对单位能力。小结
通过基础差异抹平,搭配扩展封装,Api 实现,在业务使用上已经不存在差异,可扫码体验:
3. 工程化
业务框架 + 组件生态已准备就绪,如何将多端模式与现有业务结合,是一个真正决定这套方案能否落地的事情,业务的马车已经开动起来了,既要保证业务稳定运行的同时,又要进行逐步的迁移,将马车打造成成一辆超级跑车?
这里着重聊下应用的混合方案,针对这个问题我引入一个【多端微应用】的概念。
每个页面(entry)构建产物分两种,1. 小程序的页面文件;2. 浏览器的 bundle.js & 路由关系表。
小程序通过混合操作将构建产物与原声小程序进行合并,得到一个可运行的混合小程序。混合操作没 remax 文档中的那么简单,针对原生小程序,或多或少存在一些过去的逻辑,还需要将 app.js 文件进行合并,app.config.js 文件合并等操作,remax 也提供了对应的解决方案途径,具体可查看使用插件 章节。通过混合方式逐个迁移小程序的页面。
这块进一步扩展我编写了一套混合操作命令,执行结果示例如下:
info [hybrid] 开始合并混合项目 info [hybrid] 多端输入目录 /Users/noyobo/home/wpt/wpt-miniapp/face-dist/wechat info [hybrid] 原生输入目录 /Users/noyobo/home/wpt/wpt-miniapp/pre_dist info [hybrid] 混合输出目录 /Users/noyobo/home/wpt/wpt-miniapp/dist info [hybrid] 正在清除目录 /Users/noyobo/home/wpt/wpt-miniapp/dist WARN [plugin-hybrid] 使用原生配置 app.json WARN [plugin-hybrid] 使用原生配置 project.config.json WARN [plugin-hybrid] 使用原生配置 sitemap.json WARN [plugin-hybrid] 多端应用覆盖文件,请回归测试!!! - pages/bbs/index/index.js - pages/bbs/index/index.json - pages/bbs/index/index.wxml - pages/bbs/index/index.wxss info [hybrid] 混合项目合并完成! [22:04:29] Finished '<anonymous>' after 1.63 s
将多端构建结果与原生应用合进行合并。
web 端就没那么复杂了,可以理解为一个微前端体系,目前比较热门的微前端方案有:
它们都是可以支持跨框架运行的,而我当下的场景就更简单了。由于是统一技术栈的,不需要关心跨框架的问题,只要明确路由与 bundle.js 的关联关系,加载时获取对应的 bundle.js 执行即可。主应用负责加载,以及页面堆栈缓存的操作,将更多的精力放到了优化框架体验上。
主应用这个可以简单讲讲,我称之为 MSPA (MultiInstance SinglePage Web Application) 多实例单页面应用。
看起来是一个单页面应用,它内部还管理着多个页面的实例,可实现 bundle.js 预加载,转场动画,数据预加载等,有效的提高页面的交互体验。
关于 MSPA 可以在后续再进行详细的介绍。
未来
Write once run anywhere 是一个追求的方向,期间会遇到种种让你抓狂的问题,或由于平台特定不得不做出妥协、降级、并存的解决方案,都在不断的朝着我们的目标前行;也不仅仅局限于 Web & 小程序。
这篇文章粗略的聊聊大致的实现过程和落地到实际业务中去,由于篇幅关系不能深入详细的讲解。 也只是 Facejs 多端研发体系中的一小部分,项目创建、开发调试、性能优化、组件生态、自动化测试、发布部署、性能监控等等,都在稳步进行中,微拍堂前端团队也在寻找有一同志向的人员加入,如果你也感兴趣欢迎与我联系。
感谢
The text was updated successfully, but these errors were encountered: