# Antmove跨端开发:跨端语法与条件编译

# 背景

Antmove小程序转换器自去年发布以来,GitHub上的star数上千,交流群中有近500个活跃用户,已经帮助众多小程序实现转换迁徙。 虽然Antmove可以对已经实现的小程序功能进行转换,由于不同平台存在一些无法抹平的特性差异,以及针对特定平台可能有不同的产品需求,目前用户比较常见的开发方式是使用Antmove转换源码,然后对转换后的代码进行二次修改。

这种开发方式虽然一定程度上可以实现用户需求,但存在几点问题:

第一,业务上仍然需要维护转换前后的两套代码,维护成本不可控,也与我们一次编写,多处运行的初心相悖,失去了转换的优势;

第二,用户只能使用Antmove转换一次,无法再次使用Antmove工具从源码进行转换,因为会覆盖对转换代码的修改。

总之,这种方式从开发效率和可维护性而言,都不是最理想的方案。为解决这个问题,Antmove增加了跨端语法与条件编译功能,用户可以使用跨端语法在小程序源码中编写特定平台的逻辑,结合Antmove的条件编译功能,只需维护一套源码,即可实现跨端开发。接下来,本文将通过一个案例具体介绍这种方案。

# 跨端语法

Antmove的跨端语法支持在模版、配置、脚本和样式中使用。

# 模板文件

在模版文件中,可以使用 is-xxx 属性(目前支持is-wx、is-alipay、is-swan、is-tt、 is-quick)来区分不同的平台。例如

<text is-wx>这是微信小程序</text> //需要编译到微信小程序的代码
<text is-alipay>这是支付宝小程序</text> //需要编译到支付宝小程序的代码

Antmove会针对不同的平台,保留不同的标记。

# 脚本文件

在脚本文件中,可以使用if条件语句,例如:wx.__target__ === 'alipay',比较运算前的wx代表源代码的平台端(支持wx、alipay),比较运算后的 'alipay' 代表输出平台端(支持'alipay'、'wx'、'tt'、''swan、'quick').在脚本中使用应该是最常见的场景。

//源代码
let type = null;
if (wx.__target__ === 'alipay') {
    type = 'alipay';  //需要编译到支付宝小程序的代码
} else {
    type = 'wx';  //需要编译到微信小程序的代码
}

# 样式文件

在样式文件中,可以在/* xxxStart */和/* xxxEnd */之间写下样式,xxx可替换为wx、alipay、swan、tt、quick. Antmove会将针对不同的平台保留特有的样式。

/*wxStart*/
.red {
  color: red;
}
/*wxEnd*/

/*alipayStart*/
.green {
  color: green;
}
/*alipayEnd*/

# 配置文件

在配置文件中,可以使用"_<输出平台前缀>Env" 作为键(_wxEnv、_alipayEnv、_swanEnv、_quickEnv、_ttEnv),这样就可以针对不同的平台提供不同的配置文件。

// 源代码
"_wxEnv":{
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#0068C4",
    "navigationBarTitleText": "知乎微信小程序",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true
  }
  },
"_alipayEnv": {
  "window": {
    "navigationBarTitleText": "知乎支付宝小程序",
    "navigationBarTextStyle": "white",
    "enablePullDownRefresh": true
  }
}

Antmove会识别以上跨端语法,条件编译到不同的平台。有关跨端语法的更多详情可以查看antmove相关文档 (opens new window)

# 条件编译

Antmove的条件编译使用比较简单,以微信小程序源码为例,在微信小程序源码中使用跨端语法之后,执行 antmove -t wx 命令,可以得到干净的微信源码,执行 antmove 命令,则可以条件编译到其他平台。

# 案例:vant-aliapp (opens new window)

我们使用Antmove对一些常用的微信小程序组件库比如vant/weui等做了转换,并发布了对应的支付宝小程序版本。在最新一次升级过程中,我们就采用了Antmove的跨端语法对 vant 1.6.2 的源码进行修改,条件编译到微信和支付宝平台。

# vant-aliapp演示小程序

具体效果可以使用支付宝扫码查看

image.png

# 在模板中使用跨端语法

一般情况下,你不需要在模板中使用跨端语法,但在某些场景下则比较有用,比如只在微信小程序中存在的公众号组件。

<view>
    <view>微信公众号关注组件</view>
    <view>
      <official-account is-wx></official-account>
    </view>
</view>

# 在脚本中使用跨端语法:以circle组件为例

vant的circle组件使用了canvas,而微信与支付宝小程序的canvas/SelectorQuery等接口能力不尽相同,虽然Antmove已经抹平了大部分的差异,但还是存在类似于SelectorQuery.in (opens new window)这样无法抹平的差异。因此我们在脚本中使用wx.__target__的跨端语法来加以区分。

