用戶
 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

掃一掃,登錄網站

小程序社區 首頁 教程 查看內容

曼联vs汉诺威96:小程序??榛?

汉诺威96升级 www.wikuss.com.cn Rolan 2019-10-8 00:10

原本需要 24 個工作日才能完成的任務,我卻在 0.5 個工作日內完成了,是如何實現的呢?趁著放假有空,寫篇文章給大家分享一下我們小程序??榛鈾儐钅靠⒌乃悸釩?。閱讀基?。河行〕絳螄钅烤?,有查閱官方文檔習 ...

原本需要 24 個工作日才能完成的任務,我卻在 0.5 個工作日內完成了,是如何實現的呢?趁著放假有空,寫篇文章給大家分享一下我們汉诺威96升级??榛鈾儐钅靠⒌乃悸釩?。

閱讀基?。河行〕絳螄钅烤?,有查閱官方文檔習慣的小伙伴

隨著公司小程序項目日益繁多,僅僅靠著官方提供的框架、組件、API,已經遠遠不能滿足項目高效迭代的要求了,于是我們組內萌生了對小程序進行??榛南敕?。

實際項目中我們對小程序??榛丫婕案鞲瞿??,我總結一下,從三個方向跟大家分享我們不一樣的??榛悸罰?Page+ , basePage , 適配層 。

Page+

Page() 作為頁面的入口,我們可以通過對其入參對象的封裝實現:生命周期的改造、全局狀態管理和新增頁面功能。

官方刪除了小程序分享回調 complete,一起來嘗試將其恢復吧。一般我們的邏輯是這樣的:

// pages/index/index.js
Page({
  // 數據初始化
  data: {
    shareFlag: false,        //頁面是否處于分享中
    shareComplete: false     //分享回調事件
  },
  // onShow 生命周期
  onShow: function () {
    const { shareFlag, shareComplete } = this.data
    if( shareFlag ){
      this.data.shareFlag = false    //變量不涉及頁面渲染,不使用 setData
      shareComplete && shareComplete()
    }
  },
  // 分享事件
  onShareAppMessage: function () {
    let shareInfo = {
      title: '分享測試標題',
      path: '',
      complete: function () {
        console.log('頁面分享成功啦~')
      }
    }
    this.data.shareFlag = true
    this.data.shareComplete = typeof (shareInfo.complete) == 'function' ? shareInfo.complete : false
    return shareInfo
  }
})

在單頁面內實現分享回調這樣操作是可行的,如果多頁面、多項目都要實現該功能,重復拷貝代碼,則顯格外得繁瑣。

我們來將這個功能抽離封裝一下吧。

// pages/index/index.js
import PagePlus from './pagePlus.js'
PagePlus({
  // 分享事件
  onShareAppMessage: function () {
    return {
      title: '分享測試標題',
      path: '',
      complete: function () {
        console.log('頁面分享成功啦~')
      }
    }
  }
})
// pages/index/pagePlus.js
const PagePlus = (pageObj) => {
  const _onShow = pageObj.onShow,
    _onShareAppMessage = pageObj.onShareAppMessage,
    _data = {
      shareStatus: false,   //頁面是否處于分享中
      shareComplete: false  //分享回調事件
    }
  Object.assign(_data, pageObj.data)
  delete pageObj.data
  pageObj.onShow = function () {
    typeof _onShow == 'function' && _onShow.apply(this)
    const { shareStatus, shareComplete } = this.data
    if (shareStatus) {
      this.data.shareStatus = false //變量不涉及頁面渲染,不使用 setData
      shareComplete && shareComplete()
    }
  }
  pageObj.onShareAppMessage = function () {
    const shareInfo = typeof _onShareAppMessage == 'function' && _onShareAppMessage.apply(this)
    this.data.shareStatus = true
    shareInfo && (this.data.shareComplete = shareInfo.complete)
    return shareInfo
  }
  Page({ data: _data, ...pageObj })
}
export default PagePlus

