当 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阅读 199,478评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,825评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,482评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,726评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,633评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,018评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,168评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,320评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,264评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,288评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,995评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,587评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,909评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,284评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,862评论 2 339

推荐阅读更多精彩内容