Mini app markup, styling, scripting, and updating

Markup languages

As outlined before, rather than with plain HTML, mini apps are written with dialects of HTML. If you have ever dealt with Vue.js text interpolation and directives, you will feel immediately at home, but similar concepts existed way before that in XML Transformations (XSLT). Below, you can see code samples from WeChat's WXML, but the concept is the same for all mini apps platforms, namely Alipay's AXML, Baidu's Swan Element, ByteDance's TTML (despite the DevTools calling it Bxml), and Quick App's HTML. Just like with Vue.js, the underlying mini app programming concept is the model-view-viewmodel (MVVM).

Data binding

Data binding corresponds to Vue.js' text interpolation.

<!-- wxml -->
<view>{{message}}</view>
// page.js
Page({
  data: {
    message: "Hello World!",
  },
});

List rendering

List rendering works like Vue.js v-for directive.

<!-- wxml -->
<view wx:for="{{array}}">{{item}}</view>
// page.js
Page({
  data: {
    array: [1, 2, 3, 4, 5],
  },
});

Conditional rendering

Conditional rendering works like Vue.js' v-if directive.

<!-- wxml -->
<view wx:if="{{view == 'one'}}">One</view>
<view wx:elif="{{view == 'two'}}">Two</view>
<view wx:else="{{view == 'three'}}">Three</view>
// page.js
Page({
  data: {
    view: "three",
  },
});

Templates

Rather than requiring the imperative cloning of the content of an HTML template, WXML templates can be used declaratively via the is attribute that links to a template definition.

<!-- wxml -->
<template name="person">
  <view>
    First Name: {{firstName}}, Last Name: {{lastName}}
  </view>
</template>
<template is="person" data="{{...personA}}"></template>
<template is="person" data="{{...personB}}"></template>
<template is="person" data="{{...personC}}"></template>
// page.js
Page({
  data: {
    personA: { firstName: "Alice", lastName: "Foo" },
    personB: { firstName: "Bob", lastName: "Bar" },
    personC: { firstName: "Charly", lastName: "Baz" },
  },
});

Styling

Styling happens with dialects of CSS. WeChat's is named WXSS. For Alipay, theirs is called ACSS, Baidu's simply CSS, and for ByteDance, their dialect is referred to as TTSS. What they have in common is that they extend CSS with responsive pixels. When writing regular CSS, developers need to convert all pixel units to adapt to different mobile device screens with different widths and pixel ratios. TTSS supports the rpx unit as its underlying layer, which means the mini app takes over the job from the developer and converts the units on their behalf, based on a specified screen width of 750rpx. For example, on a Pixel 3a phone with a screen width of 393px (and a device pixel ratio of 2.75), responsive 200rpx become 104px on the real device when inspected with Chrome DevTools (393px / 750rpx * 200rpx ≈ 104px). In Android, the same concept is called density-independent pixel.

Inspecting a view with Chrome DevTools whose responsive pixel padding was specified with `200rpx` shows that it is actually `104px` on a Pixel 3a device.
Inspecting the actual padding on a Pixel 3a device with Chrome DevTools.
/* app.wxss */
.container {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 200rpx 0; /* ← responsive pixels */
  box-sizing: border-box;
}

Since components (see later) do not use shadow DOM, styles declared on a page reach into all components. The common stylesheet file organization is to have one root stylesheet for global styles, and individual per-page stylesheets specific to each page of the mini app. Styles can be imported with an @import rule that behaves like the @import CSS at-rule. Like in HTML, styles can also be declared inline, including dynamic text interpolation (see before).

<view style="color:{{color}};" />

Scripting

Mini apps support a "safe subset" of JavaScript that includes support for modules using varying syntaxes that remind of CommonJS or RequireJS. JavaScript code cannot be executed via eval() and no functions can be created with new Function(). The scripting execution context is V8 or JavaScriptCore on devices, and V8 or NW.js in the simulator. Coding with ES6 or newer syntax is usually possible, since the particular DevTools automatically transpile the code to ES5 if the build target is an operating system with an older WebView implementation (see later). The documentation of the super app providers explicitly mentions that their scripting languages are not to be confused with and are distinct from JavaScript. This statement, however, refers mostly just to the way modules work, that is, that they do not support standard ES Modules yet.

