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