Понимание Метеор Опубликовать / Подписаться

У меня есть простое приложение, которое показывает список Projects, Я удалил autopublish пакет, так что я не все отправляю клиенту.

 <template name="projectsIndex">    
   {{#each projects}}      
     {{name}}
   {{/each}}
 </template>

когда autopublish был включен, это будет отображать все проекты:

if Meteor.isClient
  Template.projectsIndex.projects = Projects.find()

С его удалением я должен дополнительно сделать:

 if Meteor.isServer
   Meteor.publish "projects", ->
     Projects.find()
 if Meteor.isClient
   Meteor.subscribe "projects"
   Template.projectsIndex.projects = Projects.find()

Итак, правильно ли говорить, что на стороне клиента find() метод только ищет записи, которые были опубликованы со стороны сервера? Это сбило меня с толку, потому что я чувствовал, что должен только звонить find() один раз.

4 ответа

Решение

Коллекции, публикации и подписки представляют собой сложную область Метеора, которую документация может обсудить более подробно, чтобы избежать частой путаницы, которая иногда усиливается путаницей в терминологии.

Вот Саша Грайф (соавтор DiscoverMeteor), объясняющий публикации и подписки на одном слайде:

Подписки

Чтобы правильно понять, зачем нужно звонить find() Вам не раз нужно понимать, как коллекции, публикации и подписки работают в Meteor:

  1. Вы определяете коллекции в MongoDB. Метеор еще не задействован. Эти коллекции содержат записи базы данных (также называемые "документами" как Mongo, так и Meteor, но "документ" является более общим, чем запись базы данных; например, спецификация обновления или селектор запросов также являются документами - объекты JavaScript, содержащие field: value пар).

  2. Затем вы определяете коллекции на сервере Meteor с помощью

    MyCollection = new Mongo.Collection('collection-name-in-mongo')
    

    Эти коллекции содержат все данные из коллекций MongoDB, и вы можете запустить MyCollection.find({...}) на них, который будет возвращать курсор (набор записей, с методами для их итерации и возврата).

  3. Этот курсор (в большинстве случаев) используется для публикации (отправки) набора записей (называемого "набором записей"). При желании вы можете опубликовать только некоторые поля из этих записей. Это наборы записей (не коллекции), на которые подписываются клиенты. Публикация осуществляется с помощью функции публикации, которая вызывается каждый раз, когда подписывается новый клиент, и которая может принимать параметры для управления тем, какие записи возвращать (например, идентификатор пользователя, чтобы возвращать только документы этого пользователя).

  4. На клиенте у вас есть коллекции Minimongo, которые частично отражают некоторые записи с сервера. "Частично", потому что они могут содержать только некоторые поля, и "некоторые записи", потому что вы обычно хотите отправить клиенту только те записи, которые ему нужны, чтобы ускорить загрузку страницы, и только те, которые ему нужны и имеют разрешение на доступ.

    Minimongo - это, по сути, непостоянная реализация Mongo в чистом JavaScript. Он служит локальным кэшем, в котором хранится только подмножество базы данных, с которой работает этот клиент. Запросы к клиенту (find) обслуживаются непосредственно из этого кэша, без связи с сервером.

    Эти коллекции Minimongo изначально пусты. Они заполнены

    Meteor.subscribe('record-set-name')
    

    звонки. Обратите внимание, что параметр для подписки не является именем коллекции; это имя набора записей, который сервер использовал в publish вызов. subscribe() call подписывает клиента на набор записей - подмножество записей из коллекции серверов (например, последние 100 сообщений в блоге), со всеми или подмножеством полей в каждой записи (например, только title а также date). Как Minimongo узнает, в какую коллекцию помещать входящие записи? Название коллекции будет collection аргумент, используемый в обработчике публикации added, changed, а также removed обратные вызовы, или если они отсутствуют (что имеет место в большинстве случаев), это будет имя коллекции MongoDB на сервере.

Изменение записей

Именно здесь Meteor делает вещи очень удобными: когда вы изменяете запись (документ) в коллекции Minimongo на клиенте, Meteor мгновенно обновляет все шаблоны, которые зависят от него, а также отправляет изменения обратно на сервер, который, в свою очередь, сохранит изменения в MongoDB и отправит их соответствующим клиентам, которые подписались на набор записей, включающий этот документ. Это называется компенсацией задержки и является одним из семи основных принципов Метеора.

Несколько подписок

Вы можете иметь несколько подписок, которые извлекают разные записи, но все они окажутся в одной коллекции на клиенте, если они поступили из одной коллекции на сервере, в зависимости от их _id, Это не объясняется четко, но подразумевается в документах Meteor:

Когда вы подписываетесь на набор записей, он сообщает серверу отправлять записи клиенту. Клиент хранит эти записи в локальных коллекциях Minimongo с тем же именем, что и collection аргумент, используемый в обработчике публикации added, changed, а также removed Обратные вызовы. Meteor будет помещать в очередь входящие атрибуты, пока вы не объявите коллекцию Mongo.Collection на клиенте с соответствующим именем коллекции.

Что не объясняется, так это то, что происходит, когда вы явно не используете added, changed а также removed или публиковать обработчики вообще - что происходит в большинстве случаев. В этом наиболее распространенном случае аргумент коллекции (неудивительно) берется из имени коллекции MongoDB, которую вы объявили на сервере на шаге 1. Но это означает, что у вас могут быть разные публикации и подписки с разными именами, и все записи окажутся в одной коллекции на клиенте. Вплоть до уровня полей верхнего уровня, Meteor позаботится о том, чтобы установить объединение среди документов, чтобы подписки могли перекрываться - публиковать функции, которые отправляют различные поля верхнего уровня клиенту, работая бок о бок и на клиенте, документ в коллекция будет объединением двух наборов полей.

Пример: несколько подписок заполняют одну коллекцию на клиенте

У вас есть коллекция BlogPosts, которую вы объявляете одинаково как на сервере, так и на клиенте, даже если она выполняет разные функции:

BlogPosts = new Mongo.Collection('posts');

На клиенте, BlogPosts можно получить записи от:

  1. подписка на последние 10 постов в блоге

    // server
    Meteor.publish('posts-recent', function publishFunction() {
      return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
    
  2. подписка на посты текущего пользователя

    // server
    Meteor.publish('posts-current-user', function publishFunction() {
      return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});
      // this.userId is provided by Meteor - http://docs.meteor.com/#publish_userId
    }
    Meteor.publish('posts-by-user', function publishFunction(who) {
      return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10});
    }
    
    // client
    Meteor.subscribe('posts-current-user');
    Meteor.subscribe('posts-by-user', someUser);
    
  3. подписка на самые популярные посты

  4. и т.п.

Все эти документы поступают из posts сбор в MongoDB, через BlogPosts сбор на сервере, и в конечном итоге в BlogPosts сбор на клиенте.

Теперь мы можем понять, почему вам нужно позвонить find() более одного раза - во второй раз на клиенте, потому что документы из всех подписок окажутся в одной коллекции, и вам нужно выбрать только те, которые вам нужны. Например, чтобы получить самые последние сообщения на клиенте, вы просто зеркально отображаете запрос с сервера:

var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});

