תגי עיצוב, עיצוב, סקריפטים ועדכון של מיני אפליקציות

Markup languages

כפי שצוין קודם, אפליקציות מיני נכתבות בניבים של HTML, ולא ב-HTML רגיל. אם כבר התנסיתם בVue.js, תרגישו מיד בבית, אבל מושגים דומים היו קיימים הרבה לפני כן ב-XML Transformations‏ (XSLT). בהמשך מוצגות דוגמאות קוד מ-WXML של WeChat, אבל הרעיון זהה בכל הפלטפורמות של אפליקציות מיני, כלומר AXML של Alipay,‏ Swan Element של Baidu,‏ TTML של ByteDance (למרות שבכלי הפיתוח קוראים לזה Bxml) ו-HTML של Quick App. בדומה ל-Vue.js, המושג הבסיסי בתכנות מיני-אפליקציות הוא מודל-תצוגה-תצוגת מודל (MVVM).

קישור נתונים

התאמת הנתונים מקבילה לאינטרפולציה של טקסט ב-Vue.js.

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

רינדור הרשימה

עיבוד הרשימה פועל כמו ההוראה v-for של Vue.js.

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

רינדור מותנה

עיבוד מותנה פועל כמו ההנחיה v-if של Vue.js.

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

תבניות

במקום לדרוש שיבוט של content של תבנית HTML, אפשר להשתמש בתבניות WXML באופן הצהרתי באמצעות המאפיין 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. השם של WeChat הוא WXSS. ב-Alipay, הניב שלהם נקרא ACSS, ב-Baidu הוא נקרא פשוט CSS, וב-ByteDance הניב שלהם נקרא TTSS. המשותף לכולם הוא שהם מרחיבים את CSS עם פיקסלים רספונסיביים. כשכותבים CSS רגיל, מפתחים צריכים להמיר את כל יחידות הפיקסלים כדי להתאים למסכים שונים של מכשירים ניידים עם רוחבים ויחסי פיקסלים שונים. ‫TTSS תומך ביחידה rpx כשכבה הבסיסית שלו, כלומר המיני-אפליקציה משתלטת על העבודה מהמפתח וממירה את היחידות בשמו, על סמך רוחב מסך שצוין של 750rpx. לדוגמה, בטלפון Pixel 3a עם רוחב מסך של 393px (ויחס פיקסלים במכשיר של 2.75), רכיבים דינמיים 200rpx הופכים ל-104px במכשיר האמיתי כשבודקים אותם באמצעות כלי הפיתוח ל-Chrome‏ (393px / 750rpx * 200rpx ≈ 104px). ב-Android, אותו קונספט נקרא פיקסל שלא תלוי בדחיסות.

