告別比較晦氣的2020,迎來嶄新的2021年,希望一切都順利,特別是家人,朋友都能身體健康,健康比一切都重要,沒有健康一切都是浮云。2021年,除了好好工作外,我給自己立個(gè)小目標(biāo),爭取每周一篇原創(chuàng)文章,雖然不能像那些大V一樣把它當(dāng)成一份自由職業(yè),但也要向夢想出發(fā),萬一夢想哪天實(shí)現(xiàn)了呢?
上篇文章介紹了小程序中的自定義組件相關(guān)的內(nèi)容,包括如何定義組件,組件和頁面如何相關(guān)通訊。這篇文章將寫點(diǎn)關(guān)于數(shù)據(jù)共享的一些相關(guān)內(nèi)容。包括全局?jǐn)?shù)據(jù)共享,behaviors和mobix技術(shù)在小程序中的應(yīng)用。
一個(gè)實(shí)例
從一個(gè)實(shí)際例子開始,在下面的一個(gè)小程序中,點(diǎn)擊俱樂部介紹將進(jìn)入俱樂部詳細(xì)介紹頁面,下面是當(dāng)前俱樂部近期活動(dòng)列表。因此有兩個(gè)頁面,他們之間需要共享數(shù)據(jù),第一個(gè)頁面里將獲取到俱樂部的信息,包括俱樂部介紹的內(nèi)容,這個(gè)內(nèi)容將傳遞到第二個(gè)頁面,俱樂部詳細(xì)頁面。

