微信小程序組件化實踐

Do Not Repeat Yourself小程序

如何提升代碼質量,方法有許多:抽象、模塊、組件化,我認爲它們的中心點都是——Do Not Repeat Yourself.微信小程序

小程序組件化

咱們先看看小程序自帶的組件化能力:微信

  • 模板,經過 <import src="../name.wxml" /> 引入 ---- success
  • 樣式,經過 @import 'name.wxss' 引入 ---- success
  • js ??? ---- fail

咱們看到,微信小程序開發有一個很大的痛點,缺少組件化能力,特別是在沒有使用構建工具或第三方框架的狀況下,稍不留神,就會充滿大量重複的代碼。框架

小程序提供了自定義組件( V1.6.3 才支持)xss

舉個栗子。ide

小程序雖然提供了一些經常使用的組件——toast、loading等,但因需求不一樣,咱們要實現本身的 popupnotifymodal等公共組件。這些自定義組件的共同點是:單個組件模板、事件大體同樣,區別是每次使用組件時顯示的內容不一樣。因而,咱們在每一個頁面都能看到類似的代碼,emm,已經產生了臭味道。函數

mixins

經過混入的方式,將組件方法、數據綁定到頁面實例上,相似VUE mixins,不過,這裏實現要更爲簡單:工具

function mixins(page, ...args) {
  let data = args.reduce((res, item) => Object.assign(res, item.data), page.data || {})

  Object.assign(page, ...args)
  page.setData(data)
}

這樣,在頁面onLoad時,執行mixins(this, Component1, ...)就能夠了。組件化

寫一個popup

如今開始寫一個 popup組件,設計給的樣式以下圖:
佈局

除去佈局和樣式,應該如何設計數據模型?我的認爲須要把握兩點:職責分離、易於擴展。

第一步,分析哪些數據是不變的、可變的。

可變數據:標題、內容、按鈕文字、按鈕個數、點擊按鈕後續操做、蒙層是否可點
不變數據:點擊蒙層、取消按鈕隱藏popup,顯示/隱藏popup事件

第二步,設計數據模型。

經過第一步的分析,咱們能夠拋開具體的業務邏輯,設計出popup的數據模型:

let popupModal = {
  title: '',
  content: '',
  confirmButtonName: '確認',
  cancelButtonName: '取消',

  show(),
  hide(),
  onConfirm(),
  onCancel(),
}

使用面向對象能夠輕鬆分離可變、不可變數據,生成所需數據模型:

class GeneratePopupModal {
  constructor(options) {
    this.setParams(options)
  }

  setParams({
    title,
    content,
    confirmButtonName,
    cancelButtonName,
    success,
  } = {}) {
    this.title = title || this.title
    this.content = content || this.content
    this.confirmButtonName = confirmButtonName || this.confirmButtonName
    this.cancelButtonName = cancelButtonName || this.cancelButtonName

    this.success = success
  }
  show() {
    return Promise.resolve(this.__show = true)
  }
  hide() {
    return Promise.resolve(this.__show = false)
  }
  onConfirm() {
    return this.hide()
      .then(() => {
        if(this.success) return this.success({confirm: true})
      })
  }
  onCancel() {
    return this.hide()
      .then(() => {
        if(this.success) return this.success({cancel: true})
      })
  }
}

第三步,生成數據渲染組件。

爲了使popup組件保持簡單,只對外暴露三個方法:

  • $popup(opts) ---- 根據參數生成組件數據並顯示 popup
  • tapPopupConfirmButton() ---- 點擊 popup 確認按鈕
  • tapPopupCancelButton() ---- 點擊 popup 取消按鈕
const GeneratePopupModal = require('./generatePopupModal')
const defalut = {
  title: '管家提示',
  content: '抱歉,咱們遇到一點問題,請稍後再試',
  confirmButtonName: '確認'
}

let Popup = null

module.exports = {
  $popup(options={}) {
    /**
     * 每次使用新建實例,避免狀態共享
     * 經過私有 __show 控制 popup.show,實現組件 popup 的顯示和隱藏
     */
    Popup = new GeneratePopupModal(defalut)

    Object.defineProperty(Popup, '__show', {
      configurable: true,
      set: v => {
        Popup.show = v
        this.setData({popupModal: Popup})
      }
    })
    return new Promise((resolve, reject) => {
      Popup.setParams(
        Object.assign({}, {success: resolve, fail: reject}, options)
      )
      Popup.__show = true
    })
  },
  tapPopupConfirmButton() {
    Popup.onConfirm()
  },
  tapPopupCancelButton() {
    Popup.onCancel()
  },
}

第四步,引入、使用組件

在上面mixins部分,介紹瞭如何引入組件。那麼,怎麼使用它呢?這裏提供一個栗子:

this.$popup({ // 根據傳參生成數據、顯示 popup
  title: '刪除行程',
  content: '確認刪除此行程?刪除後將不可恢復。',
  confirmButtonName: '刪除',
  cancelButtonName: '我再想一想',
}).then(({confirm, cancel}) => { // 分別表示點擊了肯定、取消按鈕
  if(confirm) { // 點擊刪除(肯定)按鈕後續操做
    return r4251({
      id: item.productid,
      type: item.type
    })
    .then(res => this.__fetchCartList())
    .catch(e => {
      this.$popup() // 顯示默認數據的 popup

      console.error('[cart/list.js] #tapDeleteCart r4251 error: ', e)
    })
  }
})

總結

至此,popup組件基本完成了,但這種方式依然存在很大的漏洞:

  • mixins 方式混入組件數據/方法,極易形成命名衝突。經過命名空間解決
  • 若是組件有 data 對象,那麼頁面銷燬(unLoad)後,頁面數據依然常駐內存。經過導出函數對象解決

雖然上面的解決方案並不十分理想~難道不是很是彆扭?~,但好在足夠簡潔、實用,也基本實現了設計預期。

期待更好的實現思路......

.

相關文章
相關標籤/搜索