快應用開發入門

前端一鍋煮發表於2022-05-12
  • 快應用介紹
  • 遇到的問題和解決辦法
  • 幾點開發實踐
  • 一點感悟

快應用介紹

介紹

網頁:網頁無需安裝,體驗不夠很好;

原生應用:原生應用體驗流暢,但需要從應用商店下載安裝,難以一步直達使用者,而且安裝包比較大耗流量;

快應用:深度整合在手機作業系統中,使用者無需下載安裝直接在手機上執行,可流暢的體驗應用內容。

類似微信小程式,微信小程式巢狀在微信 app 裡面,快應用巢狀在手機系統裡面。

入口比較全,包括:URL 連結、全域性搜尋、應用商店、瀏覽器、負一屏、系統桌面、PUSH、語音助手、安全中心、垃圾清理、資訊助手、天氣、簡訊模板、日曆、個性主題、檔案管理...

開發技術

快應用採用前端技術棧,由基本的標籤、樣式、js指令碼等組成,與 Vue 的開發方式相近。

快應用使用 MVVM 的設計模式進行開發,開發者無需直接操作 DOM 節點的增刪,利用資料驅動的方式完成節點更新。

開發實時編譯渲染,完成後打包生成 rpk 包上傳平臺。

快速開始

  1. 安裝快應用開發工具:https://www.quickapp.cn/docCe...
  2. 新建快應用工程

  1. 程式碼結構

  1. 開發除錯

  1. 編譯打包提交測試

  1. 上傳至官網的開發者中心,提交稽核釋出

遇到的問題和解決辦法

缺點

真實開發體驗感受到的問題:

  • 開發工具屬於簡易版的 VS Code,容易卡頓,偶爾會出現詭異的問題
  • api 未全部統一,各家廠商都有部分私有的api和功能
  • 官方文件不全,不夠詳細
  • 更新迭代慢,基礎功能夠用,更細節的功能不夠全
  • 社群不繁榮,較少更新維護
  • 應用量和開發者較少導致文件和相關資料少,遇到問題很難找到可借鑑的解決方案

遇到的問題和解決辦法

1. 開發工具執行卡頓

現象:長時間執行快應用開發工具會變卡頓。

解決:可以退出殺死快應用開發工具,然後重新開啟。

2. 開發工具執行報錯

現象:初次安裝報錯,開發過程報錯,偶發編譯報錯。

解決:檢視右側預覽報錯資訊 => 檢視底部輸出的報錯資訊 => onError 監聽 => 程式碼二分法排查 => 重啟開發工具 => 重啟電腦

說明:

  • 常規明顯的錯誤右側預覽會提示
  • 元件內部報錯整個元件會渲染失敗,可檢視偵錯程式輸出查問題
  • onError 全域性監聽,用 prompt.showDialog 彈出全域性報錯
  • 異常頁面通過二分法依次對半隱藏程式碼找到錯誤
  • 未發現異常或者 $app$def、自定義的 $config 不存在報錯,清除快取重新編譯,不行再重啟開發工具,再不行重啟電腦

3. 生命週期函式執行問題

頁面執行順序 onInit => onReady => onShow

自定義元件只有 onInit => onReady

現象:載入過一次的頁面返回再進入只會執行 onShow,此時頁面中自定義元件生命週期函式不會執行。

解決:加上 if 並在頁面 onShow 中修改為 false,100ms 後為改為 true,強制子元件重新整理。

4. 定位不支援 z-index 控制層級

現象:z-index 無效,多個 position 定位元素互相覆蓋時,誰後面渲染誰就在上層。

解決:用 stack 元件替代,stack 容器子元件排列方式為層疊排列,每個直接子元件按照先後順序依次堆疊,覆蓋前一個子元件;用程式碼邏輯控制要顯示在最上層的最後去渲染。

5. 私有自動登入 api 導致預覽無法使用

現象:使用了 oppo 私有的自動登入 api,模組不存在報錯導致開發者工具右側預覽失敗,無法實時開發除錯。

解決:封裝登入方法,引入引數控制除錯階段用寫死的登入 token,不走私有登入 api。

