在開(kāi)始之前,我們先來(lái)看看這個(gè)待辦處理的效果. 代碼里面只需做相關(guān)的綁定操作, 操作dom的行為交給行為屬性處理, 這個(gè)案例在綜合案例會(huì)有詳細(xì)說(shuō)明.
store
實(shí)例.el
掛載的根元素, 默認(rèn)是: .bui-page
, 如果是在tab控件里面動(dòng)態(tài)加載(tab外層沒(méi)有header,里面有header,一般就會(huì)有2個(gè).bui-page
), 修改el
參數(shù)可以防止多次解析.scope
數(shù)據(jù)的范圍, 必須字段, 比方公共數(shù)據(jù)可以使用app
, 模塊里面的數(shù)據(jù)默認(rèn)使用 page
isPublic
是否是公共數(shù)據(jù), 默認(rèn): false | truedata
數(shù)據(jù)的定義, 結(jié)合行為屬性使用watch
偵聽(tīng)器, 偵聽(tīng)data 里面的字段改變, 觸發(fā)當(dāng)前回調(diào)computed
計(jì)算屬性, 結(jié)合 data 的字段的處理, 比方加減乘除methods
自定義的方法, 通過(guò)行為屬性b-click
訪問(wèn)templates
模板的定義,通過(guò)行為屬性b-template
訪問(wèn)beforeMount
數(shù)據(jù)解析前執(zhí)行mounted
數(shù)據(jù)解析后執(zhí)行
bui.store
初始化, 單頁(yè)一般在loader.define
里面, 多頁(yè)在 bui.ready
里面.
loader.define(function(){
var bs = bui.store({
scope: "page",
data: {},
methods: {},
watch: {},
computed: {},
templates: {},
beforeMount: function(){
// 數(shù)據(jù)解析前執(zhí)行
},
mounted: function(){
// 數(shù)據(jù)解析后執(zhí)行
}
})
})
注意: 每個(gè)單獨(dú)的模塊里面都可以使用page, 如果作為局部加載的模塊, 則需要區(qū)分?jǐn)?shù)據(jù)源.
比方模塊A, 要把模塊B的數(shù)據(jù)加載到模塊A里面.
模塊A:
loader.define(function(){
// 當(dāng)前數(shù)據(jù)源為 page
var bs = bui.store({
scope: "page",
data: {},
mounted: function(){
// 加載模塊B
router.loadPart({
id: "#test",
url: "模塊B.html"
})
}
})
})
模塊B:
loader.define(function(){
// 這里的數(shù)據(jù)源不能跟模塊A的數(shù)據(jù)源名字相同
var bs = bui.store({
scope: "pageB",
data: {},
mounted: function(){
}
})
return bs;
})
實(shí)例的名字由你自己定義,這里我們整篇使用bs
(behavior store)作為實(shí)例名. b
標(biāo)簽作為這個(gè)數(shù)據(jù)關(guān)聯(lián)的默認(rèn)標(biāo)簽.
var bs = bui.store({
scope: "page",
data: {
size: 1
}
});
// 設(shè)置以后就會(huì)觸發(fā)dom b-text="page.size"的視圖更新
bs.size = 2;
<b b-text="page.size"></b>
當(dāng)這些數(shù)據(jù)改變時(shí),視圖會(huì)進(jìn)行重渲染。綁定到模板里面的寫法是 page.xxx
而不是 bs.xxx
.
注意: 新增的屬性不是響應(yīng)式的. 例如:
bs.number = 12;
在進(jìn)行視圖的設(shè)計(jì)的時(shí)候, 需要對(duì)這些值進(jìn)行初始值的設(shè)定, 自定義鍵值, 比如:
data : {
str: '',
num: 0,
bool: false,
lists: [],
}
需要注意的是, 如果你希望通過(guò)
bs.lists = [1,2]
這種賦值操作來(lái)操作數(shù)組,bs.lists
的dom是不會(huì)進(jìn)行響應(yīng)的, 但你可以使用bs.lists.push()
的方式, 或者使用bui.array
的一些命令式方法, 來(lái)處理這些數(shù)組. 這個(gè)會(huì)在 模板渲染的章節(jié) 使用到.
1.5.3 以后可以直接修改
bs.lists.$set(0,222)
, 這樣會(huì)觸發(fā)視圖更新, 具體可以查看bui.array.set
的使用
?> 當(dāng)store初始化的時(shí)候, 會(huì)做兩件事情
Object.define
來(lái)處理 data,watch,computed 這些數(shù)據(jù)掛載到 store實(shí)例本身;
在這兩件事前后, 會(huì)分別執(zhí)行beforeMount
, mounted
方法. 所以一般業(yè)務(wù)都應(yīng)該在 mounted
里面執(zhí)行.
而頁(yè)面的生命周期, 其實(shí)是在模塊里面的, 通過(guò)路由的跳轉(zhuǎn)執(zhí)行模塊的生命周期, 很多時(shí)候我們都無(wú)需關(guān)注, 我們也僅僅是提供了最簡(jiǎn)單的使用方式.
我們看到上面的數(shù)據(jù)都是靜態(tài)數(shù)據(jù), 一開(kāi)始數(shù)據(jù)是有初始值的, 這樣是最好的, 但有時(shí)候我們還有動(dòng)態(tài)數(shù)據(jù), 需要通過(guò)請(qǐng)求以后才能加載進(jìn)來(lái), 這種又該如何處理呢?
var bs = bui.store({
scope: "page",
data: {
list: [],
},
templates: {
tplList: function (data) {
var html = "";
data.forEach(function (item,i) {
html += `<li class="bui-btn">${item}</li>`;
})
return html;
}
},
mounted: function () {
// 模擬數(shù)據(jù)動(dòng)態(tài)改變
setTimeout(()=>{
// 方法1:
// this.list.push("廣州","深圳","上海","北京");
// 方法2: 合并并觸發(fā) this.list 的視圖更新
bui.array.merge(this.list,["廣州","深圳","上海","北京"])
},1000)
}
})
<ul b-template="page.tplList(page.list)"></ul>
一般請(qǐng)求得到的是一個(gè)數(shù)組, 所以建議可以使用
bui.array.merge
合并數(shù)據(jù). 這樣視圖就能得到更新.
再?gòu)?fù)雜一點(diǎn), 我們需要對(duì)數(shù)據(jù)進(jìn)行解析, 比方模板里面,包含 b-model
, 需要雙向綁定, 那么我們需要調(diào)用 this.compile
方法, 而這個(gè)方法,是需要在 dom 渲染完成以后才能處理的, 這就要用到 this.oneTick
或者 this.nextTick
來(lái)處理, 并且需要在數(shù)據(jù)更新之前調(diào)用.
...
mounted: function () {
// 模擬數(shù)據(jù)動(dòng)態(tài)改變
setTimeout(()=>{
this.oneTick("citys",function () {
this.compile("#test")
})
bui.array.merge(this.list,["廣州","深圳","上海","北京"])
},1000)
}
...
如果首頁(yè)的tab,要異步加載公共模板在
tab1
里,則需要在tab1
里執(zhí)行一次this.compile("#id")
, id 為當(dāng)前tab的樣式或者id名 .
這兩個(gè)方法, 都是在 dom 渲染以后執(zhí)行, 不同的是:
oneTick
只在某個(gè)字段更新,并且視圖渲染以后的才會(huì)觸發(fā), 并且同個(gè)字段只監(jiān)聽(tīng)一次.nextTick
是多字段, 不管哪個(gè)字段更新,只要觸發(fā)了視圖更新, 都會(huì)執(zhí)行一次, 造成重復(fù)渲染, 重復(fù)調(diào)用還會(huì)造成重復(fù)的監(jiān)聽(tīng).
!> 特別是在watch監(jiān)聽(tīng)的時(shí)候, 千萬(wàn)不要使用 nextTick
. 一般是在 mounted
使用.
this.xxx === this.$data.xxx
; 對(duì)于data里面的字段來(lái)說(shuō), 這2個(gè)值是完全相等的. 那他們之間的區(qū)別在哪里?
bui.store
通過(guò)Object.defineProperty
劫持對(duì)象的讀取或者設(shè)置獲得字段, 通過(guò)訂閱來(lái)響應(yīng)頁(yè)面上的DOM行為, 他們之間會(huì)有很多種組合, 最常見(jiàn)的一種情況是, 容易導(dǎo)致字段更新以后, 頁(yè)面沒(méi)有同時(shí)響應(yīng). 通過(guò)log:true
可以看到字段讀取的順序.(如果只有1層數(shù)據(jù),則沒(méi)有這個(gè)問(wèn)題.)
解決這個(gè)頁(yè)面不響應(yīng)的問(wèn)題也很簡(jiǎn)單, 就是規(guī)定使用 this.xxx
用于設(shè)置; 在設(shè)置前, 數(shù)據(jù)的其它獲取,計(jì)算,比對(duì)等操作, 需要通過(guò) this.$data.xxx
去處理. 特別是多層級(jí)的設(shè)置. 如果字段層級(jí)較深, 可以使用 this.set("xx.xx.xx",123)
, 確保能夠正確觸發(fā)視圖更新.
在
beforeMount
里面的數(shù)據(jù)操作, 需要使用this.$data.xxx
var bs = bui.store({
data: {
a: {
b: 234
},
c: {
d: 345
}
},
beforeMount: function(){
// 獲取頁(yè)面參數(shù)
var pageParams = router.getPageParams();
// 在beforeMount 只能通過(guò) this.$data.xx = xxx 這樣去操作.
this.$data.a.b = pageParams.id;
},
mounted: function(){
// 判斷或者比對(duì),使用這種 this.$data.xxx
if( this.$data.c.d == 345) {
// 設(shè)置使用這種 this.xxx
this.a.b = 123;
}
}
})
如果
data
里面的值是數(shù)組的操作,bui.array.index, bui.array.indexs, bui.array.compare, bui.array.filter, bui.array.get, bui.array.getAll
取值,比對(duì),索引等方法, 應(yīng)該使用this.$data.xxx
作為參數(shù). 如果是賦值修改操作bui.array.empty, bui.array.replace, bui.array.merge, bui.array.set, bui.array.delete, bui.array.remove
, 應(yīng)該使用this.xxx
作為參數(shù).
如果在computed
的計(jì)算,也是需要直接使用 this.xxx
去讀取才會(huì)觸發(fā) computed 的計(jì)算的.
這是
bui.store
獨(dú)特的地方, 我們通過(guò)scope
來(lái)區(qū)分?jǐn)?shù)據(jù)源, 再加上isPublic:true
這個(gè)參數(shù), 這樣在index.js
初始化以后, 所有的單頁(yè)頁(yè)面都可以拿到這個(gè)公共數(shù)據(jù), 當(dāng)公共數(shù)據(jù)改變的時(shí)候, 多個(gè)頁(yè)面的數(shù)據(jù)視圖都會(huì)重新渲染.
window.router = bui.router();
bui.ready(function() {
// 公共數(shù)據(jù)
window.store = bui.store({
scope: "app",
isPublic: true,
data: {
firstName: "Hello",
lastName: "BUI"
}
})
// 初始化路由
router.init({
id: "#bui-router",
progress: true,
hash: true,
store: store,
})
})
<b b-text="app.firstName"></b>
如上面例子: store.firstName="Bingo"
的時(shí)候, 所有單頁(yè)頁(yè)面上有<b b-text="app.firstName"></b>
進(jìn)行渲染的模板,都會(huì)一起改變.
把
store
掛載到路由, 還可以解析公共數(shù)據(jù)的{{app.firstName}}
之類的數(shù)據(jù)(只渲染一次), 在模塊里面,你也可以使用store.firstName
讀取跟修改公共數(shù)據(jù)的值, 會(huì)更新頁(yè)面相關(guān)數(shù)據(jù)的視圖.
如果是在
tab
里面要加載公共數(shù)據(jù)的模板解析的話, 需要執(zhí)行多一次store.compile(".tab-news")
;
例如:
index.js 公共數(shù)據(jù)的示例數(shù)據(jù)
window.store = bui.store({
scope: "app",
isPublic: true,
data: {
list: [{
id: "news1",
title: "新聞標(biāo)題1"
},{
id: "news2",
title: "新聞標(biāo)題1"
}]
},
templates: {
tplList: function(data){
var html = "";
data.forEach(function(item,index){
html +=`<li class="bui-btn">${item.title}</li>`
})
return html;
}
}
})
tab模塊的結(jié)構(gòu)及腳本.
<div class="tab-news">
<ul class="bui-list" b-template="app.tplList(app.list)"></ul>
</div>
loader.define(function(){
// 必須執(zhí)行一次
store.compile(".tab-news");
})
因?yàn)閠ab異步加載一個(gè)模塊的時(shí)候, html模板還沒(méi)有渲染完畢, 但store已經(jīng)處理完, 所以需要告訴store 還有哪個(gè)模板需要解析. 如果不是tab 則不用.
請(qǐng)查看bui.store API
更多建議: