當 meteor 碰撞到 Vue,初使用(持續更新)

本文處于完善狀態,會持續更新。最近有點忙,各位看官可以養肥再看。多謝關注!

akryum:vue-component

使用 akryum:vue-component 包幫助 meteor 識別編譯 vue 文件。

meteor add akryum:vue-component

vue-meteor-tracker

使用 vue-meteor-tracker 包,在 Vue 組件中集合 Meteor 響應數據。

Installation

meteor npm install --save vue-meteor-tracker

Install the plugin into Vue

import VueMeteorTracker from 'vue-meteor-tracker';
Vue.use(VueMeteorTracker);

Use

在你的 vue 組件中添加 meteor 對象屬性 :

new Vue({
  meteor: {
    // Meteor specific options
  }
});

Subscriptions

在 $subscribe object 定義訂閱者,key 為發布者名稱,value 為數組參數,在訂閱的時候傳遞給發布者。

當組件銷毀時訂閱將停止

meteor: {
  //在 $subscribe object 定義訂閱者
  $subscribe: {
    // 不帶參數訂閱 'threads' 發布者 
    'threads': [],
    // 帶參數訂閱 'threads' 發布者 
    'threads': ['new', 10], // The 10 newest threads
    // 帶動態參數訂閱 'threads' 發布者 
    // 當 vue 響應屬性改變時,重新訂閱
    'posts': function() {
      // Here you can use Vue reactive properties
      return [this.selectedThreadId] // Subscription params
    }
  }
}

你能夠 $subscribe(name, ...params) 方法在你的組件代碼中:

ready () {
  // Subscribes to the 'threads' publication with two parameters
  this.$subscribe('thread', 'new', 10);
}

在你的組件中,$subReady 對象屬性包含訂閱者訂閱狀態 ,為了獲知訂閱者是否已訂閱,你可以這樣做:

console.log(this.$subReady.thread);

或者在你的模板中:

<div v-if="!$subReady.thread">Loading...</div>

Reactive data

在 meteor 對象里的屬性(不在 $subscribe 中)會被轉化為 vue 的響應屬性(附加到 vue 的 data 屬性中) ,你可以在模板中像標準 vue data 屬性一樣使用,或者在計算屬性中。

...
data() {
      return {
        // 初始化 todos 也可以不用
        todos: [],
        newTodo: ''
      }
  },
  meteor: {
    $subscribe: {
      ['todos']: []
    },
    // todos 屬性會響應更新 meteor 的響應數據源(就像 collections 或者 session )
    // 當 Todos 集合發生變化時,todos將發生改變
    todos() {
        return Todos.find({}, {sort: {date: -1}});
    }
  },
...

通過上面做法,僅僅讓 vue data 響應更新于 meteor 響應數據源,當 todos 集合發生改變,vue 的 todos 數據屬性才能獲取更新,如下圖所示

reactive1.PNG

如上圖所示,vue 和 meteor 的數據是單向的:meteor => vue

通過把 meteor 對象屬性的值定義為一個對象,我們可以讓 meteor 響應 vue,該對象有以下來個參數:

  • params() (可選), 返回對象的函數, 該對象屬性值可以為 vue data 的響應屬性
  • update([params]), 但依賴參數發生改變回調的函數。
...
meteor: {
    // 無參訂閱 todos 
    $subscribe: {
      ["todos"]: []
    },
    todos: {
      // 聲明定義一個依賴 vue 響應屬性的參數
      params() {
        // Here you can use Vue reactive properties
        // Don't use Meteor reactive sources!
        return {
          type: this.type
        };
      },
      // 可選項,深度觀察嵌套對象屬性值,
      // 此處 type 為普通類型,故 false
      deep: false,
      //// Meteor Reactivity
      // param 參數響應 vue 更新時,將刷新 todos 屬性值
      // Then it calls Tracker.autorun() to refresh the result
      // each time a Meteor reactive source changes
      update({ type }) {
        //   Here you can use Meteor reactive sources
        //  like cursors or reactive vars
        //  Don't use Vue reactive properties!
        if (type) {
          return Todos.find({checked: true},{sort: {date: -1}});
        } else {
          return Todos.find({checked: false},{sort: {date: -1}});
        }
      }
    }
  },
