小程序解決方案 Westore - 組件、純組件、插件開發


數據流轉

先上一張圖看清 Westore 怎么解決小程序數據難以管理和維護的問題:

非純組件的話,可以直接省去 triggerEvent 的過程,直接修改 store.data 並且 update,形成縮減版單向數據流。

Github: https://github.com/dntzhang/westore

組件

這里說的組件便是自定義組件,使用原生小程序的開發格式如下:


Component({
  properties: { },

  data: { },

  methods: { }
})

使用 Westore 之后:

import create from '../../utils/create'

create({
  properties: { },

  data: { },

  methods: { }
})

看着差別不大,但是區別:

  • Component 的方式使用 setData 更新視圖
  • create 的方式直接更改 store.data 然后調用 update
  • create 的方式可以使用函數屬性,Component 不可以,如:
export default {
  data: {
    firstName: 'dnt',
    lastName: 'zhang',
    fullName:function(){
      return this.firstName + this.lastName
    }
  }
}

綁定到視圖:

<view>{{fullName}}</view>

小程序 setData 的痛點:

  • 使用 this.data 可以獲取內部數據和屬性值,但不要直接修改它們,應使用 setData 修改
  • setData 編程體驗不好,很多場景直接賦值更加直觀方便
  • setData 卡卡卡慢慢慢,JsCore 和 Webview 數據對象來回傳浪費計算資源和內存資源
  • 組件間通訊或跨頁通訊會把程序搞得亂七八糟,變得極難維護和擴展

沒使用 westore 的時候經常可以看到這樣的代碼:

使用完 westore 之后:

上面兩種方式也可以混合使用。

可以看到,westore 不僅支持直接賦值,而且 this.update 兼容了 this.setData 的語法,但性能大大優於 this.setData,再舉個例子:

this.store.data.motto = 'Hello Westore'
this.store.data.b.arr.push({ name: 'ccc' })
this.update()

等同於

this.update({
  motto:'Hello Westore',
  [`b.arr[${this.store.data.b.arr.length}]`]:{name:'ccc'}
})

這里需要特別強調,雖然 this.update 可以兼容小程序的 this.setData 的方式傳參,但是更加智能,this.update 會先 Diff 然后 setData。原理:

純組件

常見純組件由很多,如 tip、alert、dialog、pager、日歷等,與業務數據無直接耦合關系。
組件的顯示狀態由傳入的 props 決定,與外界的通訊通過內部 triggerEvent 暴露的回調。
triggerEvent 的回調函數可以改變全局狀態,實現單向數據流同步所有狀態給其他兄弟、堂兄、姑姑等組件或者其他頁面。

Westore里可以使用 create({ pure: true }) 創建純組件(當然也可以直接使用 Component),比如 :


import create from '../../utils/create'

create({
  pure : true,
  
  properties: {
    text: {
      type: String,
      value: '',
      observer(newValue, oldValue) { }
    }
  },

  data: {
    privateData: 'privateData'
  },

  ready: function () {
    console.log(this.properties.text)
  },

  methods: {
    onTap: function(){
      this.store.data.privateData = '成功修改 privateData'
      this.update()
      this.triggerEvent('random', {rd:'成功發起單向數據流' + Math.floor( Math.random()*1000)})
    }
  }
})

需要注意的是,加上 pure : true 之后就是純組件,組件的 data 不會被合並到全局的 store.data 上。

組件區分業務組件和純組件,他們的區別如下:

  • 業務組件與業務數據緊耦合,換一個項目可能該組件就用不上,除非非常類似的項目
  • 業務組件通過 store 獲得所需參數,通過更改 store 與外界通訊
  • 業務組件也可以通過 props 獲得所需參數,通過 triggerEvent 與外界通訊
  • 純組件與業務數據無關,可移植和復用
  • 純組件只能通過 props 獲得所需參數,通過 triggerEvent 與外界通訊

大型項目一定會包含純組件、業務組件。通過純組件,可以很好理解單向數據流。