問題一:組件間數(shù)據(jù)共享
這是一個(gè)典型的頁面導(dǎo)航例子,可以有以下方案:
方式一,第一個(gè)頁面中獲取數(shù)據(jù),然后通過在navigator的url中傳遞數(shù)據(jù)到第二個(gè)頁面,這種方式有兩個(gè)問題,第一是url有長度限制,對于數(shù)據(jù)量小的方式可行,但是對于大數(shù)據(jù),會(huì)超過url的長度限制。其二是只能傳遞字符串內(nèi)容,對于對象的傳遞,需要序列化轉(zhuǎn)成json字符串,然后反序列化為對象。因此,這種方式對于我們的場景不太合適。
方式二,使用全局函數(shù)和全局?jǐn)?shù)據(jù),這是小程序提供的跨組件數(shù)據(jù)交互的一個(gè)方式,使用方式也比較簡單,就是在app.js里定義全局函數(shù)和全局變量,在需要的頁面里使用getApp來獲取全局對象。這種方式個(gè)人覺得比較適合于存儲(chǔ)一些與業(yè)務(wù)無關(guān)的對象,而且每個(gè)頁面都需要的全局對象,比如用戶身份信息,系統(tǒng)初始數(shù)據(jù)等對象,而不應(yīng)該把與業(yè)務(wù)相關(guān)的而且是只針對個(gè)別頁面相關(guān)的信息存儲(chǔ)到全局變量中。
方式三,使用behaviors,behaviors 是用于組件間代碼共享的特性。但它不適用于組件間數(shù)據(jù)共享,一個(gè)頁面中對數(shù)據(jù)的更新不會(huì)影響到另一個(gè)頁面中的數(shù)據(jù)狀態(tài)。因此,也不適用于此場景。
方式四,使用緩存方式,這種方式可以實(shí)現(xiàn)對數(shù)據(jù)的共享,第一個(gè)頁面獲取到數(shù)據(jù)后使用wx.setStorageSync來存儲(chǔ)信息,在其他頁面中使用wx.getStorageSync來從緩存中同步讀取信息。
問題二,組件間代碼共享
另一個(gè)問題是下方的下拉列表,這個(gè)是一個(gè)列表組件,每個(gè)列表組件都有一些公共的方法,比如分頁信息,下拉刷新,觸底加載等,這些是每個(gè)列表組件都需要的,因此,可以使用組件間代碼共享behaviors來實(shí)現(xiàn)。
全局?jǐn)?shù)據(jù)共享
全局?jǐn)?shù)據(jù)共享是簡單的數(shù)據(jù)共享方式,它是小程序框架提供的一種全局?jǐn)?shù)據(jù)共享的方式,如果熟悉javascript的同學(xué)應(yīng)該很容易理解,在網(wǎng)頁開發(fā)中,經(jīng)常會(huì)定義一個(gè)全局類來存儲(chǔ)一些app中的公共信息,在其他頁面中import此全局實(shí)例,然后對此全局實(shí)例進(jìn)行g(shù)et或set。小程序框架也類似,只是它規(guī)定了此文件的位置,以及全局變量放置的位置。
全局變量在app.js中定義。比如,我們定義一個(gè)global_UserInfo對象
App({
onLaunch: function () {
},
globalData: {
g_userInfo: {}
}
})
在需要獲取該變量的頁面.js文件中按照如下代碼即可獲取。
var app = getApp();
Page({
data: {
userInfo: app.globalData.g_userInfo
}
})
獲取之后進(jìn)行修改也很簡單,只需直接賦值即可。
app.globalData.g_userInfo = {
"name":"David",
“role":"admin"
};
使用是不是很簡單,但是建議不能把globalData當(dāng)成一個(gè)大垃圾桶,什么東西都往里面裝,雖然使用最簡單,但業(yè)務(wù)相關(guān)的東西,盡量不要放在全局變量中,避免污染。
Behaviors
behaviors 是用于組件間代碼共享的特性,類似于一些編程語言中的 “mixins” 或 “traits”。作用有點(diǎn)類似于面向?qū)ο缶幊汤锏念惱^承,抽象出一些類的屬性和方法放在父類中,派生類可以繼承父類的方法,同時(shí)可以重載父類的方法。在小程序中,behavior中定義一些公共的屬性,數(shù)據(jù)和方法,組件導(dǎo)入這些behavior,進(jìn)而就有了behavior的屬性,數(shù)據(jù)和方法,同時(shí),組件還可以覆蓋behavior里的屬性和方法。同樣一個(gè)子類實(shí)例對父類的數(shù)據(jù)修改也不會(huì)影響到其他子類實(shí)例。
小程序官網(wǎng)
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/behaviors.html
詳細(xì)介紹了behaviors的概念以及使用?;旧暇W(wǎng)上搜索到的例子也都是來自官網(wǎng)的這個(gè)例子,我就不再抄一遍了。關(guān)鍵注意以下幾點(diǎn):
- 每個(gè)behavior包含一組屬性,數(shù)據(jù),方法和聲明周期函數(shù)。
- 每個(gè)組件可以引用多個(gè)behavior,behavior可以引用其他的behavior
- 當(dāng)組件引用behavior時(shí),behavior的屬性、數(shù)據(jù)和方法會(huì)被合并到組件中,生命周期函數(shù)也會(huì)在對應(yīng)時(shí)機(jī)被調(diào)用。 如下是引入behavior后,組件的屬性,數(shù)據(jù),方法和生命周期。
1)屬性:behavior的屬性、組件自身的屬性
2)數(shù)據(jù)字段:behavior的data、組件自身的data
3)方法:behavior的方法、組件自身的方法
4)生命周期函數(shù):attached、created、ready
behavior created方法-->組件的created方法-->behavior的attach方法-->組件的created方法-->behavior的ready方法-->組件的ready方法 - behavior不能實(shí)現(xiàn)組件間數(shù)據(jù)共享,一個(gè)組件改變behavior的data,不會(huì)影響到另一個(gè)引用此behavior的組件。這個(gè)很重要,behavior只是代碼的共享,它不能實(shí)現(xiàn)組件的數(shù)據(jù)共享。
在上面的實(shí)例中,我把導(dǎo)航列表相關(guān)的一些屬性,數(shù)據(jù)和方法提煉出來放在一個(gè)navigatorListBehavior中,在我的組件中引入了此behavior,然后就可以共享這些代碼,不需要在每個(gè)導(dǎo)航列表組件中都重復(fù)定義這些數(shù)據(jù),屬性和方法。代碼如下:
let navigatorListBehavior = Behavior({
behaviors: [],
properties: {
title: {
type: String
}
},
data: {
pageNumber: 0,
pageSize: 10,
isDisableLoading: false,
isLoading: false
},
attached: function () {},
methods: {
scrollBottom: function (e) {
if (this.data.isLoading) {
console.log("waiting...")
return;
}
this.setData({
isLoading: true
});
this.showData();
},
calculateSwiperHeight: function () {
var that = this
wx.getSystemInfo({
success: function (res) {
that.setData({
clientHeight: res.windowHeight
});
}
})
},
listLoadSucceed(){
wx.hideLoading();
wx.stopPullDownRefresh();
this.setData({
pageNumber: this.data.pageNumber + 1,
isLoading:false
});
},
isDisableLoading(response){
if(response.length == 0){
this.setData({
isDisableLoading:true
})
return true;
}
return false;
},
listLoadFailed(prompt, reason){
wx.hideLoading();
wx.showToast({
title: prompt + '加載失敗,原因' + res,
duration: 5000
});
this.setData({
isLoading: false
});
},
canLoad(){
return !this.data.isDisableLoading;
},
startLoading(){
wx.showLoading({
title: '努力加載中...',
});
},
reset(){
this.setData({
pageNumber:0,
isLoading:false,
isDisableLoading:false
})
}
}
})
export {
navigatorListBehavior
}
定義完behavior后,我們可以在組件或者頁面里引用此behavior,這樣組件或者頁面就有了behavior的屬性,方法和數(shù)據(jù)。比如下面的代碼片段,就可以直接使用方法,比如canLoad, listLoadSucceed, listLoadFailed,數(shù)據(jù)pageNumber,pageSize等。
import {
navigatorListBehavior
} from '../navigatorListBehavior.js'
Page({
behaviors: [navigatorListBehavior],
showData: function () {
if (!this.canLoad()) {
return;
}
this.startLoading();
this.getData(res => {
console.log(res);
this.listLoadSucceed();
if (this.isDisableLoading(res)) {
return;
}
this.currentActivityList.loadData(res);
}, res => {
console.log(res);
this.listLoadFailed('活動(dòng)列表', res);
})
},
getData: function (successCallback, failedCallback) {
wx.cloud.callFunction({
name: 'club',
data: {
action: "requestMyActivitiesByStatus",
status: status,
pageNumber: this.data.pageNumber,
pageSize: this.data.pageSize
}
}).then(res => {
successCallback(res.result);
}).catch(res => failedCallback(res));
},
})
Mobx數(shù)據(jù)共享
還是回到之前的實(shí)例,俱樂部介紹頁面需要用到俱樂部頁面的數(shù)據(jù),這時(shí)候使用behavior是不能實(shí)現(xiàn)的,因?yàn)槭莾蓚€(gè)組件或頁面之間需要共享數(shù)據(jù),而不是代碼級別的共享。全局?jǐn)?shù)據(jù)共享(又叫做:狀態(tài)管理)是為了解決組件之間數(shù)據(jù)共享的問題。在網(wǎng)頁開發(fā)中常用的全局?jǐn)?shù)據(jù)共享方案有: Vuex 、 Redux 、 MobX 等。在小程序的官網(wǎng)中,使用了Mobx作為數(shù)據(jù)共享方案。下面介紹一些如何使用Mobx來完成數(shù)據(jù)共享。
第一步,安裝 mobx-miniprogram 和 mobx-miniprogram-bindings
npm install --save mobx-miniprogram mobx-miniprogram-bindings
第二步,創(chuàng)建 MobX Store
import { observable, action } from 'mobx-miniprogram'
export const store = observable({
// 數(shù)據(jù)字段
club: {},
//actions
setClubData: action(function (club) {
this.club = club
}),
})
第三步,在構(gòu)造器中使用
- 組件調(diào)用方式
如果是組件中使用Mobx,按照以下方式在構(gòu)造器中調(diào)用,然后就可以在組件里使用store里的字段和方法。
import {
createStoreBindings
} from 'mobx-miniprogram-bindings'
import {
store
} from '../clubStore.js'
Component({
behaviors: [storeBindingsBehavior],
data: {
someData: '...'
},
storeBindings: {
store,
fields: {
club: () => store.club,
},
actions: {
setClubData: 'setClubData'
},
},
getData: function (successCallback, failedCallback) {
wx.cloud.callFunction({
name: 'club',
data: {
action: "requestClubDetails",
id: this.pageData.clubId,
name: this.pageData.clubName
}
}).then(clubResponse => {
//調(diào)用store里的方法
this.setClubData(clubResponse);
}
}
})
2)頁面中調(diào)用
如果是頁面中使用Mobx,按照以下方式在構(gòu)造器中調(diào)用,然后在頁面中就可以使用store里的字段和方法。
import {
createStoreBindings
} from 'mobx-miniprogram-bindings'
import {
store
} from '../clubStore.js'
Page({
/**
* 頁面的初始數(shù)據(jù)
*/
data: {
clubDetail: {},
},
/**
* 生命周期函數(shù)--監(jiān)聽頁面加載
*/
onLoad: function (options) {
// 綁定 MobX store
this.storeBindings = createStoreBindings(this, {
store, // 需要綁定的數(shù)據(jù)倉庫
fields: ['club'], // 將 this.data.club 綁定為倉庫中的 club
actions: ['setClubData'], // 將 this.setClubData 綁定為倉庫中的 setClubData
});
},
onUnload() {
this.storeBindings.destroyStoreBindings()
},
getData: function (successCallback, failedCallback) {
wx.cloud.callFunction({
name: 'club',
data: {
action: "requestClubDetails",
id: this.pageData.clubId,
name: this.pageData.clubName
}
}).then(clubResponse => {
//調(diào)用store里的方法
this.setClubData(clubResponse);
}
}
})
第四步,頁面上綁定數(shù)據(jù)
同樣,需要在clubIntroduction中按照上面的方式引用Mobx,就可以在club introduction總使用club數(shù)據(jù),達(dá)到在俱樂部中調(diào)用setClubData方法更新數(shù)據(jù),在club introduction的wxml中就可以綁定此data.
<view class="page">
<view class="page__hd">
<view class="page__title"></view>
</view>
<view class="page__bd">
<view class="article-title">{{x}}俱樂部</view>
<view class="article-content">
<wemark md="{{club.introduction}}" link highlight type="wemark"></wemark>
</view>
</view>
</view>
注意
Mobx的使用需要在需要小程序基礎(chǔ)庫版本 >= 2.2.3 的環(huán)境
寫在最后
上面就介紹完了在小程序中如何進(jìn)行數(shù)據(jù),方法,屬性和代碼的共享。每種方式都有特定的使用場景。Behavior是代碼級別的重用,在組件和頁面中共享代碼,包括屬性,方法和數(shù)據(jù)。Mobx是全局?jǐn)?shù)據(jù)共享,解決了組件和頁面間數(shù)據(jù)共享,狀態(tài)更新的問題,一個(gè)組件或頁面更新數(shù)據(jù)能同步到另一個(gè)組件或者頁面。全局?jǐn)?shù)據(jù)是最簡單的數(shù)據(jù)共享方式,但最好不要把所有需要共享的數(shù)據(jù)都往全局?jǐn)?shù)據(jù)里放,避免全局?jǐn)?shù)據(jù)污染。緩存也是一種簡單有效的數(shù)據(jù)共享方式,可以同步和異步更新或者獲取緩存數(shù)據(jù),能減少服務(wù)請求次數(shù),提高小程序運(yùn)行效率。