...
rect.PNG

此刻,vue 和 meteor 數據流為雙向:vue <=> meteor

注意
在 meteor 屬性中我們有兩個地方使用到 vue 響應屬性。

 meteor: {
1. 在  $subscribe 中帶參訂閱 publisher
    $subscribe: {
      ['todos']:  function() {
     // 使用 vue 屬性
      return [xxx] 
    }
    },
2. 在對象屬性中 param 中
    todos: {
       params() {
        // 使用 vue 屬性
        return {
          type: xxx
        };
      },
    }
  },

區別是 “1” 處 vue 屬性發生改變會重新訂閱,“2”處 vue屬性發生改變重新獲取 miniMongo 獲取數據。

開啟或禁用 meteor data

export default {
  meteor: {
    // ...
  },

  methods: {
    activate () {
      this.$startMeteor()
    },

    deactivate () {
      this.$stopMeteor()
    },
  },
}

你能夠使用來防止 meteor data 自啟動

export default {
  meteor: {
    $lazy: true,
    // ...
  },
}

Freezing data

此選項將對 Meteor 數據使用 Object.freeze,以防止 Vue 響應。 這樣可以在渲染大型集合列表時提高 Vue 的性能。 默認情況下,此選項已關閉。

// Disable Vue reactivity on Meteor data
Vue.config.meteor.freeze = true;

vue-supply

Installation

npm install --save vue-supply

Use

import Vue from 'vue'
import VueSupply from 'vue-supply'

Vue.use(VueSupply)

當應用變得越來越龐大時,我們需要在每個組件重復定義 meteor 屬性,并且我們可能定義同樣的值,管理這些響應性數據源就會變得越來越困難。
使用 vue-supply,您可以輕松地使用數據并自動激活或停用訂閱。

image.png

vue-supply 為我們的應用添加了一層 supply,幫助我們管理訂閱 meteor 數據,避免在組件重復訂閱 meteor 數據。
使用 vue-supply,您可以創建擴展Supply定義的 Vue 實例。定義兩個方法: activate 和 deactivate。當在組件或 Vue store 首次消費該 supply 時,它會自動激活(使用 grasp 方法);當沒有組件使用它時,它會自動關閉(使用 release 方法)。當激活或關閉時,supply 會回調 activate 和 deactivate 方法。

// base.js
export default {
  extends: Supply,

  methods: {
    activate () {
      // 當激活時,開啟 meteor ,訂閱發布者
      this.$startMeteor()
    },

    deactivate () {
      // 關閉 meteor ,取消訂閱
      this.$stopMeteor();
    },
  },

  meteor: {
     // 關閉 vue-meteor-tracker自啟動訂閱 meteor data
     // 讓 supply 監聽組件,管理訂閱
    $lazy: true,
  },
}

當激活或關閉時,supply 會發射 consumers 和 active 事件,你可以使用 watch 其屬性和 $on 監聽其 'is-active' 和 'is-not-active' 事件。

// xxx.supply.js
import base from "./base";

export default {
    extends: base,

    data() {
        return {
           ...
        }
    },

    meteor: {
        ...
    },

    watch: {
        // 監聽 supply 是否激活
        active(val){
            console.log(val);
        },

        // 監聽 組件消費者個數
        consumers(val){
            console.log(val);
        }
    }
}

你也可以使用 supply.ensureActive(),返回一個 Promise(that resolves as soon as the supply is activated )

TestResource.ensureActive().then(() => {
  // The supply is active
})

Registration

建議注冊 supply 定義,以便注入到組件和 vuex stroe 中。

import { register } from 'vue-supply'
import TestResourceDef from 'supply/test-resource'
register('TestResource', TestResourceDef)

Usage in components

在組件內部,use(name,manageKeepAlive = true)添加一個mixin,使用注冊中使用的名稱(如上所述),創建和銷毀組件時自動消費或釋放 supply:

// todo-list.component.js
import { use } from "vue-supply";