我們來增加一個新的生命周期回調—— onReshow (頁面非首次顯示回調,常用于詳情頁操作影響上一頁列表數據的場景)。

// pages/index/index.js
import PagePlus from './pagePlus.js'
PagePlus({
  // 監聽頁面非首次顯示
  onReshow: function(){
    console.log('onReshow lifeCallBack')
  },
  onShareAppMessage: function () {
    return {
      title: '分享測試標題',
      path: '',
      complete: function () {
        console.log('頁面分享成功啦~')
      }
    }
  }
})
// pages/index/pagePlus.js
class BasePage{
  data = {
    pagePlus: {
      shareStatus: false,   //頁面是否處于分享中
      shareComplete: false, //分享回調事件
      firstEnter: true      //第一次進入頁面
    }
  }
  methods = {
    onShow: this.onShow,
    onShareAppMessage: this.onShareAppMessage,
    onReshow: this.onReshow
  }
  onShow(){
    const { shareStatus, shareComplete, firstEnter } = this.data.pagePlus
    if (firstEnter) {
      this.data.pagePlus.firstEnter = false
    } else {
      this.onReshow()
    }
    if (shareStatus) {
      this.data.pagePlus.shareStatus = false
      shareComplete && shareComplete()
    }
  }
  onShareAppMessage(shareInfo){
    this.data.pagePlus.shareStatus = true
    shareInfo && (this.data.pagePlus.shareComplete = shareInfo.complete)
  }
}

const PagePlus = (pageObj) => {
  const basePage = new BasePage()
  for (var i in basePage.methods) {
    basePage.methods[i] = (() => {
      const key = i
      const _temFn = basePage.methods[key]
      return function () {
        if (key == 'onShareAppMessage') {
          const shareInfo = typeof pageObj[key] == 'function' && pageObj[key].apply(this, arguments)
          _temFn.apply(this, [shareInfo])
          return shareInfo
        }
        typeof pageObj[key] == 'function' && pageObj[key].apply(this, arguments)
        typeof _temFn == 'function' && _temFn.apply(this, arguments)
      }
    })()
  }
  Object.assign(basePage.data, pageObj.data)
  delete pageObj.data
  Page({ data: basePage.data, ...pageObj, ...basePage.methods })
}

export default PagePlus

自此,我們修改了原生的生命周期回調和增加了新的生命周期回調。當然我們還能為 Page+ 賦予更多的功能,例如:

頁面刷新 :下拉自動刷新當前頁。

定時器自動清除 :離開頁面時,自動清除頁面執行的定時器。

全局狀態管理 :頁面間數據共享,相關數據關聯的組件即時渲染更新。

相關的代碼實現,大家可以自己思考一下怎么實現;我的實現細節,如果大家感興趣的話就在下方給我留言吧,你們的回復是我更新的動力哦。

basePage

小程序頁面彼此獨立,使用 Component 都需要各自引用,為了實現頁面公共 Component 的統一管理,這個時候就可以引入 basePage 的概念:以 basePage 作為父組件,其他公共 Component 作為子組件,頁面通過 basePage 對公共 Component 進行管理。

實現原理

1、定義一個 Component ,作為 basePage 。

2、每個頁面統一引用 basePage ,且規定頁面的元素都需要寫到 <basePage/> 標簽內部 。

3、通過 basePage 引用頁面公共的 Component ,并進行業務邏輯編輯。

實現細節

實際使用過程中,我發現有兩個問題:

1、Page 和 basePage 通信是非常頻繁的,需要通過 WXML 數據綁定和 triggerEvent 觸發事件,略顯麻煩。

2、setTimeout、webSocket 等后臺進程,可能觸發 非當前顯示頁面 的渲染更新,而絕大部分情況,我們只需要 當前顯示頁面 的渲染更新。