בדיקה של תצוגה באמצעות כלי הפיתוח ל-Chrome, שבה הריווח המותאם של הפיקסלים צוין באמצעות ‎ `200rpx` ‎, מראה שהערך בפועל הוא ‎ `104px` ‎ במכשיר Pixel 3a.
בדיקת הריווח בפועל במכשיר Pixel 3a באמצעות כלי הפיתוח ל-Chrome.
/* 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 ‏@import. בדומה ל-HTML, אפשר להצהיר על סגנונות גם בשורה, כולל אינטרפולציה של טקסט דינמי (ראו לפני).

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

כתיבת סקריפטים

אפליקציות מיני תומכות ב'קבוצת משנה בטוחה' של JavaScript, שכוללת תמיכה במודולים באמצעות תחביר משתנה שמזכיר את CommonJS או את RequireJS. אי אפשר להריץ קוד JavaScript באמצעות eval() ואי אפשר ליצור פונקציות באמצעות new Function(). הקשר של ביצוע הסקריפט הוא V8 או JavaScriptCore במכשירים, ו-V8 או NW.js בסימולטור. בדרך כלל אפשר לתכנת עם תחביר ES6 או תחביר חדש יותר, כי כלי הפיתוח הספציפיים מבצעים טרנספילציה אוטומטית של הקוד ל-ES5 אם יעד הבנייה הוא מערכת הפעלה עם הטמעה ישנה יותר של WebView (ראו בהמשך). במסמכי התיעוד של ספקי האפליקציות המתקדמות מצוין במפורש שאין לבלבל בין שפות הסקריפט שלהם לבין JavaScript, ושמדובר בשפות שונות. עם זאת, ההצהרה הזו מתייחסת בעיקר לאופן הפעולה של המודולים, כלומר, הם עדיין לא תומכים במודולים סטנדרטיים של ES.

כמו שצוין קודם, הרעיון של תכנות מיני-אפליקציות הוא מודל-תצוגה-תצוגת מודל (MVVM). שכבת הלוגיקה ושכבת התצוגה פועלות בשרשורים שונים, ולכן ממשק המשתמש לא נחסם על ידי פעולות שפועלות לאורך זמן. במונחים של אינטרנט, אפשר לחשוב על סקריפטים שפועלים ב-Web Worker.

שפת הסקריפטים של WeChat נקראת WXS, של Alipay נקראת SJS ושל ByteDance נקראת SJS. ‫Baidu מדברת על JS כשהיא מתייחסת ל-JS שלה. צריך לכלול את הסקריפטים האלה באמצעות תג מיוחד, למשל <wxs> ב-WeChat. לעומת זאת, באפליקציה מהירה נעשה שימוש בתגי <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 bridge API

גשר JavaScript שמקשר בין מיני-אפליקציות למערכת ההפעלה מאפשר להשתמש ביכולות של מערכת ההפעלה (ראו גישה לתכונות מתקדמות). בנוסף, יש בו מספר שיטות נוחות. כדי לקבל סקירה כללית, אפשר לעיין בממשקי ה-API השונים של WeChat,‏ Alipay,‏ Baidu,‏ ByteDance ו-Quick App.

זיהוי התכונות הוא פשוט, כי כל הפלטפורמות מספקות שיטה (שנקראת בדיוק כך) canIUse() שהשם שלה כנראה קיבל השראה מהאתר caniuse.com. לדוגמה, tt.canIUse() של ByteDance מאפשרת בדיקות תמיכה בממשקי API, בשיטות, בפרמטרים, באפשרויות, ברכיבים ובמאפיינים.

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

עדכונים

לאפליקציות מיני אין מנגנון עדכון סטנדרטי (דיון בנושא סטנדרטיזציה פוטנציאלית). לכל הפלטפורמות של מיני-אפליקציות יש מערכת קצה עורפי, שמאפשרת למפתחים להעלות גרסאות חדשות של המיני-אפליקציות שלהם. אפליקציית-על משתמשת במערכת העורפית הזו כדי לבדוק אם יש עדכונים ולהוריד אותם. חלק מאפליקציות הסופר מבצעות עדכונים ברקע, בלי שאפליקציית המיני עצמה תוכל להשפיע על תהליך העדכון. באפליקציות-על אחרות, לאפליקציות המיני יש יותר שליטה.

כדוגמה לתהליך מורכב, בפסקאות הבאות מתואר בפירוט מנגנון העדכון של WeChat למיני אפליקציות. מערכת WeChat בודקת אם יש עדכונים זמינים בשני התרחישים הבאים:

  1. אפליקציית WeChat תבדוק באופן קבוע אם יש עדכונים לאפליקציות מיני שהשתמשתם בהן לאחרונה, כל עוד אפליקציית WeChat פועלת. אם נמצא עדכון, הוא יורד ומוחל באופן סינכרוני בפעם הבאה שהמשתמש מפעיל את המיני-אפליקציה בהפעלה קרה. הפעלה קרה של מיני-אפליקציה מתרחשת כשהמיני-אפליקציה לא פעלה כשהמשתמש פתח אותה (WeChat סוגרת מיני-אפליקציות בכוח אחרי שהן פועלות ברקע במשך 5 דקות).
  2. בנוסף, WeChat בודקת אם יש עדכונים כשמפעילים מיני-אפליקציה בהפעלה קרה. באפליקציות מיני שהמשתמש לא פתח במשך זמן רב, המערכת בודקת אם יש עדכון ומורידה אותו באופן סינכרוני. בזמן שהעדכון יורד, המשתמש צריך לחכות. בסיום ההורדה, העדכון יופעל והמיני-אפליקציה תיפתח. אם ההורדה נכשלת, למשל בגלל חיבור רשת לא טוב, האפליקציה הקטנה תיפתח בכל מקרה. באפליקציות מיני שהמשתמש פתח לאחרונה, כל עדכון פוטנציאלי מורד באופן אסינכרוני ברקע ויוחל בפעם הבאה שהמשתמש יפעיל את אפליקציית המיני.

אפליקציות מיני יכולות להצטרף לעדכונים קודמים באמצעות UpdateManager API. הוא כולל את הפונקציות הבאות:

  • הודעה לאפליקציה המיניאטורית כשמתבצעת בדיקה של עדכונים. (onCheckForUpdate)
  • הודעה לאפליקציה המיני כשהורדה עדכון והוא זמין. (onUpdateReady)
  • הודעה לאפליקציה המינימלית על כך שלא ניתן להוריד עדכון. (onUpdateFailed)
  • ההרשאה מאפשרת לאפליקציה המיני להחיל עדכון זמין, מה שיגרום להפעלה מחדש של האפליקציה. (applyUpdate)

מערכת ה-Backend של WeChat מספקת גם אפשרויות נוספות להתאמה אישית של עדכונים למפתחי מיני-אפליקציות: ‫1. אפשרות אחת מאפשרת למפתחים לבטל את ההסכמה לעדכונים סינכרוניים למשתמשים שכבר התקינו גרסה מינימלית מסוימת של המיני-אפליקציה, ובמקום זאת לאלץ את העדכונים להיות אסינכרוניים. 2. אפשרות נוספת מאפשרת למפתחים להגדיר גרסה מינימלית נדרשת של המיני-אפליקציה שלהם. כך, עדכונים אסינכרוניים מגרסה נמוכה מהגרסה המינימלית הנדרשת יגרמו לטעינה מחדש של המיני-אפליקציה אחרי החלת העדכון. בנוסף, אם ההורדה של העדכון תיכשל, לא תהיה אפשרות לפתוח גרסה ישנה יותר של המיני-אפליקציה.

תודות

המאמר הזה נבדק על ידי Joe Medley, Kayce Basques, Milica Mihajlija, Alan Kent, ו-Keith Gu.