小程序插件

小程序插件是對一組 JS 接口、自定義組件或頁面的封裝,用於嵌入到小程序中使用。插件不能獨立運行,必須嵌入在其他小程序中才能被用戶使用;而第三方小程序在使用插件時,也無法看到插件的代碼。因此,插件適合用來封裝自己的功能或服務,提供給第三方小程序進行展示和使用。

插件開發者可以像開發小程序一樣編寫一個插件並上傳代碼,在插件發布之后,其他小程序方可調用。小程序平台會托管插件代碼,其他小程序調用時,上傳的插件代碼會隨小程序一起下載運行。

插件開發

Westore 提供的目錄如下:

|--components
|--westore	
|--plugin.json	
|--store.js

創建插件:

import create from '../../westore/create-plugin'
import store from '../../store'

//最外層容器節點需要傳入 store,其他組件不傳 store
create(store, {
  properties:{
    authKey:{
      type: String,
      value: ''
    }
  },
  data: { list: [] },
  attached: function () {
    // 可以得到插件上聲明傳遞過來的屬性值
    console.log(this.properties.authKey)
    // 監聽所有變化
    this.store.onChange = (detail) => {
      this.triggerEvent('listChange', detail)
    }
    // 可以在這里發起網絡請求獲取插件的數據
    this.store.data.list = [{
      name: '電視',
      price: 1000
    }, {
      name: '電腦',
      price: 4000
    }, {
      name: '手機',
      price: 3000
    }]

    this.update()

    //同樣也直接和兼容 setData 語法
    this.update(
        { 'list[2].price': 100000 }
    )
  }
})

在你的小程序中使用組件:

<list auth-key="{{authKey}}" bind:listChange="onListChange" />

這里來梳理下小程序自定義組件插件怎么和使用它的小程序通訊:

  • 通過 properties 傳入更新插件,通過 properties 的 observer 來更新插件
  • 通過 store.onChange 收集 data 的所有變更
  • 通過 triggerEvent 來拋事件給使用插件外部的小程序

這么方便簡潔還不趕緊試試 Westore插件開發模板

特別強調

插件內所有組件公用的 store 和插件外小程序的 store 是相互隔離的。

原理

頁面生命周期函數

名稱 描述
onLoad 監聽頁面加載
onShow 監聽頁面顯示
onReady 監聽頁面初次渲染完成
onHide 監聽頁面隱藏
onUnload 監聽頁面卸載

組件生命周期函數

名稱 描述
created 在組件實例進入頁面節點樹時執行,注意此時不能調用 setData
attached 在組件實例進入頁面節點樹時執行
ready 在組件布局完成后執行,此時可以獲取節點信息(使用 SelectorQuery )
moved 在組件實例被移動到節點樹另一個位置時執行
detached 在組件實例被從頁面節點樹移除時執行

由於開發插件時候的組件沒有 this.page,所以 store 是從根組件注入,而且可以在 attached 提前注入:

export default function create(store, option) {
    let opt = store
    if (option) {
        opt = option
        originData = JSON.parse(JSON.stringify(store.data))
        globalStore = store
        globalStore.instances = []
        create.store = globalStore
    }

    const attached = opt.attached
    opt.attached = function () {
        this.store = globalStore
        this.store.data = Object.assign(globalStore.data, opt.data)
        this.setData.call(this, this.store.data)
        globalStore.instances.push(this)
        rewriteUpdate(this)
        attached && attached.call(this)
    }
    Component(opt)
}

總結

  • 組件 - 對 WXML、WXSS 和 JS 的封裝,與業務耦合,可復用,難移植
  • 純組件 - 對 WXML、WXSS 和 JS 的封裝,與業務解耦,可復用,易移植
  • 插件 - 小程序插件是對一組 JS 接口、自定義組件或頁面的封裝,與業務耦合,可復用

Star & Fork 小程序解決方案

https://github.com/dntzhang/westore

License

MIT @dntzhang


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM