最近完成了一个项目,要同时开发微信小程序和支付宝小程序。因为没有自建跨端开发框架,也没有使用Taro、uni-app等框架,并且开发过程中根据实际情况需求在增加,所以都是微信小程序写完一个功能后马上挪到支付宝小程序,这样写的效率比较低,因为挪到支付宝后有些地方的样式、功能需要重写并且测试。暂且不说考察一下选择一个跨端框架赶紧用起来,当前还是使用手动挪功能的方式开发的。本文记录将微信小程序挪到支付宝小程序的过程中遇到的一些支付宝小程序和微信小程序的差异,以免每次都去官方文档查。
1.文件后缀名
微信小程序的四个文件后缀为.js、.json、.wxml、.wxss,支付宝小程序的四个文件后缀为.js、.json、.axml、.acss。
使用命令将当前目录下后缀名为wxml的文件全部替换为axml,后缀名为wxss的文件换为acss:
rename 's/\.wxml/\.axml/' * && rename 's/\.wxss/\.acss/' * 复制代码
使用man rename
查看更多rename用法。参考地址:linux 批量修改文件名后缀名命令rename。执行命令时如果提示zsh: command not found: rename
,就用brew install rename
安装一下rename
,如果没有安装brew,使用brew首页的安装命令安装一下即可。Mac下载brew时报错的处理。
2.控制属性
微信小程序 |
支付宝小程序 |
wx:if |
a:if |
wx:for |
a:for |
wx:key |
a:key |
在axml文件中的a:
对应wxml文件中的wx:
。
3.标签
微信小程序 |
支付宝小程序 |
<i> 、<icon> |
<icon> |
<span> 、<text> |
<text> |
微信小程序即支持<icon>
标签,又支持<i>
,但是支付宝小程序只支持<icon>
标签,写HTML5标签习惯之后比较偏向于写<i>
和<span>
,但是如果是同时要写支付宝小程序和微信小程序,还是直接使用小程序标签<icon>
和<text>
比较好。
4.事件属性
微信小程序 |
支付宝小程序 |
bindtap |
onTap |
catchtap |
catchTap |
微信小程序使用小写的事件属性名称,支付宝小程序使用的是小驼峰的事件属性名称。需要注意的是微信小程序比支付宝小程序健壮许多,一些微信小程序支持的事件支付宝小程序是不支持的。
微信小程序事件,支付宝小程序事件。
5.数据绑定
微信小程序和支付宝小程序都是在axml/wxml中使用{{}}
绑定js中的数据。需要注意的是,支付宝小程序的axml中不支持相对复杂处理。
比如微信小程序中使用如下判断是有效的:
<view class="common-button" wx:if="{{detail.show['a_1'] || detail.show['a_2']}}" bindtap="handleA">A按钮</view> 复制代码
但是在支付宝中直接使用a:if="{{detail.show['a_1'] || detail.show['a_2']}}"
无效,需要将判断简化。先在data中定义一个变量,再在axml中直接使用
<view a:if="{{showButtonA}}">A按钮</view> 复制代码
js中代码如下:
{ data: { showButtonA: false, }, methods: { init () { const { show = {} } = detail; const { a_1, a_2 } = show; this.setData({ showButtonA: a_1 || a_2, }); }, } } 复制代码
微信wxml,支付宝axml。
6.生命周期
这部分直接引用官方文档里的描述文本以及图片。更多生命周期相关内容,查看:微信小程序App、微信小程序页面生命周期、微信小程序注册页面、支付宝小程序页面运行机制。
首先看看官网给出的小程序的生命周期示意图:
图1为微信小程序生命周期,图2为支付宝小程序生命周期:
APP生命周期
微信小程序&支付宝小程序
App({ onLaunch (options) {}, onShow (options) {}, onHide () {}, onError (msg) {}, globalData: '全局数据' }); 复制代码
这里的前台和后台指的是用户是否在使用小程序,当用户在使用小程序的时候,相当于在前台,当用户点击右上角的退出按钮退出小程序时相当于进入后台。
虽然微信小程序和支付宝小程序生命周期回调函数参数的属性略有不同,但常用的几个基本是一样的,比如onLaunch和onShow的options参数的以下属性:
options的属性 |
属性值的类型 |
代表的意义 |
path |
string |
启动小程序的路径 |
scene |
number |
启动小程序的场景值 |
referrerInfo |
Object |
来源信息 |
页面的生命周期
微信小程序和支付宝小程序注册页面时常用的几个生命周期基本相同。
微信小程序&支付宝小程序
Page({ onLoad: function(options) {}, onShow: function() {}, onReady: function() {}, onHide: function() {}, onUnload: function() {}, }); 复制代码
这里的前台和后台指的是否是当前页面。比如从页面A跳转到页面B,那么B页面进入前台(onLoad、onShow、onReady),A页面进入后台(onHide)。如果按A页面的左上角的返回按钮,那么A页面被销毁(onUnload)。
微信小程序页面路由。
组件的生命周期
微信小程序App的生命周期和页面的生命周期基本相同,但是组件的生命周期的声明方式有很大不同。
微信小程序组件
Component({ behaviors: [], properties: { contactInfo: { type: Object, value: null, observer(newVal, oldVal) { if (newVal) { this.doSomthing(); } }, }, }, data: {}, lifetimes: { created: function () {}, attached: function () {}, ready: function () {}, moved: function () { }, detached: function () {}, error: function (err) {}, }, attached: function () { }, ready: function() { }, pageLifetimes: { show: function () { }, hide: function () { }, resize: function () { }, }, methods: { doSomething () { console.log('doSomething'); }, } }) 复制代码
支付宝小程序组件
Component({ mixins:[], data: {}, props:{}, onInit(){}, didMount(){}, didUpdate(prevProps,prevData){}, didUnmount(){}, methods:{ doSomething(){ console.log('doSomething'); }, }, }); 复制代码
微信小程序组件的生命周期、支付宝小程序组件的生命周期。
7.监听父组件传递给子组件的属性值的变化
微信小程序
微信小程序直接使用observer属性,在传入子组件的属性变化的时候会触发observer对应的函数,newVal是属性的最新值。
Component({ properties: { currentStatus: { type: String, value: 'all', observer: function (newVal, oldVal) { this.doSomething(); }, }, }, ... 复制代码
父组件的wxml中使用组件:
<component-a currentStatus="{{currentStatus}}" bind:changeStatus="changeStatus"/> 复制代码
当传入的属性currentStatus变化的时候,就会触发observer对应的函数。
支付宝小程序
根据支付宝小程序文档中的问答,目前还没有像observer那样直接的监听父组件传入子组件的属性值变化的方法。支付宝中可以使用didUpdate代替。
didUpdate 为自定义组件数据更新后的回调,每次组件数据变更的时候都会调用。
要注意的是不论是props改变还是data改变 ,都会触发didUpdate,所以在使用didUpdate根据属性值的变化做一些处理的时候一定要小心,及时返回。
didUpdate (prevProps) { const { currentStatus } = this.props; const prevCurrentStatus = prevProps.currentStatus; if (currentStatus === prevCurrentStatus) return; this.doSomething(); }, 复制代码
8.子组件中的事件改变父组件中的数据
微信小程序
子组件中的wxml部分:
<view wx:for="{{statusOptions}}" wx:key="index" data-value="{{item.value}}" data-index="{{index}}" class="list-item {{currentStatus === item.value ? 'selected' : ''}}" bindtap="changeStatus" >{{item.label}}</view> 复制代码
子组件中的js部分:
methods: { changeStatus(event) { const { value } = event.target.dataset; ... this.triggerEvent('changeStatus', { status: value }); }, }, 复制代码
父组件中的wxml部分:
<component-a currentStatus="{{currentStatus}}" bind:changeStatus="changeStatus"/> 复制代码
父组件中的事件,为了方便,绑定的事件名称和调用的事件名称我使用了一样的,这里的事件名称也可以用别的,比如bind:changeStatus="handleA"
。
父组件中的js部分:
changeStatus(event) { const { status } = event.detail; this.setData({ currentStatus: status, }); ... }, 复制代码
支付宝小程序
子组件中的axml部分:
<view a:for="{{statusOptions}}" a:key="index" data-value="{{item.value}}" class="list-item {{currentStatus === item.value ? 'selected' : ''}}" onTap="changeStatus" >{{item.label}}</view> 复制代码
子组件中的js部分:
changeStatus(event) { const { value } = event.target.dataset; ... this.props.onChangeStatus({ status: value }); }, 复制代码
父组件中的axml部分:
<component-a currentStatus="{{currentStatus}}" onChangeStatus="changeStatus"/> 复制代码
父组件中的js部分:
changeStatus(data) { const { status } = data; this.setData({ currentStatus: status, }); ... }, 复制代码
注意,支付宝父组件绑定的事件中拿到的参数直接是子组件传过来的数据,但是微信小程序中父组件绑定的事件拿到的参数是微信小程序的event对象,要通过event.detail
才能拿到传递的数据。
9.获取组件实例
微信小程序
父组件的wxml部分:
<component-a id="componentA" /> 复制代码
父组件的js部分:
this.componentA = this.selectComponent('#componentA'); 复制代码
支付宝小程序
父组件的axml部分:
<component-a ref="saveComponentA" /> 复制代码
父组件的js部分:
saveComponentA(ref) { this.componentA= ref; }, 复制代码
10.API
微信的API都放在wx下,比如wx.canIUse,支付宝的API都放在my下,比如my.canIUse。以下是这次项目用到的API的差异:
1.请求
微信小程序
wx.request({ url: 'https://www.test.com/test/', data: { a: 'valueA', b: 'valueB', }, header: { 'content-type': 'application/json', }, success: (res) => {}, fail: (err) => {}, complete: () => {}, }) 复制代码
wx.request中的method可以是GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT,里面没有PATCH方法,因为目前项目中没有使用到PATCH请求,所以只简单放一个在微信开放社区中的不支持PATCH请求的解决方案:wx.request()不支持PATCH请求。
支付宝小程序
my.request 目前支持 GET/POST/PUT(其中 PUT 请求在支付宝客户端 10.1.92 或更高版本支持)。
如上所示支付宝小程序支持的请求方式比较少,但是项目中用到了PUT、DELETE请求,无法避免。由后端解决了这个问题。前端传递参数的时候传递一个my.request的参数中不存在的属性,当后端收到这个属性的时候,以这个属性值作为真正的请求方式。
my.request({ url: 'https://www.test.com/test/', method: 'POST', useMethod: 'DELETE', data: { a: 'valueA', b: 'valueB', }, headers:{ 'content-type':'application/json', }, dataType: 'json', success: (res) => {}, fail: (res) => {}, complete: (res) => {}, }); 复制代码
当后端接收到useMethod属性时,会以useMethod的值作为真正的请求方式。
微信小程序request、微信小程序网络、支付宝小程序request。
2.拨打电话
微信小程序:
wx.makePhoneCall({ phoneNumber: '12345', success: (res) => {}, fail: (err) => {}, complete: () => {}, }); 复制代码
支付宝小程序:
my.makePhoneCall({ number: '12345' }); 复制代码
3.复制文本到剪切板
微信小程序
设置系统剪切板的内容:
wx.setClipboardData({ data: '被复制的数据', success: () => {}, fail: () => {}, complete: () => {} }) 复制代码
获取系统剪切板的内容:
wx.getClipboardData({ success ({ data }) { console.log(data); }, }) 复制代码
支付宝小程序
设置系统剪切板的内容:
my.setClipboard({ text: '被复制的内容', success: () => {}, fail: () => {}, complete: () => {}, }); 复制代码
获取系统剪切板的内容:
my.getClipboard({ success: ({ text }) => { console.log(text); }, }); 复制代码
4.交互
项目中经常会用到交互API,这里不一一列举出来了,不记得的时候去下面两个路径中查找。
微信小程序界面交互、支付宝小程序交互反馈。