Boost Fast App Development: Interface Encapsulation, Global APIs, and Performance Tweaks
This article shares practical techniques for fast app projects, covering environment setup, API encapsulation with async/await support, global API mounting, rpk size reduction, callback‑to‑Promise conversion, tabs lazy‑loading, and a custom PV tracking SDK to improve maintainability and performance.
Development Environment
macOS 10.14.2, Node v10.15.0, hap‑toolkit 0.2.1, VSCode.
Interface Encapsulation
To handle frequent API calls we create a reusable reqMethod that supports async/await, easy configuration, unified error handling, and consistent naming.
import req from './reqMethod.js';
const baseUrl = 'https://www.example.com';
function reqMethod() {
let arg = Array.from(arguments);
arg.splice(3, 0, baseUrl);
return req.apply(null, arg);
}
export const getBrandmenus = params => reqMethod('GET', `/api/path`, params);
export const editNickname = params => reqMethod('GET', '/api/path', params);
export const addFavouriteCar = params => reqMethod('GET', '/api/path', params);
export const delFavouriteCar = params => reqMethod('GET', '/api/path', params);Using const guarantees unique API names across developers and centralizes configuration in api.js .
Mounting APIs on the Global Object
const injectRef = Object.getPrototypeOf(global) || global;
import * as api from './api.js';
injectRef.API = api;Pages can now call await API.getBrandmenus({sessionid: deviceInfo.deviceId}) directly.
reqMethod Implementation
import fetch from '@system.fetch';
import prompt from '@system.prompt';
function reqMethod(method, url, params = {}, baseUrl, stateDetection = true, showPrompt = true) {
fixXiaomiParamsBug(params);
url = /^http/.test(url) ? url : baseUrl + url;
params = sign(params);
return request(method, url, params, stateDetection, showPrompt);
}
function request(method, url, params, stateDetection = true, showPrompt = true) {
return new Promise((resolve, reject) => {
fetch.fetch({url, data: params, method, success: (res) => {
try {
if (res.code !== 200) { prompt.showToast({message: '网络错误'}); reject(res); return; }
const data = JSON.parse(res.data);
if (data.returncode === 0) { resolve(data); return; }
if (!stateDetection) { resolve(data); return; }
if (showPrompt) { prompt.showToast({message: data.message}); }
reject(res);
} catch (e) { reject(res); }
}, fail: (res) => { prompt.showToast({message: '网络错误'}); reject(res); } });
});
}
function fixXiaomiParamsBug(params) {
if (typeof params === 'object') {
for (let key in params) {
if (typeof params[key] === 'number') {
const x = String(params[key]).indexOf('.') + 1;
const y = String(params[key]).length - x;
if (y > 3) params[key] = String(params[key]);
}
}
}
}
function sign(obj) { /* encryption logic */ }
export default reqMethod;Reducing RPK Package Size
Duplicate imports inflate the RPK. By mounting common utilities globally, each file is included only once.
const injectRef = Object.getPrototypeOf(global) || global;
import * as Utils from './utils/index.js';
const { api } = Utils;
injectRef.UTILS = Utils;
injectRef.API = api;Usage in a page:
export default {
async getDataList() {
try {
const res = await API.getBrandmenus({sessionid: deviceInfo.deviceId});
this.list = res.result.map(item => {
item.date = UTILS.Formate(item.date, 'YYYY-MM-DD');
return item;
});
} catch (e) { console.log(e); }
}
}Callback → Promise Conversion
System APIs often use callbacks, leading to callback hell. A generic promiseFactory wraps them into promises.
import storage from '@system.storage';
import device from '@system.device';
import network from '@system.network';
import geolocation from '@system.geolocation';
const promiseFactory = (pointer, params = {}) => new Promise((resolve, reject) => {
params = Object.assign({
success: data => resolve(data),
fail: (err, code) => reject(err, code)
}, params);
pointer(params);
});
export const getDeviceInfo = () => promiseFactory(device.getInfo);
export const getDeviceId = () => promiseFactory(device.getId, {type: ['device','mac']});
export const getStorage = key => promiseFactory(storage.get, {key});
export const setStorage = (key, value) => promiseFactory(storage.set, {key, value});
export const getNetworkType = () => promiseFactory(network.getType);
export const getLocation = () => promiseFactory(geolocation.getLocation, {timeout: 3000});Business code can now use await promiseApi.getDeviceId() without nesting callbacks.
Tabs Optimization
Default tabs render all pages eagerly, causing UI lag. The optimized version lazy‑loads content, caches rendered tabs, and tracks PV per channel.
<import name="original-tab" src="./original.ux"></import>
<import name="recommend-tab" src="./recommend.ux"></import>
<import name="chejiahao-tab" src="./chejiahao.ux"></import>
<template>
<div class="container">
<tabs onchange="onChangeTabIndex" index="{{currentindex}}" class="tab">
<tab-bar class="tab-header" mode="scrollable">
<stack class="tab-header__item" for="{{tabHeadList}}" @click="clickTabBar($idx)">
<text class="tab-header__text {{currentindex == $idx ? 'tab-header__text--active' : ''}}">{{$item.title}}</text>
<div class="tab-header__line {{currentindex == $idx ? 'tab-header__line--active' : ''}}"></div>
</stack>
</tab-bar>
<tab-content class="tab-content">
<div class="tab-content-section"><trend-tab page-show="{{tabHeadList[0].isShow}}"></trend-tab></div>
<div class="tab-content-section"><recommend-tab page-show="{{tabHeadList[1].isShow}}"></recommend-tab></div>
<div class="tab-content-section"><original-tab page-show="{{tabHeadList[2].isShow}}"></original-tab></div>
</tab-content>
</tabs>
</div>
</template>
<script>
export default {
data() {
return {
tabHeadList: [
{title: '热榜', pv: 'hotlist', isShow: false},
{title: '推荐', pv: 'recommend', isShow: false},
{title: '原创', pv: 'original', isShow: false}
],
currentindex: -1
};
},
onShow() { this.watchCurrentIndexChange(this.currentindex, null); },
onHide() { this.watchCurrentIndexChange(null, this.currentindex); },
async onInit() { this.$watch('currentindex', 'watchCurrentIndexChange'); this.currentindex = 1; },
watchCurrentIndexChange(newVal, oldVal) {
if (oldVal !== null && oldVal >= 0) {
this.tabHeadList.splice(oldVal, 1, Object.assign({}, this.tabHeadList[oldVal], {isShow: false}));
PV_TRACK.endTime(this.tabHeadList[oldVal].pv);
}
if (newVal !== null && oldVal >= 0) {
this.tabHeadList.splice(newVal, 1, Object.assign({}, this.tabHeadList[newVal], {isShow: true}));
PV_TRACK.startTime(this.tabHeadList[newVal].pv);
}
},
clickTabBar(index) { this.currentindex = index; },
onChangeTabIndex(evt) { this.currentindex = evt.index; }
};
</script>PV_TRACK Design
A lightweight statistics SDK for fast apps. It records page start/end times, page_show/page_hide shortcuts, and click events.
// Page start/end
onShow() { PV_TRACK.startTime('pageId', this.auto_open_from); }
onHide() { PV_TRACK.endTime('pageId', this.auto_open_from); }
// Shortcut page_show/page_hide (v2.0)
onShow() { PV_TRACK.page_show(this, {uid: 'extra'}); }
onHide() { PV_TRACK.page_hide(this, {uid: 'extra'}); }
// Click tracking
PV_TRACK.click('pageId', 'buttonClick');Device Information Extraction
Expensive device‑info calls are centralized to avoid duplicate prompts and to reuse data across modules.
const { PV, getDeviceData } = Utils;
const pv = new PV();
injectRef.PV_TRACK = pv;
let deviceData = null;
let deviceCompleteArray = [];
export default {
onCreate() {
pv.send({cate: 'clt', event: 'kuai_app_launch_clt'});
getDeviceData().then(res => {
deviceData = res;
pv.setInfo(res);
while (deviceCompleteArray.length) {
const resolve = deviceCompleteArray.shift();
resolve(res);
}
}).catch(err => console.log(err));
},
getDeviceInfo() {
return new Promise((resolve) => {
if (!deviceData) deviceCompleteArray.push(resolve);
else resolve(deviceData);
});
}
};Business code can await this.$app.$def.getDeviceInfo() before proceeding with other logic.
Conclusion
Applying these encapsulation patterns, global API mounting, lazy‑loading tabs, promise‑based wrappers, and a unified PV tracking SDK improves code maintainability, reduces bundle size, and streamlines performance monitoring for fast app projects.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