6. 頁面棧過多資料被銷燬

現象:閱讀頁和詳情頁因為 id 不一樣每次都會產生新的頁面棧,頁面棧的數量超過 5個,快取在記憶體裡的最早頁面資料會被銷燬,一般表現就是首頁會變空白,因為最先進的是首頁。

解決:閱讀頁和詳情頁這種帶 id 的頁面用 router.replace 替換 router.push;監聽頁面棧數量超過 5個記錄一下,在回到首頁的時候重新載入資料。

7. 計時器問題

現象:計時器內函式執行報錯 this 找不到,A頁面跳轉到B,後臺繼續監聽A頁面計時器,到點執行計時器,此時A頁面已被銷燬,報錯。

解決:給 Function 增加 bindPage 方法,在 bindPage 裡面判斷頁面存在才執行函式,所有計時器都用以下方法:

const bindPageLC = () => {
  Function.prototype.bindPage = function (vmInst) {
    const fn = this
    return function () {
      if (!vmInst) {
        throw new Error('使用錯誤:請傳遞VM物件')
      }
      if (vmInst.$valid) {
        return fn.apply(vmInst, arguments)
      } else {
        console.error('頁面銷燬時,不執行回撥函式')
      }
    }
  }
}
bindPageLC()

setTimeout(function() {

}.bindPage(this), 500)

8. 系統字型設定變化導致內容錯位

現象:手動修改系統字型大小,未做適配導致頁面錯位,目前未提供相關 api 能監聽到系統字型大小變化導致。

解決:manifest.json 關閉字型大小響應 "textSizeAdjust": "none"

9. 系統顯示設定變化導致內容錯位

現象:手動修改系統顯示大小,使用富文字容器內嵌html元素的會變大錯位。"textSizeAdjust": "none" 只能關閉不響應系統字型變化。

解決:不使用富文字容器內嵌 html,改用 divtext 等元件。

10. 螢幕適配

快應用和負一屏卡片預設尺寸都是 "designWidth": 750,使用 750 的設計圖可 1:1 寫樣式;

如果 "designWidth": 1080,375 的圖需要乘以 3,可以在公共樣式裡定義變數,@3:3px;

具體使用則:height: 156*@3;padding: 16*@3 16*@3 0;

11. 夜間適配

現象:監聽夜間模式然後變換最外層樣式去更換顏色,發現不起作用,需要每個元素都單獨換樣式才有效。

解決:快應用可以夜間媒體查詢適配 @media (prefers-color-scheme: dark) {}

manifest.json"themeMode": -1, 跟隨主題色,"forceDark": true, 開啟反色

圖片暗夜 src 替換:監聽主題模式切換 onConfigurationChanged,獲取當前的主題模式 configuration.getThemeMode()

元件單獨關閉反色:forcedark="false"

自定義樣式:用媒體查詢適配 @media (prefers-color-scheme: dark) {}

12. 閱讀頁豎翻模式 1.0 廣告問題

現象:多個廣告元件 for 迴圈,繫結 appear 觸發載入,每個元件會多次觸發 appear 事件;廣告元件onLoad回撥會觸發多次。

解決:加索引快取過濾掉已載入過的元件;廣告物件抽離到外部共同引用,傳資料到廣告元件裡面去渲染資料,做資料快取過濾。

13. 2.0 原生廣告注意點

載入:最外層通過 if 重新渲染載入新廣告

成功:返回的資料不再有廣告 id

點選:廣告曝光和點選只會觸發1次,再次點選不會觸發廣告點選上報,無收益;觸發廣告跳轉區域不能用 if 控制,否則點選無任何反應,用 show 控制

關閉:自定義關閉廣告元素用 show,不用 if,這樣就不會跳轉到廣告連結

14. 安全稽核問題

loglevel 需設定為 off

未同意隱私協議前存在聯網行為:所有請求在同意協議後再發;

不允許存在 http 連結:對介面下發的連結全部轉換成 https,介面連結支援 httphttps,部分下發的還是 http

