# 启动时延优化

# 避免setTimeout延迟

logo页如非必要,在执行页面跳转时,不要增加setTimeout延迟跳转。如果是需要等待异步结果返回,例如获取storage后决定跳转的下一个页面,建议将异步方法封装成同步,使用await,等待结果返回后立即执行跳转。以storage为例:

// ❌不推荐写法
onInit(){
  this.checkifHome()
  setTimeout(() => {
    if(!this.ifHome){
      router.push({uri:'pages/home'})
    }
  },1000)
}
checkifHome(){
  const that = this 
  storage.get({
    key: 'ifHome',
    success: function(data) {
      that.ifHome = data
    },
    fail: function(data, code) {
      console.log(`handling fail, code = ${code}`)
    }
  })
}
// ✅推荐写法一
onInit(){
  storage.get({
    key: 'ifHome',
    success: function(data) {
      if(!data){
        router.push({uri:'pages/home'})
      }
    },
    fail: function(data, code) {
      console.log(`handling fail, code = ${code}`)
    }
  })
}
// ✅推荐写法二
async onInit(){
  const ifHome = await checkifHome()
  if(!ifHome){
    router.push({uri:'pages/home'})
  }
}
checkifHome(){
  return new Promise((resolve, reject) => {
    storage.get({
      key: 'ifHome',
      success: function(data) {
        resolve(data) 
      },
      fail: function(data, code) {
        console.log(`handling fail, code = ${code}`)
        reject(code)
      }
    })
  })
}
// ✅推荐写法三
//可统一封装promise.js,方便其他异步接口复用
export function promisify(fn) {
  if (typeof fn !== 'function') {
    throw Error('[promisify] the type of `fn` should be function');
  }

  return (opts={}) => {
    let { success, fail, complete, ...args } = opts;

    if (typeof success === 'function' || typeof fail === 'function' || typeof complete === 'function') {
      console.warn('[promisify] [WARN] The `success`, `fail` and `complete` callback will be ignored');
    }

    return new Promise((resolve, reject) => {
      try {
        fn({
          ...args,
          success: data => resolve(data),
          fail: (data, code) => {
            let err = new Error(data);
            err.code = code;
            reject(err);
          }
        });
      } catch (error) {
        reject(error)
      }
    })
  }
}

//统一封装storage方法
import storage from '@system.storage';
import {promisify} from './promise';

const _get = promisify(storage.get);
const _set = promisify(storage.set);
const _clear = promisify(storage.clear);
const _delete = promisify(storage.delete);
export default {
  getItem(key) {
    return _get({key});
  },

  setItem(key, value) {
    return _set({key, value});
  },

  deleteItem(key) {
    return _delete({key});
  },

  clear() {
    return _clear();
  },
}

//logo.ux
async onInit(){
   const ifHome = await storage.getItem('ifHome')
  if(!ifHome){
     router.push({uri:'pages/home'})
  }
}

# 首页数据缓存

首页数据如果二次进入,需要再次展示的,可以考虑在应用(或首页)退出时增加上缓存,下次进入从logo页读取缓存后将数据存储在全局,首页page在onInit时直接读取,然后同时发起异步请求进行更新即可;

# logo页避免http请求

建议不要在logo页引入http请求,尽可能放到首页执行,防止弱网或者无网情况阻塞页面跳转;

# UI先行

如音乐类应用,进入应用建议默认状态为不播放,可以UI先行,如果歌曲信息获取成功立即展示,无需等到audio资源加载完成展示;

# 隐私页信息使用静态数据

隐私页的数据代码里使用静态的数据,不用动态获取。需要展示长文本的,可以通过二维码扫码查看,二维码直接本地写死一个h5链接,不要通过接口去获取;

# 减少从console打印

尽可能减少console打印,特别是长日志,很影响性能,避免很长的(>10行)console打印,尽可能减少json对象的打印,如果是debug期间需要打印日志,建议使用console.debug,并且配置quickapp.config.js(具体配置如下),在打release包的时候过滤掉console.debug的日志;