Это вернет курсор ко всем документам / записям, которые клиент уже получил, как к верхним публикациям, так и к публикациям пользователя. ( спасибо Джеффри).

Да, клиентская функция find() возвращает только те документы, которые находятся на клиенте в Minimongo. Из документов:

На клиенте создается экземпляр Minimongo. Minimongo - это, по сути, непостоянная реализация Mongo в чистом JavaScript. Он служит локальным кэшем, в котором хранится только подмножество базы данных, с которой работает этот клиент. Запросы к клиенту (find) обслуживаются непосредственно из этого кэша, без связи с сервером.

Как вы говорите, publish() указывает, какие документы будут иметь клиент.

Основное правило большого пальца здесь publish а также subscribed имена переменных должны быть одинаковыми на стороне клиента и сервера.

Имена коллекций на БД Mongo и на стороне клиента должны быть одинаковыми.

Предположим, что я использую опубликовать и подписаться на мою коллекцию с именем employees тогда код будет выглядеть


серверная сторона

Здесь использование var ключевое слово необязательно (используйте это ключевое слово, чтобы сделать коллекцию локальной для этого файла).

CollectionNameOnServerSide = new Mongo.Collection('employees');   

Meteor.publish('employeesPubSub', function() { 
    return CollectionNameOnServerSide.find({});     
});

файл на стороне клиента.js

CollectionNameOnClientSide = new Mongo.Collection('employees');
var employeesData = Meteor.subscribe('employeesPubSub');

Template.templateName.helpers({
  'subcribedDataNotAvailable' : function(){
        return !employeesData.ready();
    },
   'employeeNumbers' : () =>{
       CollectionNameOnClientSide.find({'empId':1});
  }
});

HTML-файл на стороне клиента

Здесь мы можем использовать subcribedDataNotAvailable вспомогательный метод, чтобы узнать, готовы ли данные на стороне клиента, если данные готовы, то распечатайте номера сотрудников, используя employeeNumbers вспомогательный метод.

<TEMPLATE name="templateName">
{{#if subcribedDataNotAvailable}}
   <h1> data loading ... </h1>
 {{else}}
  {{#each employeeNumbers }}
     {{this}}
  {{/each}}
 {{/if}}
<TEMPLATE>
// on the server
Meteor.publish('posts', function() {

    return Posts.find();

});

// on the client
Meteor.subscribe('posts');
Другие вопросы по тегам