A quick look at WeChat's Mini Programs

tomayac

Thomas Steiner

Posted on August 15, 2019

A quick look at WeChat's Mini Programs

While preparing for my presentation at the Google Developer Days 2019 in Shanghai, China I was reminded again that China is a market where few super apps like WeChat host a gazillion mini apps or mini programs that fulfill everyday needs like booking cabs, reserving tables, etc.

I got curious and downloaded the SDK and after playing a bit with the Your First Mini App tutorial, I realized the whole thing is so close to building for the actual web, it both fascinates, intrigues, and honestly somewhat infuriates me.

App Architecture

  • You style your apps with WXSS, which is essentially CSS with some neat additions like responsive pixels.
page-section-gap{
  box-sizing: border-box;
  padding: 0 30rpx;
}

.page-body-button {
  margin-bottom: 30rpx;
}
Enter fullscreen mode Exit fullscreen mode
  • You write your app logic with JavaScript (or TypeScript), with App as the top level object and wx as the object you get all the cool capabilities from. The API is incredibly powerful. Here are the keys of the wx object:
[
  "addCard",
  "addNativeDownloadTask",
  "addPhoneContact",
  "addWeRunData",
  "arrayBufferToBase64",
  "authorize",
  "base64ToArrayBuffer",
  "batchGetContactDirectly",
  "bindPaymentCard",
  "calRqt",
  "canIUse",
  "cancelDownloadAppTask",
  "canvasGetImageData",
  "canvasPutImageData",
  "canvasToTempFilePath",
  "captureScreen",
  "checkIsSoterEnrolledInDevice",
  "checkIsSupportFacialRecognition",
  "checkIsSupportSoterAuthentication",
  "checkSession",
  "chooseAddress",
  "chooseContact",
  "chooseImage",
  "chooseInvoice",
  "chooseInvoiceTitle",
  "chooseLocation",
  "chooseMedia",
  "chooseMessageFile",
  "chooseMultiMedia",
  "chooseShareGroup",
  "chooseVideo",
  "chooseWeChatContact",
  "clearStorage",
  "clearStorageSync",
  "closeBLEConnection",
  "closeBluetoothAdapter",
  "closeSocket",
  "cloud",
  "compressImage",
  "connectSocket",
  "connectWifi",
  "createAnimation",
  "createAudioContext",
  "createBLEConnection",
  "createCameraContext",
  "createCanvasContext",
  "createContext",
  "createInnerAudioContext",
  "createIntersectionObserver",
  "createInterstitialAd",
  "createLivePlayerContext",
  "createLivePusherContext",
  "createMapContext",
  "createOffscreenCanvas",
  "createRewardedVideoAd",
  "createSelectorQuery",
  "createUDPSocket",
  "createVideoContext",
  "createWorker",
  "downloadApp",
  "downloadAppForIOS",
  "downloadFile",
  "downloadSilkVoice",
  "drawCanvas",
  "enterContact",
  "env",
  "error",
  "exitVoIPChat",
  "faceVerifyForPay",
  "getABTestConfig",
  "getAccountInfoSync",
  "getAppInstallState",
  "getAvailableAudioSources",
  "getBLEDeviceCharacteristics",
  "getBLEDeviceServices",
  "getBackgroundAudioManager",
  "getBackgroundAudioPlayerState",
  "getBackgroundFetchData",
  "getBackgroundFetchToken",
  "getBatteryInfo",
  "getBatteryInfoSync",
  "getBeacons",
  "getBluetoothAdapterState",
  "getBluetoothDevices",
  "getClipboardData",
  "getConnectedBluetoothDevices",
  "getConnectedWifi",
  "getCookies",
  "getExtConfig",
  "getExtConfigSync",
  "getFileInfo",
  "getFileSystemManager",
  "getHCEState",
  "getImageInfo",
  "getLabInfo",
  "getLaunchOptionsSync",
  "getLocation",
  "getLogManager",
  "getMenuButtonBoundingClientRect",
  "getNetworkType",
  "getOpenDeviceId",
  "getPublicLibVersion",
  "getRealtimeLogManager",
  "getRecorderManager",
  "getResPath",
  "getSavedFileInfo",
  "getSavedFileList",
  "getScreenBrightness",
  "getSelectedTextRange",
  "getSetting",
  "getShareInfo",
  "getStorage",
  "getStorageInfo",
  "getStorageInfoSync",
  "getStorageSync",
  "getSystemInfo",
  "getSystemInfoSync",
  "getUpdateManager",
  "getUserInfo",
  "getWeRunData",
  "getWifiList",
  "getWxSecData",
  "hideKeyboard",
  "hideLoading",
  "hideNavigationBarLoading",
  "hideShareMenu",
  "hideTabBar",
  "hideTabBarRedDot",
  "hideToast",
  "installDownloadApp",
  "isSDKError",
  "isSystemError",
  "isThirdError",
  "joinVoIPChat",
  "launchApplicationDirectly",
  "launchApplicationForNative",
  "launchMiniProgram",
  "loadFontFace",
  "loadPaySpeechDialectConfig",
  "login",
  "makePhoneCall",
  "makeVoIPCall",
  "navigateBack",
  "navigateBackApplication",
  "navigateBackH5",
  "navigateBackMiniProgram",
  "navigateBackNative",
  "navigateTo",
  "navigateToDevMiniProgram",
  "navigateToMiniProgram",
  "navigateToMiniProgramDirectly",
  "nextTick",
  "notifyBLECharacteristicValueChange",
  "notifyBLECharacteristicValueChanged",
  "offAppHide",
  "offAppShow",
  "offAudioInterruptionBegin",
  "offAudioInterruptionEnd",
  "offError",
  "offLocalServiceDiscoveryStop",
  "offLocalServiceFound",
  "offLocalServiceLost",
  "offLocalServiceResolveFail",
  "offPageNotFound",
  "offWindowResize",
  "onAccelerometerChange",
  "onAppEnterBackground",
  "onAppEnterForeground",
  "onAppHide",
  "onAppRoute",
  "onAppRouteDone",
  "onAppShow",
  "onAppUnhang",
  "onAudioInterruptionBegin",
  "onAudioInterruptionEnd",
  "onBLECharacteristicValueChange",
  "onBLEConnectionStateChange",
  "onBLEConnectionStateChanged",
  "onBackgroundAudioPause",
  "onBackgroundAudioPlay",
  "onBackgroundAudioStop",
  "onBackgroundFetchData",
  "onBeaconServiceChange",
  "onBeaconUpdate",
  "onBluetoothAdapterStateChange",
  "onBluetoothDeviceFound",
  "onCompassChange",
  "onDeviceMotionChange",
  "onDownloadAppStateChange",
  "onError",
  "onEvaluateWifi",
  "onGetWifiList",
  "onGyroscopeChange",
  "onHCEMessage",
  "onKeyboardHeightChange",
  "onLocalServiceDiscoveryStop",
  "onLocalServiceFound",
  "onLocalServiceLost",
  "onLocalServiceResolveFail",
  "onLocationChange",
  "onMemoryWarning",
  "onNativeEvent",
  "onNetworkStatusChange",
  "onPageNotFound",
  "onPageReload",
  "onSocketClose",
  "onSocketError",
  "onSocketMessage",
  "onSocketOpen",
  "onTapNavigationBarRightButton",
  "onUploadEncryptedFileToCDNProgress",
  "onUserCaptureScreen",
  "onVoIPChatInterrupted",
  "onVoIPChatMembersChanged",
  "onVoIPChatSpeakersChanged",
  "onVoicePlayEnd",
  "onWebviewEvent",
  "onWifiConnected",
  "onWindowResize",
  "openBluetoothAdapter",
  "openBusinessView",
  "openCard",
  "openDeliveryList",
  "openDocument",
  "openGoldenRedPacketDetail",
  "openLocation",
  "openMiniProgramHistoryList",
  "openMiniProgramProfile",
  "openMiniProgramSearch",
  "openMiniProgramStarList",
  "openOfficialAccountProfile",
  "openOfflinePayView",
  "openQRCode",
  "openSetting",
  "openUrl",
  "openUserProfile",
  "openWCPayCardList",
  "operateWXData",
  "pageScrollTo",
  "pauseBackgroundAudio",
  "pauseDownloadAppTask",
  "pauseVoice",
  "playBackgroundAudio",
  "playVoice",
  "presetWifiList",
  "preventApplePayUI",
  "previewImage",
  "queryDownloadAppTask",
  "reLaunch",
  "readBLECharacteristicValue",
  "redirectTo",
  "removeSavedFile",
  "removeStorage",
  "removeStorageSync",
  "removeTabBarBadge",
  "removeUserCloudStorage",
  "reportAction",
  "reportAnalytics",
  "reportIDKey",
  "reportKeyValue",
  "reportMonitor",
  "request",
  "requestMallPayment",
  "requestPayment",
  "requestPaymentToBank",
  "requestVirtualPayment",
  "resumeDownloadAppTask",
  "saveFile",
  "saveImageToPhotosAlbum",
  "saveVideoToPhotosAlbum",
  "scanCode",
  "secureTunnel",
  "seekBackgroundAudio",
  "sendBizRedPacket",
  "sendGoldenRedPacket",
  "sendHCEMessage",
  "sendSocketMessage",
  "setBackgroundColor",
  "setBackgroundFetchToken",
  "setBackgroundTextStyle",
  "setClipboardData",
  "setCookies",
  "setCurrentPaySpeech",
  "setEnableDebug",
  "setInnerAudioOption",
  "setKeepScreenOn",
  "setLabInfo",
  "setNavigationBarAlpha",
  "setNavigationBarColor",
  "setNavigationBarRightButton",
  "setNavigationBarTitle",
  "setPageStyle",
  "setResPath",
  "setScreenBrightness",
  "setStorage",
  "setStorageSync",
  "setTabBarBadge",
  "setTabBarItem",
  "setTabBarStyle",
  "setTopBarText",
  "setUserCloudStorage",
  "setWifiList",
  "shareAppMessageForFakeNative",
  "showActionSheet",
  "showLoading",
  "showModal",
  "showNavigationBarLoading",
  "showShareActionSheet",
  "showShareMenu",
  "showTabBar",
  "showTabBarRedDot",
  "showToast",
  "startAccelerometer",
  "startBeaconDiscovery",
  "startBluetoothDevicesDiscovery",
  "startCompass",
  "startCustomFacialRecognitionVerify",
  "startCustomFacialRecognitionVerifyAndUploadVideo",
  "startDeviceMotionListening",
  "startFacialRecognitionVerify",
  "startFacialRecognitionVerifyAndUploadVideo",
  "startGyroscope",
  "startHCE",
  "startLocalServiceDiscovery",
  "startLocationUpdate",
  "startLocationUpdateBackground",
  "startPullDownRefresh",
  "startRecord",
  "startSoterAuthentication",
  "startWifi",
  "stopAccelerometer",
  "stopBackgroundAudio",
  "stopBeaconDiscovery",
  "stopBluetoothDevicesDiscovery",
  "stopCompass",
  "stopDeviceMotionListening",
  "stopGyroscope",
  "stopHCE",
  "stopLocalServiceDiscovery",
  "stopLocationUpdate",
  "stopPullDownRefresh",
  "stopRecord",
  "stopVoice",
  "stopWifi",
  "switchTab",
  "traceEvent",
  "triggerGettingWidgetData",
  "updatePerfData",
  "updateShareMenu",
  "updateVoIPChatMuteConfig",
  "uploadEncryptedFileToCDN",
  "uploadFile",
  "uploadSilkVoice",
  "uploadWeRunData",
  "verifyPaymentPassword",
  "version",
  "vibrateLong",
  "vibrateShort",
  "voiceSplitJoint",
  "writeBLECharacteristicValue"
]
Enter fullscreen mode Exit fullscreen mode
<view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view wx:elif="{{view == 'APP'}}"> APP </view>
<view wx:else="{{view == 'MINA'}}"> MINA </view>
Enter fullscreen mode Exit fullscreen mode