程式碼中不允許存在硬編碼 IP:可拼接使用;

storage 不允許明文儲存敏感資訊,比如暱稱、手機號等:可用 cipher.aes() 加密儲存。

幾點開發實踐

平臺版本

快應用覆蓋式更新,平臺版本可升級到 1100 最新版

錯誤處理

  1. JSON.parse 解析報錯處理
const parseJSON = () => {
  const rawParse = JSON.parse
  JSON.parse = (str, defaults) => {
    try {
      return rawParse(str)
    } catch (err) {
      console.error(`JSON 解析失敗:${str}, ${err.stack}`)
      return defaults
    }
  }
}
  1. 通過 $valid 判斷頁面狀態,解決回撥函式中引用 this 資料包錯
const bindPageLC = () => {
  Function.prototype.bindPage = function (vmInst) {
    const fn = this
    return function () {
      if (!vmInst) {
        throw new Error('使用錯誤:請傳遞VM物件')
      }
      if (vmInst.$valid) {
        return fn.apply(vmInst, arguments)
      } else {
        console.error('頁面銷燬時,不執行回撥函式')
      }
    }
  }
}
  1. 錯誤頁面

監聽 onPageNotFound,當頁面跳轉異常時跳轉到自定義錯誤頁面,加埋點上報

  1. 全域性報錯監聽

監聽onError應用報錯,應用捕獲異常時呼叫,加埋點上報

  1. 後端伺服器介面請求失敗上報

效能優化

1. 全域性掛載公共函式

global.$config 掛載公共靜態配置資料:$config.bpImg

global.$utils 掛載純函式以及部分系統api的封裝:$utils.formatDate()

app.ux 掛載封裝的快應用方法函式:this.$app.$def.login

2. 合理巢狀 html

html 層級不要超過 28 層,否則會警告;標籤要閉合,否則不會報錯但是渲染的判斷邏輯會不起作用,出現詭異 bug

3. 合理使用 css 選擇器

css 樣式巢狀層級越深,單次匹配耗時越長

避免使用元件(比如 text)名稱作為最後一項匹配規則,否則每個 text 元件渲染時都會遍歷匹配一次

從右到左匹配,最後一位樣式名儘量唯一,較少匹配次數

4. 簡化 ViewModel 資料

快應用會對賦值的響應式資料中每個屬性進行遞迴式的定義,屬性個數的定義越少越好

所以針對陣列型別資料,賦值時可過濾掉不需要用到的物件屬性

5. 使用懶載入

list 元件中,不在螢幕之內的 list-item 可以在滑動時載入更多,完成渲染

tabs 元件中,非當前顯示的頁籤內容,可以在使用者點選頁籤時完成渲染(藉助if指令控制tab-content元件的子節點)

資料傳遞

1. 父子元件傳資料

父元件:props <==> 子元件:$emit() 觸發繫結的自定義事件

子元件:$dispatch() 觸發自定義事件 <==> 父元件:$on() 監控自定義事件,向外傳遞

父元件:$broadcast() 觸發自定義事件 <==> 子元件:$on() 監控自定義事件,向內傳遞

2. 兄弟元件傳資料

自己寫一個提供釋出訂閱能力的 JS,然後各個 ViewModel 引入這個JS檔案

或者將其掛載在頁面級別的 ViewModel,子元件通過 $root 引用到頁面級別的 ViewModel

父元件

<script>
  import {
    createOrRetrieveInst
  } from './pubsub.js'

  export default {
    onReady () {
      // 1. 例項化:並繫結在VM上
      this.pubsubModel = createOrRetrieveInst()

      // 2. 訂閱:其它VM也可以呼叫
      this.pubsubModel.subscribe('count-add', function (vArg0, vArg1){ ... })

      // 3. 釋出:其它VM也可以呼叫
      this.pubsubModel.publish('count-add', ['arg0', 'arg1'])
    }
  }
</script>

子元件

<script>
  export default {
    onReady () {
      // 1. 訂閱
      this.$root.pubsubModel.subscribe('count-add', function (vArg0, vArg1){ ... })

      // 2. 釋出
      this.$root.pubsubModel.publish('count-add', ['arg0', 'arg1'])
    }
  }
