Понимание Метеор Опубликовать / Подписаться
У меня есть простое приложение, которое показывает список 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:
Вы определяете коллекции в MongoDB. Метеор еще не задействован. Эти коллекции содержат записи базы данных (также называемые "документами" как Mongo, так и Meteor, но "документ" является более общим, чем запись базы данных; например, спецификация обновления или селектор запросов также являются документами - объекты JavaScript, содержащие
field: value
пар).Затем вы определяете коллекции на сервере Meteor с помощью
MyCollection = new Mongo.Collection('collection-name-in-mongo')
Эти коллекции содержат все данные из коллекций MongoDB, и вы можете запустить
MyCollection.find({...})
на них, который будет возвращать курсор (набор записей, с методами для их итерации и возврата).Этот курсор (в большинстве случаев) используется для публикации (отправки) набора записей (называемого "набором записей"). При желании вы можете опубликовать только некоторые поля из этих записей. Это наборы записей (не коллекции), на которые подписываются клиенты. Публикация осуществляется с помощью функции публикации, которая вызывается каждый раз, когда подписывается новый клиент, и которая может принимать параметры для управления тем, какие записи возвращать (например, идентификатор пользователя, чтобы возвращать только документы этого пользователя).
На клиенте у вас есть коллекции 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
можно получить записи от:
подписка на последние 10 постов в блоге
// server Meteor.publish('posts-recent', function publishFunction() { return BlogPosts.find({}, {sort: {date: -1}, limit: 10}); } // client Meteor.subscribe('posts-recent');
подписка на посты текущего пользователя
// 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);
подписка на самые популярные посты
- и т.п.
Все эти документы поступают из 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');