小程序标记、样式、脚本和更新

标记语言

如前所述,小程序不是用纯 HTML 编写的,而是用 HTML 的方言编写的。如果您曾经处理过 Vue.js 文本插值和指令,您会立即感到宾至如归,但类似的概念早在 XML 转换 (XSLT) 中就已存在。下面,您可以看到来自微信 WXML 的代码示例,但对于所有小程序平台,概念都是相同的,即支付宝的 AXML、百度的 Swan Element、字节跳动的 TTML(尽管 DevTools 称之为 Bxml)和快应用的 HTML。就像 Vue.js 一样,底层的小程序编程概念是 模型-视图-视图模型 (MVVM)。

数据绑定

数据绑定对应于 Vue.js 的 文本插值

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

列表渲染

列表渲染的工作方式类似于 Vue.js v-for 指令

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

条件渲染

条件渲染的工作方式类似于 Vue.js 的 v-if 指令

<!-- 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",
  },
});

模板

WXML 模板不是要求命令式地 克隆 HTML 模板的 content,而是可以通过 is 属性声明性地使用,该属性链接到模板定义。

<!-- 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" },
  },
});

样式

样式是通过 CSS 的方言实现的。微信的方言名为 WXSS。对于支付宝,他们的方言称为 ACSS,百度的简称为 CSS,而对于字节跳动,他们的方言被称为 TTSS。它们的共同点在于它们使用响应式像素扩展了 CSS。在编写常规 CSS 时,开发人员需要转换所有像素单位,以适应宽度和像素比率不同的不同移动设备屏幕。TTSS 支持 rpx 单位作为其底层,这意味着小程序接管了开发人员的工作,并代表他们转换单位,基于指定的 750rpx 屏幕宽度。例如,在屏幕宽度为 393px(设备像素比率为 2.75)的 Pixel 3a 手机上,响应式 200rpx 在使用 Chrome DevTools 检查时,在真实设备上变为 104px (393px / 750rpx * 200rpx ≈ 104px)。在 Android 中,相同的概念称为 与密度无关的像素

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.
使用 Chrome DevTools 检查 Pixel 3a 设备上的实际内边距。
/* 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;
}

由于组件(请参阅稍后)不使用 shadow DOM,因此在页面上声明的样式会影响所有组件。常见的样式表文件组织方式是,为一个全局样式设置一个根样式表,并为小程序的每个页面设置单独的页面特定样式表。可以使用类似于 @import CSS at-rule 的 @import 规则导入样式。与 HTML 中一样,样式也可以内联声明,包括动态文本插值(请参阅之前)。

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

脚本

小程序支持 JavaScript 的“安全子集”,其中包括对模块的支持,这些模块使用不同的语法,让人想起 CommonJSRequireJS。JavaScript 代码无法通过 eval() 执行,并且无法使用 new Function() 创建函数。脚本执行上下文是设备上的 V8JavaScriptCore,以及模拟器中的 V8 或 NW.js。通常可以使用 ES6 或更新的语法进行编码,因为如果构建目标是具有较旧 WebView 实现的操作系统,则特定的 DevTools 会自动将代码转换为 ES5(请参阅稍后)。超级应用提供商的文档明确指出,他们的脚本语言不应与 JavaScript 混淆,并且与 JavaScript 不同。但是,此声明主要指的是模块的工作方式,即它们尚不支持标准 ES 模块

之前所述,小程序编程概念是 模型-视图-视图模型 (MVVM)。逻辑层和视图层在不同的线程上运行,这意味着用户界面不会被长时间运行的操作阻塞。在 Web 术语中,您可以将脚本视为在 Web Worker 中运行。

微信的脚本语言称为 WXS,支付宝的为 SJS,字节跳动的同样为 SJS。百度在引用他们的脚本语言时,称之为 JS。这些脚本需要使用特殊类型的标记包含,例如,微信中的 <wxs>。相比之下,快应用使用常规 <script> 标记和 ES6 JS 语法。

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

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

模块也可以通过 src 属性加载,或通过 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 桥接 API

将小程序与操作系统连接起来的 JavaScript 桥接器,使得可以使用操作系统功能(请参阅访问强大功能)。它还提供了许多便捷方法。有关概述,您可以查看 微信支付宝百度字节跳动快应用 的不同 API。

功能检测非常简单,因为所有平台都提供了一个(字面意思是这样称呼的)canIUse() 方法,其名称似乎受到网站 caniuse.com 的启发。例如,字节跳动的 tt.canIUse() 允许检查对 API、方法、参数、选项、组件和属性的支持。

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

更新

小程序没有标准化的更新机制(围绕潜在标准化的讨论)。所有小程序平台都有一个后端系统,允许小程序开发者上传他们的小程序的新版本。然后,超级应用使用该后端系统来检查和下载更新。一些超级应用完全在后台执行更新,小程序本身无法以任何方式影响更新流程。其他超级应用则为小程序本身提供了更多控制权。

作为复杂过程的一个示例,以下段落更详细地描述了 微信小程序更新机制。微信在以下两种情况下检查是否有可用更新

  1. 只要微信正在运行,微信就会定期检查最近使用过的小程序的更新。如果找到更新,则下载更新,并在用户下次冷启动小程序时同步应用。当用户打开小程序时,如果该小程序当前未运行(微信会在小程序在后台运行 5 分钟后强制关闭小程序),则会发生冷启动小程序。
  2. 当小程序冷启动时,微信也会检查更新。对于用户长时间未打开的小程序,将检查并同步下载更新。在更新下载期间,用户必须等待。下载完成后,应用更新,小程序打开。如果下载失败,例如,由于网络连接不良,小程序仍会打开。对于用户最近打开的小程序,任何潜在的更新都会在后台异步下载,并在用户下次冷启动小程序时应用。

小程序可以通过使用 UpdateManager API 选择更早的更新。它提供以下功能

  • 在进行更新检查时通知小程序。(onCheckForUpdate
  • 在更新已下载并可用时通知小程序。(onUpdateReady
  • 在更新无法下载时通知小程序。(onUpdateFailed
  • 允许小程序强制安装可用更新,这将重启应用程序。(applyUpdate

微信还在其后端系统中为小程序开发者提供了额外的更新自定义选项:1. 一种选项允许开发者选择不对已安装特定最低版本的小程序的用户进行同步更新,而是强制更新为异步更新。2. 另一种选项允许开发者设置其小程序所需的最低版本。这将使来自低于最低所需版本的版本的异步更新在应用更新后强制重新加载小程序。如果更新下载失败,它还会阻止打开旧版本的小程序。

致谢

本文由 Joe MedleyKayce BasquesMilica MihajlijaAlan Kent 和 Keith Gu 审阅。