// ...
methods: {
  //...
  getContext() {
      const {
        type,
        size
      } = this.data;
      if (type === '') {
        let ctx = null
        if (wx.__target__ === 'alipay') {
          ctx = wx.createCanvasContext(this.props.id);
        } else {
          ctx = wx.createCanvasContext('van-circle', this);
        }
        return Promise.resolve(ctx);
      }
      const dpr = wx.getSystemInfoSync().pixelRatio;
      return new Promise((resolve) => {
        if (wx.__target__ === 'alipay') {
          const ctx = wx.createCanvasContext(this.props.id);
          if (!this.inited) {
            this.inited = true;
            this.setData({
              _size: size * dpr
            })
            ctx.scale(dpr, dpr);
          }
          resolve(ctx);
        } else {
          wx.createSelectorQuery()
            .in(this)
            .select('#van-circle')
            .node()
            .exec((res) => {
              const canvas = res[0].node;
              const ctx = canvas.getContext(type);
              if (!this.inited) {
                this.inited = true;
                canvas.width = size * dpr;
                canvas.height = size * dpr;
                ctx.scale(dpr, dpr);
              }
              resolve(adaptor(ctx));
            });
        }

      });
    },
    // ...
}

在上述的代码中,在支付宝平台,我们直接使用canvas上的id创建canvas context,在微信平台,则是在当前自定义组件内查询特定id的canvas context. 我们使用Antmove转换到支付宝之后,只会保留支付宝的代码:

// ...
methods: {
  //...
  getContext() {
      const {
        type,
        size
      } = this.data;
      if (type === '') {
        let ctx = null
        ctx = wx.createCanvasContext(this.props.id);
        return Promise.resolve(ctx);
      }
      const dpr = wx.getSystemInfoSync().pixelRatio;
      return new Promise((resolve) => {
        const ctx = wx.createCanvasContext(this.props.id);
          if (!this.inited) {
            this.inited = true;
            this.setData({
              _size: size * dpr
            })
            ctx.scale(dpr, dpr);
          }
          resolve(ctx);
      });
    },
    // ...
}

同时我们执行 antmove -t wx 命令则会得到干净的微信代码:

// ...
methods: {
  //...
  getContext() {
      const {
        type,
        size
      } = this.data;
      if (type === '') {
        let ctx = null
        ctx = wx.createCanvasContext('van-circle', this);
        return Promise.resolve(ctx);
      }
      const dpr = wx.getSystemInfoSync().pixelRatio;
      return new Promise((resolve) => {
        wx.createSelectorQuery()
            .in(this)
            .select('#van-circle')
            .node()
            .exec((res) => {
              const canvas = res[0].node;
              const ctx = canvas.getContext(type);
              if (!this.inited) {
                this.inited = true;
                canvas.width = size * dpr;
                canvas.height = size * dpr;
                ctx.scale(dpr, dpr);
              }
              resolve(adaptor(ctx));
            });
      });
    },
    // ...
}

在脚本中使用跨端语法尤其适用于针对不同平台实现不同逻辑的场景,以上只是一例。

# 在app.json中使用不同的配置

支付宝中不支持设置导航栏文字颜色,而是根据导航栏背景色自动配置,因此,我们对app.json的内容作了如下修改,实现不同的导航栏配置:

{
  // ...
  "_wxEnv": {
    "window": {
      "navigationBarBackgroundColor": "#f8f8f8",
      "navigationBarTitleText": "Vant Weapp",
      "navigationBarTextStyle": "black",
      "backgroundTextStyle": "dark",
      "backgroundColor": "#f8f8f8"
    }
  },
  "_alipayEnv": {
    "window": {
      "titleBarColor": "#ffffff",
      "navigationBarTitleText": "Vant Aliapp",
      "backgroundColor": "#f8f8f8"
    }
  },
  // ...
}

对于其他各平台有差异的属性,也可以通过这种方式赋予不同的配置。

# 结语

善用跨端语法,可以抹平不同平台的特性差异,也可以针对特定平台实现产品需求。相对于修改转换之后的代码而言,只需维护一套代码,降低维护成本,做到了一次编写,多处运行。

跨端语法和条件编译,让 Antmove 的跨端转换能力更加完善。对于一些历史比较悠久的有跨端需求的微信小程序项目,在使用类似taro/uni-app/rax等跨端框架重构或者重新开发一套特定平台的小程序之外,使用Antmove进行转换是成本更低的一种跨端方案。

最后,Antmove作为维护时间最久的小程序转换项目,可以从微信小程序一键转换到支付宝、头条、快应用等各个端,欢迎大家交流和使用。