微信小程序兼容性问题

本文我们来谈谈微信小程序系统兼容性的那些坑。

微信小程序兼容性问题

微信小程序发布一周多了,兼容性问题,特别是 Android 平台兼容性问题特别严重。据我观察,好多小程序掉到兼容性的坑里。掉坑里不要紧,更让人捉急的是,从坑里爬上来的时候,手刚抓到坑沿,又被微信官方踩到(紧急修复兼容性的版本没审核通过,被微信打回重审),再次跌落坑底,然后眼睁睁地看着后台用户在破口大骂“什么东西都没有啊~,什么破小程序”。

微信小程序的兼容性问题除了微信本身的 Bug 外,大部分是目标平台对 JavaScript 标准库支持程度不同造成的。

微信本身的 Bug 引起的

微信本身的 Bug 引发的兼容性问题有个现成的例子,就是 wx.request() 返回的状态码 res.statusCode 的值在 iOS 下是 int 型数据,而在 Android 6.0.1 上却是 String 型数据。如果你判断服务器的返回状态码方法不当,可能就踩到坑里了。

wx.request({
    url: 'http://api.example.com',
    success: function (res) {
        if (res.statusCode === 200) {
            // success
        } else {
            // server failure
        }
    }
})

上述代码就踩坑了,正确的做法是使用 == 而不是使用 === 来判断。另外一个更规范的方法是使用 parseInt(res.statusCode) === 200 来实现。

Javascript 标准库兼容性问题

比如 Array.find() 方法在 iOS 10.2/Android 7.0 上完美支持,但在 Android 6.0.1 上却不支持。如果代码里用到了这个接口,就会导致在 Android 6.0.1 上无法正常工作。通过对比发现,这类接口不支持的个数还是比较多的。特别是 Android 平台版本众多,兼容性问题就更严重,可能一不小小心就掉到坑里。

解决方法

微信本身 Bug 只能绕过去,但对 JavaScript 引擎的兼容性,可以有更优雅的解决方法。比如,我们可以打补丁,使用 polyfill 来实现这些不支持的标准库方法。比如,修复 Android 6.0.1 平台不支持 String.startsWith() 的问题,可以使用下面的 polyfill 代码:

if (!String.prototype.startsWith) {
    console.warn('define polyfill for Array.prototype.startsWith');
    String.prototype.startsWith = function (searchString, position) {
      position = position || 0;
      return this.substr(position, searchString.length) === searchString;
  };
}

推而广之,我们可以把平台不支持的标准库方法,使用 polyfill 实现。这就是 minapp-polyfill 这个项目的目的。

使用方法很简单,把 minapp-polyfill 项目里的 polyfill.js 拷贝到小程序源码目录下,在需要打补丁的 JavaScript 源文件头部引入如下代码即可:

import 'path/to/polyfill.js'

目前这个项目只是搭了个骨架,还有很多方法需要实现。PRs is welcome。

各个平台对 JavaScript 标准库支持情况

条件限制,这里统计了四个平台对 JavaScript 标准库的支持情况,分别是 iOS 10.2, Android 6.0.1, Android 7.0, 微信开发者工具,具体数据如下:

Component.apiName iOS 10.2 Android 6.0.1 Android 7.0 devtool
Array.toString YES YES YES YES
Array.values YES N/A YES N/A
Array.toLocaleString YES YES YES YES
Array.concat YES YES YES YES
Array.fill YES N/A YES YES
Array.join YES YES YES YES
Array.pop YES YES YES YES
Array.push YES YES YES YES
Array.reverse YES YES YES YES
Array.shift YES YES YES YES
Array.slice YES YES YES YES
Array.sort YES YES YES YES
Array.splice YES YES YES YES
Array.unshift YES YES YES YES
Array.every YES YES YES YES
Array.forEach YES YES YES YES
Array.some YES YES YES YES
Array.indexOf YES YES YES YES
Array.lastIndexOf YES YES YES YES
Array.filter YES YES YES YES
Array.reduce YES YES YES YES
Array.reduceRight YES YES YES YES
Array.map YES YES YES YES
Array.entries YES N/A YES YES
Array.keys YES N/A YES YES
Array.find YES N/A YES YES
Array.findIndex YES N/A YES YES
Array.includes YES N/A N/A YES
Array.copyWithin YES N/A YES YES
Array.constructor YES YES YES YES
Buffer N/A N/A N/A N/A
DataView.getInt8 YES YES YES YES
DataView.getUint8 YES YES YES YES
DataView.getInt16 YES YES YES YES
DataView.getUint16 YES YES YES YES
DataView.getInt32 YES YES YES YES
DataView.getUint32 YES YES YES YES
DataView.getFloat32 YES YES YES YES
DataView.getFloat64 YES YES YES YES
DataView.setInt8 YES YES YES YES
DataView.setUint8 YES YES YES YES
DataView.setInt16 YES YES YES YES
DataView.setUint16 YES YES YES YES
DataView.setInt32 YES YES YES YES
DataView.setUint32 YES YES YES YES
DataView.setFloat32 YES YES YES YES
DataView.setFloat64 YES YES YES YES
DataView.constructor YES YES YES YES
Date.toString YES YES YES YES
Date.toISOString YES YES YES YES
Date.toDateString YES YES YES YES
Date.toTimeString YES YES YES YES
Date.toLocaleString YES YES YES YES
Date.toLocaleDateString YES YES YES YES
Date.toLocaleTimeString YES YES YES YES
Date.valueOf YES YES YES YES
Date.getTime YES YES YES YES
Date.getFullYear YES YES YES YES
Date.getUTCFullYear YES YES YES YES
Date.getMonth YES YES YES YES
Date.getUTCMonth YES YES YES YES
Date.getDate YES YES YES YES
Date.getUTCDate YES YES YES YES
Date.getDay YES YES YES YES
Date.getUTCDay YES YES YES YES
Date.getHours YES YES YES YES
Date.getUTCHours YES YES YES YES
Date.getMinutes YES YES YES YES
Date.getUTCMinutes YES YES YES YES
Date.getSeconds YES YES YES YES
Date.getUTCSeconds YES YES YES YES
Date.getMilliseconds YES YES YES YES
Date.getUTCMilliseconds YES YES YES YES
Date.getTimezoneOffset YES YES YES YES
Date.setTime YES YES YES YES
Date.setMilliseconds YES YES YES YES
Date.setUTCMilliseconds YES YES YES YES
Date.setSeconds YES YES YES YES
Date.setUTCSeconds YES YES YES YES
Date.setMinutes YES YES YES YES
Date.setUTCMinutes YES YES YES YES
Date.setHours YES YES YES YES
Date.setUTCHours YES YES YES YES
Date.setDate YES YES YES YES
Date.setUTCDate YES YES YES YES
Date.setMonth YES YES YES YES
Date.setUTCMonth YES YES YES YES
Date.setFullYear YES YES YES YES
Date.setUTCFullYear YES YES YES YES
Date.setYear YES YES YES YES
Date.getYear YES YES YES YES
Date.toJSON YES YES YES YES
Date.toUTCString YES YES YES YES
Date.toGMTString YES YES YES YES
Date.constructor YES YES YES YES
Error.toString YES YES YES YES
Error.constructor YES YES YES YES
Float32Array.constructor YES YES YES YES
Float64Array.constructor YES YES YES YES
Function.constructor YES YES YES YES
Int16Array.constructor YES YES YES YES
Int32Array.constructor YES YES YES YES
Int8Array.constructor YES YES YES YES
Map.forEach YES N/A YES YES
Map.clear YES N/A YES YES
Map.delete YES N/A YES YES
Map.get YES N/A YES YES
Map.has YES N/A YES YES
Map.set YES N/A YES YES
Map.keys YES N/A YES YES
Map.values YES N/A YES YES
Map.entries YES N/A YES YES
Map.constructor YES N/A YES YES
Math.abs YES YES YES YES
Math.acos YES YES YES YES
Math.asin YES YES YES YES
Math.atan YES YES YES YES
Math.acosh YES N/A YES YES
Math.asinh YES N/A YES YES
Math.atanh YES N/A YES YES
Math.atan2 YES YES YES YES
Math.cbrt YES N/A YES YES
Math.ceil YES YES YES YES
Math.clz32 YES N/A YES YES
Math.cos YES YES YES YES
Math.cosh YES N/A YES YES
Math.exp YES YES YES YES
Math.expm1 YES N/A YES YES
Math.floor YES YES YES YES
Math.fround YES N/A YES YES
Math.hypot YES N/A YES YES
Math.log YES YES YES YES
Math.log10 YES N/A YES YES
Math.log1p YES N/A YES YES
Math.log2 YES N/A YES YES
Math.max YES YES YES YES
Math.min YES YES YES YES
Math.pow YES YES YES YES
Math.random YES YES YES YES
Math.round YES YES YES YES
Math.sign YES N/A YES YES
Math.sin YES YES YES YES
Math.sinh YES N/A YES YES
Math.sqrt YES YES YES YES
Math.tan YES YES YES YES
Math.tanh YES N/A YES YES
Math.trunc YES N/A YES YES
Math.imul YES YES YES YES
Object.toString YES YES YES YES
Object.toLocaleString YES YES YES YES
Object.valueOf YES YES YES YES
Object.hasOwnProperty YES YES YES YES
Object.propertyIsEnumerable YES YES YES YES
Object.isPrototypeOf YES YES YES YES
Object._defineGetter_ YES YES YES YES
Object._defineSetter_ YES YES YES YES
Object._lookupGetter_ YES YES YES YES
Object._lookupSetter_ YES YES YES YES
Object.constructor YES YES YES YES
Promise.then YES YES YES YES
Promise.catch YES YES YES YES
Promise.constructor YES YES YES YES
RegExp.compile YES YES YES YES
RegExp.exec YES YES YES YES
RegExp.toString YES YES YES YES
RegExp.test YES YES YES YES
RegExp.constructor YES YES YES YES
Set.forEach YES N/A YES YES
Set.add YES N/A YES YES
Set.clear YES N/A YES YES
Set.delete YES N/A YES YES
Set.has YES N/A YES YES
Set.entries YES N/A YES YES
Set.values YES N/A YES YES
Set.keys YES N/A YES YES
Set.constructor YES N/A YES YES
String.match YES YES YES YES
String.padStart YES N/A N/A N/A
String.padEnd YES N/A N/A N/A
String.repeat YES N/A YES YES
String.replace YES YES YES YES
String.search YES YES YES YES
String.split YES YES YES YES
String.toString YES YES YES YES
String.valueOf YES YES YES YES
String.charAt YES YES YES YES
String.charCodeAt YES YES YES YES
String.codePointAt YES N/A YES YES
String.concat YES YES YES YES
String.indexOf YES YES YES YES
String.lastIndexOf YES YES YES YES
String.slice YES YES YES YES
String.substr YES YES YES YES
String.substring YES YES YES YES
String.toLowerCase YES YES YES YES
String.toUpperCase YES YES YES YES
String.localeCompare YES YES YES YES
String.toLocaleLowerCase YES YES YES YES
String.toLocaleUpperCase YES YES YES YES
String.big YES YES YES YES
String.small YES YES YES YES
String.blink YES YES YES YES
String.bold YES YES YES YES
String.fixed YES YES YES YES
String.italics YES YES YES YES
String.strike YES YES YES YES
String.sub YES YES YES YES
String.sup YES YES YES YES
String.fontcolor YES YES YES YES
String.fontsize YES YES YES YES
String.anchor YES YES YES YES
String.link YES YES YES YES
String.trim YES YES YES YES
String.trimLeft YES YES YES YES
String.trimRight YES YES YES YES
String.startsWith YES N/A YES YES
String.endsWith YES N/A YES YES
String.includes YES N/A YES YES
String.normalize YES YES YES YES
String.constructor YES YES YES YES
Symbol.toString YES N/A YES YES
Symbol.valueOf YES N/A N/A YES
Symbol.constructor YES N/A YES YES
TypeError.toString YES N/A N/A YES
TypeError.constructor YES YES YES YES
Uint16Array.constructor YES YES YES YES
Uint32Array.constructor YES YES YES YES
Uint8Array.constructor YES YES YES YES
Uint8ClampedArray.constructor YES YES YES YES
WeakMap.delete YES YES YES YES
WeakMap.get YES YES YES YES
WeakMap.has YES YES YES YES
WeakMap.set YES YES YES YES
WeakMap.constructor YES YES YES YES