As mentioned before, the mini app programming concept is the model-view-viewmodel (MVVM). The logic layer and the view layer run on different threads, which means the user interface does not get blocked by long-running operations. In web terms, you can think of scripts running in a Web Worker.

WeChat's scripting language is called WXS, Alipay's SJS, ByteDance's likewise SJS. Baidu speaks of JS when referencing theirs. These scripts need to be included using a special kind of tag, for example, <wxs> in WeChat. In contrast, Quick App uses regular <script> tags and the ES6 JS syntax.

<wxs module="m1">
  var msg = "hello world";
  module.exports.message = msg;
</wxs>

<view>{{m1.message}}</view>

Modules can also be loaded via a src attribute, or imported via require().

// /pages/tools.wxs
var foo = "'hello world' from tools.wxs";
var bar = function (d) {
  return d;
};
module.exports = {
  FOO: foo,
  bar: bar,
};
module.exports.msg = "some msg";
<!-- page/index/index.wxml -->
<wxs src="./../tools.wxs" module="tools" />
<view>{{tools.msg}}</view>
<view>{{tools.bar(tools.FOO)}}</view>
// /pages/logic.wxs
var tools = require("./tools.wxs");

console.log(tools.FOO);
console.log(tools.bar("logic.wxs"));
console.log(tools.msg);

JavaScript bridge API

The JavaScript bridge that connects mini apps with the operating system makes it possible to use OS capabilities (see Access to powerful features. It also offers a number of convenience methods. For an overview, you can check out the different APIs of WeChat, Alipay, Baidu, ByteDance, and Quick App.

Feature detection is straightforward, since all platforms provide a (literally called like this) canIUse() method whose name seems inspired by the website caniuse.com. For example, ByteDance's tt.canIUse() allows for support checks for APIs, methods, parameters, options, components, and attributes.

// Testing if the `<swiper>` component is supported.
tt.canIUse("swiper");
// Testing if a particular field is supported.
tt.canIUse("request.success.data");

Updates

Mini apps do not have a standardized update mechanism (discussion around potential standardization). All mini app platforms have a backend system, which allows mini app developers to upload new versions of their mini apps. A super app then uses that backend system to check for and download updates. Some super apps perform updates entirely in the background, without any way for the mini app itself to influence the update flow. Other super apps give more control to the mini apps themselves.

As an example of a sophisticated process, the following paragraphs describe WeChat's update mechanism for mini apps in more detail. WeChat checks for available updates in the following two scenarios:

  1. WeChat will regularly check for updates of recently used mini apps as long as WeChat is running. If an update is found, the update is downloaded, and synchronously applied the next time the user cold-starts the mini app. Cold-starting a mini apps occurs when the mini app was not currently running when the user opened it (WeChat force-closes mini apps after being in the background for 5 minutes).
  2. WeChat also checks for updates when a mini app is cold-started. For mini apps that the user has not opened for a long time, the update is checked for and downloaded synchronously. While the update is downloading, the user has to wait. Once the download finishes, the update is applied, and the mini app opens. If the download fails, e.g., due to bad network connectivity, the mini app opens regardless. For mini apps that the user has opened recently, any potential update is downloaded asynchronously in the background and will be applied the next time the user cold-starts the mini app.

Mini apps can opt-in to earlier updates by using the UpdateManager API. It provides the following functionality:

  • Notifies the mini app when a check for updates is made. (onCheckForUpdate)
  • Notifies the mini app when an update has been downloaded and is available. (onUpdateReady)
  • Notifies the mini app when an update could not be downloaded. (onUpdateFailed)
  • Allows the mini app to force-install an available update, which will restart the app. (applyUpdate)

WeChat also provides additional update customization options for mini app developers in its backend system: 1. One option allows developers to opt-out from synchronous updates for users that already have a certain minimum version of the mini app installed, and instead forces the updates to be asynchronous. 2. Another option allows developers to set a minimum required version of their mini app. This will make asynchronous updates from a version lower than the minimum required version force-reload the mini app after applying the update. It will also block opening an older version of the mini app if downloading of the update fails.

Acknowledgements

This article was reviewed by Joe Medley, Kayce Basques, Milica Mihajlija, Alan Kent, and Keith Gu.