快應(yīng)用 優(yōu)化技巧

2020-08-08 15:23 更新

了解常用的優(yōu)化手段,提升對(duì)應(yīng)用代碼整理的組織能力,合理拆分功能模塊

通過本節(jié),你將學(xué)會(huì):

教程文檔對(duì)應(yīng)的項(xiàng)目代碼文件:src/Optimization目錄

函數(shù)共享

框架應(yīng)用開發(fā)與傳統(tǒng)的H5頁面開發(fā)之間有一個(gè)顯著的不同點(diǎn):

應(yīng)用是多頁面共享同一個(gè)V8 Context,而H5頁面通常是一個(gè)頁面一個(gè)V8 Context,頁面間無法通訊

因此,在應(yīng)用開發(fā)中,開發(fā)者需要了解頁面與APP之間,頁面與頁面之間的數(shù)據(jù)共享方式。常見的通訊需求如下:

  • 引入 babel 或者 lodash 等類庫,全局暴露供所有頁面使用
  • 實(shí)現(xiàn)頁面間參數(shù)傳遞,尤其是回傳。詳見文檔頁面切換及參數(shù)傳遞

目前,在框架中,可以通過使用框架API實(shí)現(xiàn)數(shù)據(jù)共享

使用框架API

開發(fā)者可以在頁面ViewModel中,通過this.$app.$def獲取APP上定義的數(shù)據(jù)及方法。詳見文檔生命周期

示例如下:

// app.ux中導(dǎo)出的對(duì)象
const appDef = this.$app.$def

    性能優(yōu)化

    性能優(yōu)化包括兩個(gè)大類

    • 通用的解決方案,這類具有普適性
    • 與具體組件結(jié)合的解決方案,如:組件級(jí)別的懶加載,甚至利用組件本身特性完成優(yōu)化

    建議開發(fā)者了解性能優(yōu)化的常見方案,提升應(yīng)用性能

    簡(jiǎn)化ViewModel的數(shù)據(jù)

    ViewModel的定義中,屬性public、protectedprivate 主要承擔(dān)數(shù)據(jù)驅(qū)動(dòng)的數(shù)據(jù)定義與改造功能,會(huì)對(duì)賦值的數(shù)據(jù)中每個(gè)屬性進(jìn)行遞歸式的定義。因此,屬性個(gè)數(shù)的定義越少越好,尤其是數(shù)組類型數(shù)據(jù),建議過濾不需要用到的對(duì)象屬性

    示例如下:

    如果頁面僅需要用到list中每個(gè) item 的 userId,orderName 屬性的話,則僅賦值這兩個(gè)屬性到ViewModel數(shù)據(jù)中,過濾掉非相關(guān)屬性:

    // 模擬fetch請(qǐng)求返回的數(shù)據(jù)
    const orderList = [
      {
        userId: '123',
        orderName: 'XX產(chǎn)品',
        userName: '張三',
        shoppingList: [
          {
            productId: '001',
            productLink: 'http://xxx',
            productShop: {
              ownerId: '2390',
              ownerName: 'XXX店鋪'
            }
          }
        ]
      },
      {
        // ...
      }
    ]
    
    export default {
      data () {
        return {
          list: []
        }
      },
      onInit () {
        // 返回頁面中需要的對(duì)象屬性,過濾無用的對(duì)象屬性
        this.list = orderList.map(item => {
          userId: item.userId,
          orderName: item.orderName
        })
      }
    }

    合理使用后代選擇器

    框架支持 CSS 中的后代選擇、支持 less 預(yù)編譯,方便開發(fā)者開發(fā),提升代碼可維護(hù)性。然而,過多的使用后代選擇器,也會(huì)在節(jié)點(diǎn)匹配上帶來性能損耗,尤其是當(dāng)一個(gè)節(jié)點(diǎn)滿足多個(gè)選擇時(shí)

    優(yōu)化建議如下:

    • 避免使用組件名稱(tag標(biāo)簽名稱)作為后代選擇的最后一項(xiàng)匹配規(guī)則,如: .doc-page #shop text { ... };否則每個(gè)text組件渲染時(shí)都會(huì)遍歷匹配一次
    • 減少后代選擇的層級(jí)數(shù)量,層級(jí)越深,單次匹配耗時(shí)越長(zhǎng),如:.class1 .class2 .class3 .class4 .class5 .class6 { ... }
    • 后代選擇中最后一條匹配規(guī)則的定義名稱盡量唯一,如:.doc-page #shop .shop-item .shop-name-full { ... }

    使用懶加載

    懶加載是一項(xiàng)通用的優(yōu)化手段,傳統(tǒng)H5頁面中的圖片懶加載,指的是頁面即將滾動(dòng)到屏幕可視區(qū)域時(shí),才加載資源,渲染頁面

    在框架中,也可以使用懶加載技術(shù):為了加快頁面可視區(qū)域的渲染,可以通過指令或者事件觸發(fā)等手段推遲不可見區(qū)域的渲染

    例如:

    • list組件中,不在屏幕之內(nèi)的list-item可以在滑動(dòng)時(shí)加載更多,完成渲染。詳見文檔list教程
    • tabs組件中,非當(dāng)前顯示的頁簽內(nèi)容,可以在用戶點(diǎn)擊頁簽時(shí)完成渲染(借助if指令控制tab-content組件的子節(jié)點(diǎn))。詳見文檔tabs教程

    錯(cuò)誤處理

    在框架的開發(fā)中,一旦程序執(zhí)行出錯(cuò),就會(huì)報(bào)出 JS 異常彈框。常見的一些錯(cuò)誤有:

    讀取 undefined 上的屬性

    場(chǎng)景舉例:

    這是 JS 開發(fā)中常見的錯(cuò)誤。對(duì)一個(gè)值為 null 或 undefined 的變量取屬性就會(huì)報(bào)錯(cuò)。例如:

    <!-- a = {}; -->
    <text>{{ a.b.c }}</text>
    <!-- Error: Cannot read property 'c' of undefined -->
    

    解決方案:

    1、&& 方法,通過邏輯運(yùn)算的執(zhí)行順序來規(guī)避錯(cuò)誤。代碼如下:

    <text>{{ a && a.b && a.b.c }}</text>
    

    2、 在 ViewModel 上增加函數(shù)方法

    推薦方案 2,在 ViewModel 上建立一個(gè) checkEmpty 函數(shù)。示例代碼如下:

    export default {
      checkEmpty(...args) {
        let ret
        if (args.length > 0) {
          ret = args.shift()
          let tmp
          while (ret && args.length > 0) {
            tmp = args.shift()
            ret = ret[tmp]
          }
        }
        return ret || false
      }
    }
    

    這樣,就可以方便的調(diào)用了。

    <text>{{checkEmpty(a, 'b', 'c')}}</text>

    JSON.parse()解析出錯(cuò)

    場(chǎng)景舉例:

    代碼執(zhí)行 fetch 請(qǐng)求,請(qǐng)求返回的數(shù)據(jù)默認(rèn)是 JSON 化的字符串了,開發(fā)者使用 JSON 對(duì)象解析,這是正常邏輯;

    但是一旦遇到服務(wù)器端權(quán)限校驗(yàn)失敗等問題時(shí),會(huì)返回類似 503 的 HTML 頁面,此時(shí) JSON 解析肯定就會(huì)失敗

    解決方案:

    1. 在每個(gè)JSON.parse() 的代碼執(zhí)行處進(jìn)行 try-catch 包圍,處理出錯(cuò)情況
    2. 在 app.ux 中提前代理 JSON.parse(),使用 try-catch 包圍,待異常出現(xiàn)時(shí)埋點(diǎn)數(shù)據(jù),或者返回默認(rèn)正常數(shù)據(jù)替代

    推薦方案2,示例代碼如下:

    export function parseProxy () {
      const rawParse = JSON.parse
      JSON.parse = function (str, defaults) {
        try {
          return rawParse(str)
        }
        catch (err) {
          console.error(`JSON解析失?。?{str}, ${err.stack}`)
          return defaults
        }
      }
    }

    回調(diào)函數(shù)中引用ViewModel數(shù)據(jù)報(bào)錯(cuò)

    場(chǎng)景舉例:

    用戶打開 PageA,然后點(diǎn)擊鏈接打開 PageB,PageB 中執(zhí)行接口方法(如 fetch 請(qǐng)求),然后立即返回到 PageA;此時(shí)接口的回調(diào)函數(shù)返回,但 PageB 已經(jīng)出棧銷毀,此時(shí),執(zhí)行開發(fā)者傳遞的回調(diào)函數(shù)報(bào)錯(cuò)

    這是由于,回調(diào)函數(shù)中訪問了一些data數(shù)據(jù)等,而這些ViewModel的數(shù)據(jù)屬性已經(jīng)伴隨著頁面銷毀而刪除了,所以引起報(bào)錯(cuò)

    報(bào)錯(cuò)信息為:undefined:217: TypeError: Cannot read property 'xx' of null

    解決方案:

        1.在回調(diào)函數(shù)執(zhí)行之前,通過ViewModel對(duì)象的$valid、$visible判斷頁面狀態(tài) 

        2.在Function.prototype上定義方法,關(guān)聯(lián)到每個(gè)回調(diào)函數(shù)綁定ViewModel實(shí)例

    推薦方案2,示例代碼如下:

    /**
     * 在Function原型上定義bindPage方法:將回調(diào)函數(shù)綁定到頁面對(duì)象,頁面不可見或者銷毀時(shí),不執(zhí)行回調(diào)函數(shù)
     */
    export function bindPageLC () {
      Function.prototype.bindPage = function (vmInst) {
        const fn = this
        return function () {
          if (!vmInst) {
            throw new Error(`使用錯(cuò)誤:請(qǐng)傳遞VM對(duì)象`)
          }
          if (vmInst.$valid && vmInst.$visible) {
            return fn(...arguments)
          }
          else {
            console.info(`頁面不可見或者銷毀時(shí),不執(zhí)行回調(diào)函數(shù)`)
          }
        }
      }
    }

    ${anyPage}.ux中,通過fn.bindPage(this),在回調(diào)函數(shù)上綁定ViewModel實(shí)例

    export default {
      data () {
        return {}
      },
      request () {
        // 調(diào)用bindPage(this)返回:綁定了頁面對(duì)象的回調(diào)函數(shù),當(dāng)頁面不可見或者銷毀時(shí),不執(zhí)行回調(diào)函數(shù)
        fetch.fetch({
          success: function(ret) {
            // 數(shù)據(jù)操作等
          }.bindPage(this)
        })
      }
    }

    示例代碼詳見Tutorial項(xiàng)目中app.ux文件引入的:util.js

    結(jié)構(gòu)優(yōu)化


    結(jié)構(gòu)優(yōu)化的目的是減小頁面以及整體rpk包的體積,減少冗余代碼

    常用的手段有以下幾項(xiàng):

    整合常用JS庫到app.ux中

    app.ux中引入常用的JS庫,并暴露給每個(gè)頁面使用;可以避免每個(gè)頁面在打包時(shí)對(duì)JS的重復(fù)定義

    總結(jié)


    好的優(yōu)化能夠提升項(xiàng)目的可維護(hù)性,保證頁面渲染的性能,減少不必要的代碼耦合;建議開發(fā)者多多體會(huì)


    以上內(nèi)容是否對(duì)您有幫助:
    在線筆記
    App下載
    App下載

    掃描二維碼

    下載編程獅App

    公眾號(hào)
    微信公眾號(hào)

    編程獅公眾號(hào)