N/A 表示这个标准库方法在平台上不支持

Q: 这些数据是怎么来的,靠谱吗?
A: 这些数据是在真实小程序运行环境下运行,然后把 API 支持情况发送到服务器后台,再写个脚本把数据整理汇总后得来的。

Q: 其他平台,比如 Android 5.0 的支持情况怎么样?
A: 由于条件限制,手上没有 Android 5.0 的手机,有愿意配合收集数据的,私信留言。配合的方法很简单,用指定型号的手机打开一个微信小程序,按一个按钮即可。

Q: 为什么不使用 lodash 之类效率更高的库,而使用的标准库?
A: 使用 lodash 之类的确实效率更高,兼容性也更好。基于两个原因没有使用,一是 lodash 太大,而微信小程序限制在 1MB 以内。当然,可以用 lodash 模块化的版本来解决,但还有第二个原因,即 lodash 的一些 API 也有兼容性问题,比如我试过 lodash.findIndex 这个包,结果在 Android 6.0.1 上也无法成功运行 (这一点未做深入验证,感兴趣的同学可以验证一下)。

总结

从后台数据来看,小程序刚发布的前三天,确实带来了非常可观的流量红利,但这部分偿鲜的用户,很快就消失了。三天过后,基本上保持了平衡的访问量。流量红利和广告一样,是催化剂,真正有价值的还是要做用户需要的产品。

在此顺手安利一下开发的两个小程序 360好书推荐51经典电影,偶尔想用的时候打开,可能会偶遇一些小惊喜。但坦白讲,这两个小程序都和微信倡导的小程序价值观不符。微信还是希望通过小程序把线下低频的,服务成本高(这里应该主要是时间成本,即便利性)的场景,转化为线上快捷的使用方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容