針對這兩種場景的優化,我們可以把當前顯示頁面的 basePage 實例對象賦值到 global 的某個具體變量;每當 Page 觸發 show 生命周期回調的時候,我們就對這個變量賦值的實例對象進行更新,這樣我們就可以通過 global 的變量直接操作當前顯示頁面的 basePage 了。

部分代碼示例

{
  "文件路徑": "pages/index/index.json",
  "usingComponents": {
    "basePage": "../../components/basePage/index"
  }
}
<!--pages/index/index.wxml-->
<basePage>
  <!-- 
  頁面元素
  -->
</basePage>
// components/basePage/index.js
Component({
  /**
   * Component 所在頁面的生命周期函數
   */
  pageLifetimes: {
    show: function () {
      global.basePage = this
    },
    hide: function () {
      global.basePage = null
    }
  }
})
{
  "文件路徑": "components/basePage/index.json",
  "說明": "在此處統一引入頁面公共的 Component",
  "component": true,
  "usingComponents": {}
}
<!--components/basePage/index.wxml-->
<slot />

適配層

如果你的項目對代碼后續維護、迭代和可移植性有較高需求,或者需要多項目并行,這個時候通過適配層去調用各個功能??榫拖緣糜任匾?。適配層方面我做的還是比較粗糙的,如果有建議歡迎指出。

適配層的時機

項目不是 bugfix 級別的迭代,都有適配層設計的必要。

如果是 新項目 ,心底不認為自己是“咸魚”而是代碼的“親爹”, 適配層完全可以作為標配 去實現;這就是展現自己對代碼全局觀的時候了,把自己對代碼的理解都用適配層去詮釋吧。

如果是 舊項目迭代 ,在項目排期允許的情況下,盡可能理解原代碼的基本實現細節;對比新的項目是要束手束腳一些,適配層的設計要在 盡可能少改變原有代碼 的情況下進行;如果排期比緊急,適配層的完整實現 可以在幾個版本迭代中逐步實現 。

??檣杓票匭敫吣誥鄣婉詈?/h4>

如果功能??櫚納杓乒謁繕?、耦合復雜,這就意味著適配層將需要做各種兼容,這和適配層設計的初衷背道而馳,不做也罷。

配置文件

如果你的代碼有移植性要求,為這些不同環境準備對應的配置文件吧,配置文件可以通過自制腳手架實現,也可以粗暴地手動替換,在保證盡可能不出錯的情況下實現即可。

功能??櫚娜肟?/p>

所有整合的功能??槎夾枰ü逝洳憬械饔?,適配層就是你的“王之財寶”。

規范 && 文檔

適配層是從代碼的全局考慮,如果是項目是分工完成,項目的開發人員都需要遵守適配層規范進行代碼開發;文檔我一直都認為都是非常必要的,但還是經?;嶁傅?,沒有進行完整的文檔編寫,但我基本會在所有項目成員都能理解適配層的情況下,進行簡單的口頭說明。

因為開心說一些廢話

一次需求迭代中,幾乎涉及手頭上的所有小程序項目;剛好就在需求前的半個月,我們小組完成了對所有項目??榛腦?;雖然需求來得很急,我們還是很完美的實現了。畢竟 ??榛?,每個項目的改造都是獨立的工作量;??榛?,就只有適配層迭代的工作量了 。不過真是辛苦了測試小伙伴,因為對所有項目進行??榛腦?,意味著測試小伙伴對所有項目進行回歸測試,感謝測試小伙伴,比心!

這篇文章,對 Page+ 的具體實現展示比較詳細,感覺對 basePage 和適配層講的都比較偏概念。畢竟這部分內容都和業務邏輯聯系比較緊密,很難抽象深入講解。剛好還有假期還有一段時間,如果自己還有時間就再寫一篇關于最近項目的??榛飾靄?,哈哈。

分享至 : QQ空間
收藏
原作者: cinglong 來自: segmentfault.