Overall a really nice separation of concerns. I could very well imagine being productive with this in no time. The onboarding experience of the documentation is pretty neat. The SDK is well made with (essentially an adapted) Chrome DevTools integrated and some VS Code like features like code completion.

Programming Concept

The overall programming concept reminds of OpenSocial (if anyone remembers that) where there is a baseline assumption of a logged in user whose social graph can be explored:

// OpenSocial
osapi.people.getViewer().execute(function(result) {
  if (!result.error) {
    console.log('Your name is ' + result.displayName + '!');
  }
});
Enter fullscreen mode Exit fullscreen mode
// WeChat
wx.getSetting({
  success: res => {
    if (res.authSetting['scope.userInfo']) {
      wx.getUserInfo({
        success: res => {        
          console.log(res.userInfo);
        }
      });
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

The

Component Library

They also have nice set of declarative components, think web components essentially, that you can create with WXML and interact with from JavaScript:

<map
  id="myMap"
  style="width: 100%; height: 300px;"
  latitude="{{latitude}}"
  longitude="{{longitude}}"
  markers="{{markers}}"
  covers="{{covers}}"
  show-location
></map>
Enter fullscreen mode Exit fullscreen mode
Page({
  data: {
    latitude: 23.099994,
    longitude: 113.324520,
    markers: [{
      id: 1,
      latitude: 23.099994,
      longitude: 113.324520,
      name: 'T.I.T 创意园'
    }],
    covers: [{
      latitude: 23.099994,
      longitude: 113.344520,
      iconPath: '/image/location.png'
    }, {
      latitude: 23.099994,
      longitude: 113.304520,
      iconPath: '/image/location.png'
    }]
  },
  onReady: function (e) {
    this.mapCtx = wx.createMapContext('myMap')
  },
  getCenterLocation: function () {
    this.mapCtx.getCenterLocation({
      success: function(res){
        console.log(res.longitude)
        console.log(res.latitude)
      }
    })
  }
})
Enter fullscreen mode Exit fullscreen mode

WeChat  raw `<map>` endraw  component sample running in WeChat DevTools

And of course they have a <web-view>.

WeChat  raw `<web-view>` endraw  component sample running in WeChat DevTools

Inner Mechanics

From what I understand, it's all running in an iframe, Chrome/WeChat DevTools then hides the container, and all you see is the WXML layer.
When you look at the SDK's package contents, they make no real effort at hiding any of the inner mechanics: it's all HTML (of particular interest: /Applications/wechatwebdevtools.app/Contents/Resources/package.nw/html/standalone.html), CSS (of particular interest: /Applications/wechatwebdevtools.app/Contents/Resources/package.nw/static/app.css), and JavaScript (of particular interest: /Applications/wechatwebdevtools.app/Contents/Resources/package.nw/js/vendor/index.js).

Inspecting package contents of the WeChat DevTools

Finally there is a local web server running that allows them to link from the online docs to local URLs like 127.0.0.1:32123/minicode/VBZ3Jim26zYu, which in turn allows them to open code samples from the docs that are ready to play with a click in WeChat DevTools. The web server luckily only runs when the Wechat Devtools are open.

💖 💪 🙅 🚩
tomayac
Thomas Steiner

Posted on August 15, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related