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.

AutoHome Frontend
AutoHome Frontend
AutoHome Frontend
Boost Fast App Development: Interface Encapsulation, Global APIs, and Performance Tweaks

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Performance Optimizationfast appglobal APIinterface encapsulationpromise conversionpv tracking
AutoHome Frontend
Written by

AutoHome Frontend

AutoHome Frontend Team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.