Development Practices for the 360 AI Speaker H5 Mobile Application
This article details the development of the 360 AI speaker’s H5 mobile application, covering project environment setup, native‑WebView interaction, custom Chinese font handling, dynamic form input components, and Docker deployment, providing practical front‑end engineering insights and code examples for similar projects.
The "360 AI Speaker" is about to launch, and its mobile H5 application is being built. This article introduces the practical implementation of the H5 portion of the 360 AI Speaker app.
Application Overview
The app consists of four main sections: Content (music, stories, audiobooks, etc.), Skills (pre‑configured voice commands), Scenes (user‑defined commands), and My (user devices and account). The Skills and Scenes sections are implemented with H5.
Project Environment Setup
The front‑end H5 uses a traditional B/S architecture to avoid FOIT/FOUT when loading custom fonts and to leverage server‑side caching. The development stack includes:
ThinkJS – 360’s open‑source Node.js framework
Webpack – front‑end compilation and bundling
Vue.js – component‑based SPA framework (used for parts of the project)
The code structure is:
deploy : deployment scripts and Docker build files
frontend : Webpack entry files, templates, JS and CSS resources
runtime : ThinkJS runtime configuration
src : server‑side source code
view : server‑side templates compiled by Webpack
www : static assets (Webpack‑packed JS, CSS, images, fonts) served via CDN
Static resources are uploaded directly to CDN, so the server does not store any static files.
Native Interaction
1. JS Calls Native
The H5 page defines a bridge object injected by the native app. Example call:
// JS calls Native with parameters
SpkNativeBridge.callNative({action, params, whoCare});Parameters:
action : string indicating the operation the native side should perform
params : JSON object with data for the native side
whoCare : 0 (both iOS & Android), 1 (iOS only), 2 (Android only)
2. Native Calls JS
// Native calls JS with parameters
SpkJSBridge.callJS({action, params, whoAmI});Parameters are analogous to the JS‑to‑Native call, with whoAmI indicating the originating platform.
3. Interaction Example
When a user tries to leave a scene with unsaved changes, the native navigation bar invokes:
window.SpkJSBridge.callJS({
action: "can_back",
params: {},
whoAmI: 1/2
});The H5 page returns:
{
can: false,
target: "prev"
}Based on the can flag, the native side decides whether to show a confirmation dialog. Similar logic is used for the Save button, where H5 calls SpkNativeBridge.callNative with action: "displayRightButton" and parameters describing the button state.
Custom Chinese Font Handling
The Skills list and details require the Adobe open‑source "Source Han Serif" font, which is originally ~23 MB. To reduce payload, a dynamic font‑subsetting service called "Qi Font Library" was built. It provides on‑the‑fly font files (TTF/WOFF/WOFF2/EOT) and corresponding @font-face CSS, shrinking the download size to a few kilobytes.
Typical API response for a CDN URL:
{
"ttf": "//s3.ssl.qhres.com/static/b73305c8dde4d68e.ttf",
"woff": "//s1.ssl.qhres.com/static/e702cca6e68ab80a.woff",
"woff2": "//s1.ssl.qhres.com/static/e27f2a98e5baf04d.woff2",
"eot": "//s2.ssl.qhres.com/static/590b2e87fb74c9d6.eot"
}And a sample @font-face rule:
@font-face {
font-family: myWebFont;
src: url('data:font/opentype;base64,Fg8AA...8AAw==');
src: url('data:font/opentype;base64,Fg8AA...8AAw==?#iefix') format('embedded-opentype'),
url('data:font/opentype;base64,d09GM...') format('woff2'),
url('data:font/opentype;base64,d09GR...') format('woff'),
url('data:font/opentype;base64,AAEAA...') format('truetype');
}The service is internal to 360 and not publicly available, but the approach can be reused for any web project that needs large Chinese fonts.
Form Input Component
The input area uses a contenteditable="true" div to allow rich text entry, with a placeholder and character counter. The component is instantiated via:
import inputComponent from './_replyInputComponent';
new inputComponent({ formsSelector, formTemplate, lengthLimit });Character counting relies on three events:
keyup – fires after a key press
compositionstart – fires when an IME composition begins
compositionend – fires when the IME composition finishes
Because compositionend does not fire for English input, the final counting is bound to keyup as well. To avoid excessive processing, the handler is wrapped with debounce (300 ms):
import debounce from 'lodash.debounce';
const inputHandler = debounce(e => {
// ...process input
}, 300);The article also clarifies the difference between debounce (wait for inactivity) and throttle (limit execution rate).
Docker Deployment
The application is containerized using Docker and deployed on 360’s cloud platform (Stark). The deployment workflow includes building a local image, uploading it to Stark, updating the image version, and restarting the service. The article points to Docker’s official "Get Started" and "Dockerfile reference" documentation for further reading.
Conclusion
This quick‑start guide outlines the essential front‑end practices used in the 360 AI speaker H5 project, from environment setup and native bridging to custom font subsetting, dynamic form components, and containerized deployment, offering a reference for developers facing similar challenges.
360 Tech Engineering
Official tech channel of 360, building the most professional technology aggregation platform for the brand.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.