BUI store 實例

2020-08-12 14:08 更新

在開始之前,我們先來看看這個待辦處理的效果. 代碼里面只需做相關(guān)的綁定操作, 操作dom的行為交給行為屬性處理, 這個案例在綜合案例會有詳細說明.

案例

案例圖片

1. 創(chuàng)建一個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 | true
  • data 數(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 里面.

  1. loader.define(function(){
  2. var bs = bui.store({
  3. scope: "page",
  4. data: {},
  5. methods: {},
  6. watch: {},
  7. computed: {},
  8. templates: {},
  9. beforeMount: function(){
  10. // 數(shù)據(jù)解析前執(zhí)行
  11. },
  12. mounted: function(){
  13. // 數(shù)據(jù)解析后執(zhí)行
  14. }
  15. })
  16. })

注意: 每個單獨的模塊里面都可以使用page, 如果作為局部加載的模塊, 則需要區(qū)分數(shù)據(jù)源.

比方模塊A, 要把模塊B的數(shù)據(jù)加載到模塊A里面.

模塊A:

  1. loader.define(function(){
  2. // 當前數(shù)據(jù)源為 page
  3. var bs = bui.store({
  4. scope: "page",
  5. data: {},
  6. mounted: function(){
  7. // 加載模塊B
  8. router.loadPart({
  9. id: "#test",
  10. url: "模塊B.html"
  11. })
  12. }
  13. })
  14. })

模塊B:

  1. loader.define(function(){
  2. // 這里的數(shù)據(jù)源不能跟模塊A的數(shù)據(jù)源名字相同
  3. var bs = bui.store({
  4. scope: "pageB",
  5. data: {},
  6. mounted: function(){
  7. }
  8. })
  9. return bs;
  10. })

2. 基本使用

實例的名字由你自己定義,這里我們整篇使用bs (behavior store)作為實例名. b 標簽作為這個數(shù)據(jù)關(guān)聯(lián)的默認標簽.

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. size: 1
  5. }
  6. });
  7. // 設(shè)置以后就會觸發(fā)dom b-text="page.size"的視圖更新
  8. bs.size = 2;

  1. <b b-text="page.size"></b>

當這些數(shù)據(jù)改變時,視圖會進行重渲染。綁定到模板里面的寫法是 page.xxx 而不是 bs.xxx .

注意: 新增的屬性不是響應(yīng)式的. 例如:

  1. bs.number = 12;

在進行視圖的設(shè)計的時候, 需要對這些值進行初始值的設(shè)定, 自定義鍵值, 比如:

  1. data : {
  2. str: '',
  3. num: 0,
  4. bool: false,
  5. lists: [],
  6. }

需要注意的是, 如果你希望通過 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 的使用

3. 加載的時機

?> 當store初始化的時候, 會做兩件事情

  • 第1件事, 把當前已有的數(shù)據(jù)進行代理, 也就是vue使用的 Object.define來處理 data,watch,computed 這些數(shù)據(jù)掛載到 store實例本身;
  • 第2件事, 把模板進行匹配過濾, 找到對應(yīng)的選擇器.

在這兩件事前后, 會分別執(zhí)行beforeMount, mounted 方法. 所以一般業(yè)務(wù)都應(yīng)該在 mounted 里面執(zhí)行.

而頁面的生命周期, 其實是在模塊里面的, 通過路由的跳轉(zhuǎn)執(zhí)行模塊的生命周期, 很多時候我們都無需關(guān)注, 我們也僅僅是提供了最簡單的使用方式.

4. 動態(tài)加載時機

我們看到上面的數(shù)據(jù)都是靜態(tài)數(shù)據(jù), 一開始數(shù)據(jù)是有初始值的, 這樣是最好的, 但有時候我們還有動態(tài)數(shù)據(jù), 需要通過請求以后才能加載進來, 這種又該如何處理呢?

  1. var bs = bui.store({
  2. scope: "page",
  3. data: {
  4. list: [],
  5. },
  6. templates: {
  7. tplList: function (data) {
  8. var html = "";
  9. data.forEach(function (item,i) {
  10. html += `<li class="bui-btn">${item}</li>`;
  11. })
  12. return html;
  13. }
  14. },
  15. mounted: function () {
  16. // 模擬數(shù)據(jù)動態(tài)改變
  17. setTimeout(()=>{
  18. // 方法1:
  19. // this.list.push("廣州","深圳","上海","北京");
  20. // 方法2: 合并并觸發(fā) this.list 的視圖更新
  21. bui.array.merge(this.list,["廣州","深圳","上海","北京"])
  22. },1000)
  23. }
  24. })

  1. <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)用.

  1. ...
  2. mounted: function () {
  3. // 模擬數(shù)據(jù)動態(tài)改變
  4. setTimeout(()=>{
  5. this.oneTick("citys",function () {
  6. this.compile("#test")
  7. })
  8. bui.array.merge(this.list,["廣州","深圳","上海","北京"])
  9. },1000)
  10. }
  11. ...

如果首頁的tab,要異步加載公共模板在tab1里,則需要在tab1里執(zhí)行一次 this.compile("#id"), id 為當前tab的樣式或者id名 .

5. oneTick 與 nextTick 的區(qū)別.

這兩個方法, 都是在 dom 渲染以后執(zhí)行, 不同的是:

  • oneTick 只在某個字段更新,并且視圖渲染以后的才會觸發(fā), 并且同個字段只監(jiān)聽一次.
  • nextTick 是多字段, 不管哪個字段更新,只要觸發(fā)了視圖更新, 都會執(zhí)行一次, 造成重復渲染, 重復調(diào)用還會造成重復的監(jiān)聽.

!> 特別是在watch監(jiān)聽的時候, 千萬不要使用 nextTick. 一般是在 mounted 使用.

6. this.xxx 跟 this.$data.xxx 有什么區(qū)別?

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

  1. var bs = bui.store({
  2. data: {
  3. a: {
  4. b: 234
  5. },
  6. c: {
  7. d: 345
  8. }
  9. },
  10. beforeMount: function(){
  11. // 獲取頁面參數(shù)
  12. var pageParams = router.getPageParams();
  13. // 在beforeMount 只能通過 this.$data.xx = xxx 這樣去操作.
  14. this.$data.a.b = pageParams.id;
  15. },
  16. mounted: function(){
  17. // 判斷或者比對,使用這種 this.$data.xxx
  18. if( this.$data.c.d == 345) {
  19. // 設(shè)置使用這種 this.xxx
  20. this.a.b = 123;
  21. }
  22. }
  23. })

如果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 的計算的.

7. 公共數(shù)據(jù)與私有數(shù)據(jù)

這是bui.store獨特的地方, 我們通過scope來區(qū)分數(shù)據(jù)源, 再加上isPublic:true這個參數(shù), 這樣在index.js初始化以后, 所有的單頁頁面都可以拿到這個公共數(shù)據(jù), 當公共數(shù)據(jù)改變的時候, 多個頁面的數(shù)據(jù)視圖都會重新渲染.

  1. window.router = bui.router();
  2. bui.ready(function() {
  3. // 公共數(shù)據(jù)
  4. window.store = bui.store({
  5. scope: "app",
  6. isPublic: true,
  7. data: {
  8. firstName: "Hello",
  9. lastName: "BUI"
  10. }
  11. })
  12. // 初始化路由
  13. router.init({
  14. id: "#bui-router",
  15. progress: true,
  16. hash: true,
  17. store: store,
  18. })
  19. })

  1. <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ù)的視圖.

8. Tab子模塊加載公共數(shù)據(jù)

如果是在tab 里面要加載公共數(shù)據(jù)的模板解析的話, 需要執(zhí)行多一次 store.compile(".tab-news") ;

例如:

index.js 公共數(shù)據(jù)的示例數(shù)據(jù)

  1. window.store = bui.store({
  2. scope: "app",
  3. isPublic: true,
  4. data: {
  5. list: [{
  6. id: "news1",
  7. title: "新聞標題1"
  8. },{
  9. id: "news2",
  10. title: "新聞標題1"
  11. }]
  12. },
  13. templates: {
  14. tplList: function(data){
  15. var html = "";
  16. data.forEach(function(item,index){
  17. html +=`<li class="bui-btn">${item.title}</li>`
  18. })
  19. return html;
  20. }
  21. }
  22. })

tab模塊的結(jié)構(gòu)及腳本.

  1. <div class="tab-news">
  2. <ul class="bui-list" b-template="app.tplList(app.list)"></ul>
  3. </div>

  1. loader.define(function(){
  2. // 必須執(zhí)行一次
  3. store.compile(".tab-news");
  4. })

因為tab異步加載一個模塊的時候, html模板還沒有渲染完畢, 但store已經(jīng)處理完, 所以需要告訴store 還有哪個模板需要解析. 如果不是tab 則不用.

9. 常用方法

請查看bui.store API

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號