export default {
  name: "app",
  components: {
    "todo-list": TodoList
  },
  mixins: [use("TodoSupply")],
  data() {
    return {
      newTodo: ""
    };
  },
  computed: {
    todos() {
      return this.$supply.TodoSupply.todos;
    },
    ready() {
      return this.$supply.TodoSupply.active;
    },
    type() {
      return this.$supply.TodoSupply.type;
    }
  },
....

通過計算 this.$supply.TestResource.[someData] 該響應屬性獲取數據。

Usage in Vuex store

see Usage in Vuex store

Collections

aldeed:simple-schema (設計數據模式)

Mongo 數據雖然是無模式,但這不意味著我們就不可以用模式去設計規范驗證我們的數據。使用 aldeed:simple-schema ,在寫入數據操作時驗證數據。

Installation

$ meteor add aldeed:simple-schema

Use

定義 Schema

// todos.collection.js
export const TodoSchema = new SimpleSchema({
    title: {
        type: String,   
        max: 200,
        min: 1,
        // 自定驗證器
        custom: function () {
            if(this.value.trim().length == 0){
                return '不能為空'
            }
        }
    },

    checked: {
        type: Boolean
    },

    date: {
        type: Date
    }
})

todos.schmea = TodoSchema;

驗證

// App.vue
data() {
    return {
      newTodo: "",
      // 定義驗證空間,針對不同區域驗證
      context: Todos.schmea.namedContext("myContext") 
    };
  },
  computed: {
    todos() {
      return this.$supply.TodoSupply.todos;
    },
    ready() {
      return this.$supply.TodoSupply.ready;
    },
    type() {
      return this.$supply.TodoSupply.type;
    }
  },
  methods: {
    submit() {
      let newtodo = {
        title: this.newTodo,
        date: new Date(),
        checked: false
      };
      // 驗證
      if (this.context.validate(newtodo)) {
        Todos.insert(newtodo);
      }

       this.newTodo = "";
    },

錯誤信息提示

<span v-show="context.keyIsInvalid('title')" class="has-text-danger">不能為空</span>
      <div class="control has-icons-left has-icons-right">
        <input class="input is-medium" :class="{'is-danger': context.keyIsInvalid('title')}" type="text" placeholder="write it..." v-model="newTodo" @keyup.enter="submit">
        <span class="icon is-small is-left">
          <i class="fa fa-file-o"></i>
        </span>
      </div>

這里不多述 SimpleSchema 的用法,請自行查看 api

Publications and Data Loading(發布訂閱)

為了提高數據安全性,我們不應該直接在客戶端使用 Collections 全部詳細字段,不應該直接調用 collection.insert() 等方法對數據庫進行寫入。我們應該服務端發布可訪問的公有數據,定義數據寫入接口:

  • Meteor.pulish() 定義數據發布接口
  • Meteor.methods() 定義數據操作接口

具體 api 查看。

在使用之前我們必須刪除以下的包

  • meteor remove autopublish
  • meteor remove insecure

注意:publisher 在沒有 cursor 返回的情況下要返回 this.ready()

服務端發布

// todos.collection.js
if (Meteor.isServer) {
    // 定義數據發布接口
    Meteor.publish('todos', function tasksPublication() {
        return todos.find({});
    });
    // 定義數據操作接口
    Meteor.methods({
        ['todos.insert'](val) {
            todos.insert(val);
        },

        ['todo.update.checked']({_id: _id, checked: _checked}) {
            Todos.update(
                { _id: _id },
                { $set: { checked: _checked } },
                { multi: true }
            );
        }
    })
}

客戶端訂閱

const handle = Meteor.subscribe('lists.public');
/**
handle:
{
.ready():boolean(true:當this.ready()明確調用,或者返回的游標的初始內容將被發送)
.stop():停止訂閱,清除緩存
}
*/

你能夠自定義錯誤信息發送到客戶端:

// on the server, pick a code unique to this error
// the reason field should be a useful debug message
throw new Meteor.Error("logged-out",
  "The user must be logged in to post a comment.");

// on the client
Meteor.call("methodName", function (error) {
  // identify the error
  if (error && error.error === "logged-out") {
    // show a nice error message
    Session.set("errorMessage", "Please log in to post a comment.");
  }
});

aldeed:collection2(自動驗證數據)

擴展 Mongo.Collection,提供對 Collection 指定 Schmea,在進行插入和更新數據時自動驗證數據模式。

// todos.collection.js
if (Meteor.isServer) {
    // 定義數據發布接口
    Meteor.publish('todos', function tasksPublication() {
        return todos.find({});
    });
    // 定義數據操作接口
    Meteor.methods({
        ['todos.insert'](val) {
            try {
                todos.insert(val);
            } catch (error) {

                if (error.sanitizedError.error == 400) {
                     // 由于驗證只在服務端,所以必須把驗證錯誤信息反饋給前臺  
                    // 發送 驗證錯誤消息
                    error.sanitizedError.error = 'ValidationErrors';
                    throw error.sanitizedError;
                }
            }
        },

        ...
    })
}
...

todos.schema = TodoSchema;

// 指定附加 Schema
todos.attachSchema(TodoSchema);
// app.vue
submit() {
      let newtodo = {
        title: this.newTodo,
        date: new Date(),
        checked: false
      };
      // 重置驗證器
      this.context.resetValidation() 

      // 調用遠程服務
      Meteor.call("todos.insert", newtodo,  (error) => {
        // 動態添加錯誤驗證消息
        if(error && error.error == 'ValidationErrors')
          this.context.addInvalidKeys(JSON.parse(error.details))
      });

      this.newTodo = "";
    },
// 響應反饋錯誤消息
 <span v-show="context.keyIsInvalid('title')" class="has-text-danger">{{context.keyErrorMessage('title')}}</span>

分頁

分頁是一種非常常見的數據訪問模式。通常有兩種分頁樣式,即“逐頁”樣式,您只能在一段時間只顯示一頁結果,從某些偏移開始(用戶可以控制),以及“無限滾動“樣式。
在逐頁技術中,如果我們要達到以下效果:

image.png

我至少要關注三個參數:

  • skip,跳過第幾頁
  • limit,每頁顯示數據數量
  • pages,總頁數

通過在 $subscribe 子屬性值中返回 vue響應屬性數組(訂閱參數),我們可以響應式向服務端發布者獲取不同頁碼數據。

//todos.supply.js
...
data() {
        return {
            // 初始化屬性值
            todos: [],
            type: false, // type 用來標志 todos 列表顯示完成或未完成
            skip: 0,    // 跳過頁數
            limit: 5,   // 顯示數據數量
            pages: 0 // 頁數
        }
    },

    meteor: {
        // 無參訂閱 todos 
        $subscribe: {
            ["todos"]: function () {
                // 響應式訂閱
                return [this.type, this.limit, this.skip] //查詢 是否已標記, 數據量, 跳躍點
            }
        },
        todos() {
            this.pages = Math.ceil(Counts.get('todosCounts')/this.limit) || 1; // 計算頁數
            return Todos.find({}, {sort: { date: -1 }});
        }
    },
...
// todos.collection.js
 Meteor.publish('todos', function tasksPublication(checked, limit, skip) {
        // 實時訂閱 todos 總數
        Counts.publish(this, 'todosCounts', todos.find({ checked: checked }));
        return todos.find({ checked: checked }, { limit: limit, skip: skip * limit, sort: { date: -1 } });
    });

tmeasday:publish-counts(實時獲取 Collection Count)

在上面代碼中,使用 tmeasday:publish-counts 包,來實時獲取 todos 總數量,再計算出總頁數。publish-counts 有以下主要方法:

  • Counts.publish [server] ,服務端代碼中訂閱集合
  • Counts.get [client]
    一旦你在服務端調用 Counts.publish , 你就可以在客戶端調用 Counts.get('name-of-counter') 去響應獲取計數器.
    該方法會返回一個整數, 返回 0 意味著你還沒發布或訂閱不成功。
  • Counts.has [client],判斷是否有指定計數器。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容