const TerserPlugin = require("terser-webpack-plugin")
const webpack = require("webpack")

module.exports = {
  postHook: (config) => {
    if (config.mode === "production") {
      config.optimization.minimize = true
      config.optimization.minimizer = [
        new TerserPlugin({
          terserOptions: {
            compress: {
              pure_funcs: ["console.debug"]
            }
          }
        })
      ]
    }
  }
}

# 图片缓存/裁剪

如果有较大的(>100kb)动态图片,建议首次加载增加loading页,下载并缓存到本地,后续通过internal://files/XXX.png加载(重要:一般非必要不建议引入在线大图,引入的大图尺寸也不要超过屏幕尺寸,且大小不超过200kb,尽量使用本地图片代替在线图片,或者在线图片里支持resize-尺寸裁剪)

//login.ux
export function downloadFile(url) {// 下载图片
  return new Promise((resolve, reject) => {
    if(!url){
      resolve('')
    }
    request.download({
      url,
      success: function (ret) {
        const token = ret.token
        request.onDownloadComplete({
          token: token,
          success: function (ret) {
            console.info(`### request.download ### ret`,ret)
            resolve(ret.uri)
          },
          fail: function (msg, code) {
            console.info(`### request.onDownloadComplete ### ${code}: ${msg}`)
            resolve(null)
          }
        })
      }
    })
  })
}
const formUrl = 'http://XXX.cdn.homeBg.png'
downloadFile(formUrl).then(url => {
  global.homeBgUrl = url; //url => 'internal://files/homeBg.png'
})
 
//home.ux
<image class="w-466 h-466" src="{{bgImage}}" alt="../../common/images/homeBg.png"></image>
//....
  computed:{
    bgImage() {
      const img =  global.homeBgUrl || 'http://XXX.cdn.homeBg.png'
      return img
    }
  }
//....
 
 //logo页
 global.homeBgUrl = await storage.getItem('homeBgUrl')
 
 //根据条件变化,及时进行图片清理
 logoOut(){
   file.delete({
    uri:global.homeBgUrl,
    success: function(data) {
      console.info(`###delFile sucess ${data}`)
      resolve(true)
    },
    fail: function(data, code) {
      resolve(false)
      console.log(`###delFile fail, code = ${code}`)
    }
  })
}

# 通信类应用通信之前使用diagnosis方法判断连接状态

使用interconnect实现手表app和手机app的通信时,摒弃之前的轮询调用getApkStatus方法,改用新api diagnosis

data: {
   status: '',
   connectNum: 3,
   conn: null
},
onInit() {
   this.conn = interconnect.instance();
   this.connectStatus();
}, 
// ❌ 不推荐写法
connectStatus() {
  let status = this.conn.getApkStatus();
  if (status === 'CONNECTED' || this.connectNum === 0){
    this.status = status;
    // do something
  } else if (this.connectNum > 0) {
    this.connectNum --;
    setTimeout(() => {
      this.connectStatus()
    },500)
  }
}
// ✅推荐写法
connectStatus() {
  this.conn.diagnosis({
    success: (data) => {
      console.log(`handling success, status= ${data.status}`)
      // do something
    },
    fail: (data,code) => {
      console.log(`handling fail, code = ${code}`)
      // do something
    }
  })
}  

# 使用interconnect传输多条数据

手表app向手机app传输多条数据时,若传输数量不大,建议直接一次性发送,无需增加延迟发送

// ❌不推荐写法
sendMsg(list) {
  for (let x in list) { 
    setTimeout(() => {
      this.conn.send({
        data: list[x],
        success: ()=>{ },
        fail: (data: {data, code})=> { }
      })
    },x*500) 
  }
}
// ✅推荐写法
sendMsg(list) {
  for (let x in list) {            
    this.conn.send({
      data: list[x],
      success: ()=>{ },
      fail: (data: {data, code})=> { }
    })
  }
}