</script>

3. 跨層級傳資料

app.ux 中:

  onCreate() {
    this.dataCache = {} // 初始化 app 快取的資料
  },
  getAppData(key) { // 獲取 app 快取的資料
    return this.dataCache[key]
  },
  setAppData(key, val) { // 設定 app 快取的資料
    this.dataCache[key] = val
  },

使用:

// 任意頁面設定
this.$app.setAppData('adbooks', JSON.parse(JSON.stringify(adBook)))

// 任意頁面獲取
this.$app.getAppData('adbooks')

快速生成多個快應用

需求:一個快應用為主,衍生出多個換膚、換圖、換問題的快應用

解決:用命令列復制新的配置去覆蓋現有配置再打包,配置項檔案 manifest.jsonvariables.less、公共 config.js

負一屏卡片開發

  1. 新版負一屏卡片整體空間完全自定義,包括 logo、標題、重新整理頻率,每次滑到負一屏就會觸發1次 onShow
  2. 卡片載入有系統 loading,自己不需要再加 loading,否則2個 loading 會衝突,載入中間態要用骨架屏
  3. 快取讀取問題

負一屏卡片要讀取快應用 storage 快取,包名必須一致,不能獨立建包,要把卡片嵌在快應用中,然後在管理平臺建立智慧服務即可

  1. console 檢視
  • 電腦安裝 adb
  • brew cask install android-platform-tools
  • adb logcat -c 清除log快取資訊
  • adb logcat -v time >./log.log 寫入日誌,然後在日誌檔案中找資訊
  1. 夜間適配
  • 必須適配夜間模式
  • 不支援媒體查詢 @media (prefers-color-scheme: dark) {}
  • 根據獲取的當前題模式值去動態修改每個元件樣式

一點感悟

做完一個週期的快應用專案,對比靜態頁面、jq、vue、react、小程式、uni-app、electron以及各種衍生的元件庫

核心還是三個東西,html 頁面結構、css 樣式、js 邏輯:

  • html 頁面結構這塊寫法、優化、注意項都差不多
  • css 寫法也一樣(除了 react 的 css in js 寫法),掌握一套完整的樣式寫法基本能搞定常規業務
  • js 核心邏輯寫法也是一樣的

然後會發現各種專案的腳手架、專案結構、使用方法也都差不多,到最後寫的最多的始終是業務邏輯。

這裡就可以整理出一整套自己的專案開發方法然後持續複用:包括專案結構、檔案命名、html 結構、css 命名和寫法、js 常規方法、api 呼叫方法、說明文件、eslint 規則等配置項等等。

舉其中一個點:

經常會用到的訊息提示彈窗

element:

this.$message({
  message: '這是一條訊息提示',
  type: 'info',
  duration: 3000,
  ...
})

vant:

this.$notify({
  message: '這是一條訊息提示',
  type: 'danger',
  duration: 3000
  ...
})

antd:

message.success({
  content: '這是一條訊息提示',
  duration: 3,
  ...
})
message.loading({
  content: '這是一條訊息提示',
  duration: 3,
  ...
})

微信小程式:

wx.showToast({
  title: '成功',
  icon: 'success',
  duration: 1500
})

uni-app:

uni.showToast({
    title: '這是一條訊息提示',
  icon: 'success',
    duration: 1500
})

快應用:

prompt.showToast({
  message: 'message',
  duration: 0
})

這些框架、元件庫基本用法和原理是類似的,最大的區別在於 api 的命名以及引數的差異,每個都去記住費勁又費記憶。

這裡就可以重新封裝這個 api,比如統一成 $utils.showToast(),這樣始終記住這一個就夠了。

可以封裝的功能包括:各種反饋彈窗、登入退出登出、支付、快取儲存、埋點函式、各框架提供的系統呼叫方法等。

把常用的重新封一遍再用,可以進一步磨平差異,減輕記憶負擔。

我的分享到此完畢,感謝閱讀,謝謝~

相關文章