Grundlegendes zu Meteor Publish / Subscribe


84

Ich habe eine einfache App eingerichtet, die eine Liste von zeigt Projects. Ich habe das autopublishPaket entfernt , damit ich nicht alles an den Client sende.

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

Wenn autopublishdiese Option aktiviert ist, werden alle Projekte angezeigt:

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

Wenn es entfernt ist, muss ich zusätzlich tun:

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

Ist es also richtig zu sagen, dass die clientseitige find()Methode nur Datensätze durchsucht, die vom Server veröffentlicht wurden? Es hat mich gestolpert, weil ich das Gefühl hatte, ich sollte nur einmal anrufen find().

Antworten:


286

Sammlungen, Veröffentlichungen und Abonnements sind ein schwieriger Bereich von Meteor, auf den in der Dokumentation näher eingegangen werden könnte, um häufige Verwirrung zu vermeiden , die manchmal durch verwirrende Terminologie verstärkt wird .

Hier erklärt Sacha Greif (Co-Autor von DiscoverMeteor ) Veröffentlichungen und Abonnements auf einer Folie:

Abonnements

Um zu verstehen, warum Sie find()mehrmals anrufen müssen, müssen Sie wissen, wie Sammlungen, Veröffentlichungen und Abonnements in Meteor funktionieren:

  1. Sie definieren Sammlungen in MongoDB. Noch kein Meteor beteiligt. Diese Sammlungen enthalten Datenbankeinträge (auch als „Dokumente“ sowohl von Mongo und Meteor , sondern ein „Dokument“ ist allgemeiner als eine Datenbank - Datensatz, zum Beispiel, eine Update - Spezifikation oder eine Abfrage Wähler sind Dokumente zu - JavaScript - Objekte enthalten field: valuePaare).

  2. Anschließend definieren Sie Sammlungen auf dem Meteor-Server mit

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

    Diese Sammlungen enthalten alle Daten aus den MongoDB-Sammlungen, und Sie können sie ausführen MyCollection.find({...}), wodurch ein Cursor zurückgegeben wird (eine Reihe von Datensätzen mit Methoden zum Durchlaufen und Zurückgeben).

  3. Dieser Cursor wird (meistens) zum Veröffentlichen (Senden) einer Reihe von Datensätzen (als "Datensatzgruppe" bezeichnet ) verwendet. Sie können optional nur einige Felder aus diesen Datensätzen veröffentlichen. Es sind Datensatzgruppen ( keine Sammlungen), die Clients abonnieren . Die Veröffentlichung erfolgt über eine Veröffentlichungsfunktion , die jedes Mal aufgerufen wird, wenn ein neuer Client abonniert, und die Parameter verwenden kann, um zu verwalten, welche Datensätze zurückgegeben werden sollen (z. B. eine Benutzer-ID, um nur die Dokumente dieses Benutzers zurückzugeben).

  4. Auf dem Client haben Sie Minimongo Sammlungen , die teilweise spiegeln einige der Datensätze vom Server. "Teilweise", weil sie möglicherweise nur einige der Felder enthalten, und "einige der Datensätze", weil Sie normalerweise nur die benötigten Datensätze an den Client senden möchten, um das Laden der Seite zu beschleunigen, und nur die Datensätze, die er benötigt und für die er die Berechtigung hat Zugriff.

    Minimongo ist im Wesentlichen eine speicherinterne, nicht persistente Implementierung von Mongo in reinem JavaScript. Es dient als lokaler Cache, in dem nur die Teilmenge der Datenbank gespeichert wird, mit der dieser Client arbeitet. Abfragen auf dem Client (find) werden direkt aus diesem Cache heraus bereitgestellt, ohne mit dem Server zu sprechen.

    Diese Minimongo-Sammlungen sind zunächst leer. Sie sind gefüllt mit

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

    Anrufe. Beachten Sie, dass der zu abonnierende Parameter kein Sammlungsname ist. Dies ist der Name eines Datensatzes , den der Server im publishAufruf verwendet hat. Der subscribe()Aufruf abonniert den Client für einen Datensatz - eine Teilmenge von Datensätzen aus der Serversammlung (z. B. die letzten 100 Blog-Beiträge) mit allen oder einer Teilmenge der Felder in jedem Datensatz (z . B. nur titleund date). Woher weiß Minimongo, in welche Sammlung die eingehenden Datensätze gestellt werden sollen? Der Name der Sammlung wird das sein collectionArgument in den veröffentlichen Handler verwendet added, changedund removedRückrufe, oder wenn letztere fehlen (was der Fall die meiste Zeit ist), wird es der Name der MongoDB - Sammlung auf dem Server sein.

Datensätze ändern

Hier macht Meteor die Dinge sehr bequem: Wenn Sie einen Datensatz (ein Dokument) in der Minimongo-Sammlung auf dem Client ändern, aktualisiert Meteor sofort alle davon abhängigen Vorlagen und sendet die Änderungen an den Server zurück, der dies wiederum tut speichert die Änderungen in MongoDB und sendet sie an die entsprechenden Clients, die einen Datensatz mit diesem Dokument abonniert haben. Dies wird als Latenzkompensation bezeichnet und ist eines der sieben Kernprinzipien von Meteor .

Mehrere Abonnements

Sie können eine Reihe von Abonnements haben, die unterschiedliche Datensätze abrufen, aber alle landen auf dem Client in derselben Sammlung, wenn sie auf der Grundlage ihrer Daten aus derselben Sammlung auf dem Server stammen _id. Dies wird nicht klar erklärt, sondern durch die Meteor-Dokumente impliziert:

Wenn Sie einen Datensatz festlegen, wird der Server angewiesen, Datensätze an den Client zu senden. Der Client speichert diese Aufzeichnungen in lokalen Minimongo Sammlungen, mit dem gleichen Namen wie das collectionverwendete Argument in der Handler veröffentlichen added, changedund removedRückrufe. Meteor stellt eingehende Attribute in die Warteschlange, bis Sie die Mongo.Collection auf dem Client mit dem passenden Sammlungsnamen deklarieren.

Was nicht erklärt ist , was passiert , wenn Sie nicht tun explizit verwenden added, changedund removed, oder veröffentlichen Handler überhaupt - die meiste Zeit ist. In diesem häufigsten Fall wird das Sammlungsargument (nicht überraschend) dem Namen der MongoDB-Sammlung entnommen, die Sie in Schritt 1 auf dem Server deklariert haben. Dies bedeutet jedoch, dass Sie unterschiedliche Veröffentlichungen und Abonnements mit unterschiedlichen Namen und alle Namen haben können Datensätze landen in derselben Sammlung auf dem Client. Bis auf die Ebene der Felder der obersten Ebene achtet Meteor darauf, eine festgelegte Vereinigung zwischen Dokumenten durchzuführen, sodass sich Abonnements überschneiden können. Veröffentlichen Sie Funktionen, die verschiedene Felder der obersten Ebene nebeneinander an den Client senden, und auf dem Client das Dokument im Sammlung wird die seinVereinigung der beiden Feldsätze .

Beispiel: Mehrere Abonnements füllen dieselbe Sammlung auf dem Client

Sie haben eine BlogPosts-Sammlung, die Sie sowohl auf dem Server als auch auf dem Client auf dieselbe Weise deklarieren, obwohl sie verschiedene Aktionen ausführt:

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

Auf dem Client BlogPostskönnen Datensätze abgerufen werden von:

  1. ein Abonnement für die letzten 10 Blog-Beiträge

    // server
    Meteor.publish('posts-recent', function publishFunction() {
      return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
  2. ein Abonnement für die Beiträge des aktuellen Benutzers

    // 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. ein Abonnement für die beliebtesten Beiträge

  4. etc.

Alle diese Dokumente stammen aus der postsSammlung in MongoDB über die BlogPostsSammlung auf dem Server und landen in der BlogPostsSammlung auf dem Client.

Jetzt können wir verstehen, warum Sie find()mehr als einmal anrufen müssen - das zweite Mal auf dem Client, da Dokumente aus allen Abonnements in derselben Sammlung landen und Sie nur diejenigen abrufen müssen, die Ihnen wichtig sind. Um beispielsweise die neuesten Beiträge auf dem Client abzurufen, spiegeln Sie einfach die Abfrage vom Server:

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

Dadurch wird ein Cursor auf alle Dokumente / Datensätze zurückgegeben, die der Kunde bisher erhalten hat, sowohl auf die Top-Beiträge als auch auf die Beiträge des Benutzers. ( Danke Geoffrey ).


10
Das ist toll. Erwähnenswert ist möglicherweise, was passiert, wenn Sie BlogPosts.find({})nach dem Abonnieren beider Veröffentlichungen auf dem Client etwas tun - dh es wird ein Cursor aller Dokumente / Datensätze zurückgegeben, die sich derzeit auf dem Client befinden, sowohl die Top-Beiträge als auch die Beiträge des Benutzers. Ich habe andere Fragen zu SO gesehen, bei denen der Fragesteller dadurch verwirrt war.
Geoffrey Booth

3
Das ist toll. Vielen Dank. Darüber hinaus wird die Meteor.users () - Auflistung etwas verwirrend, da sie auf der Clientseite automatisch veröffentlicht wird. Kann der obigen Antwort ein Bit hinzugefügt werden, um die Benutzer () -Sammlung anzugeben?
Jimmy MG Lim

3
Auch wenn viel mehr als ursprünglich verlangt wurde, denke ich, dass @DVG diese großartige Beschreibung als akzeptierte Antwort markieren sollte. Danke Dan.
Physiocoder

1
Vielen Dank an @DanDascalescu, eine großartige Erklärung, die viel für mich geklärt hat. Das einzige, was ich beim Lesen von Meteor-Dokumenten über "Sammlungen" nach dem Lesen Ihrer Erklärung für BlogPostskeine Sammlung halte, ist das zurückgegebene Objekt mit Methoden wie "Einfügen", "Aktualisieren" "..etc, und die eigentliche Sammlung befindet sich auch postsim Client und auf dem Server.
UXE

4
Ist es möglich, nur den Datensatz anzurufen, den Sie abonniert haben? Ist es wie in möglich, den Datensatz direkt in meinem Javascript abzurufen, anstatt die Minimongo-Datenbank lokal abzufragen?
Jimmy Knoot

27

Ja, das clientseitige find () gibt nur Dokumente zurück, die sich auf dem Client in Minimongo befinden. Aus Dokumenten :

Auf dem Client wird eine Minimongo-Instanz erstellt. Minimongo ist im Wesentlichen eine speicherinterne, nicht persistente Implementierung von Mongo in reinem JavaScript. Es dient als lokaler Cache, in dem nur die Teilmenge der Datenbank gespeichert wird, mit der dieser Client arbeitet. Abfragen auf dem Client (find) werden direkt aus diesem Cache heraus bereitgestellt, ohne mit dem Server zu sprechen.

Wie Sie sagen, gibt Publish () an, über welche Dokumente der Client verfügen wird.


1

Grundlegende Daumenregel ist hier publishund subscribedVariablennamen sollten auf Client- und Serverseite gleich sein.

Sammlungsnamen auf Mongo DB- und Clientseite sollten identisch sein.

Angenommen, ich verwende Publish und Subscribe für meine Sammlung mit dem Namen " employeesCode"


Serverseite

Hier ist die Verwendung des varSchlüsselworts optional (verwenden Sie dieses Schlüsselwort, um die Sammlung für diese Datei lokal zu machen).

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

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

clientseitige .js-Datei

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

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

clientseitige HTML-Datei

Hier können wir mithilfe der subcribedDataNotAvailableHilfsmethode feststellen , ob Daten auf Client-Seite bereit sind. Wenn Daten bereit sind, drucken Sie die Mitarbeiternummern mithilfe der employeeNumbersHilfsmethode.

<TEMPLATE name="templateName">
{{#if subcribedDataNotAvailable}}
   <h1> data loading ... </h1>
 {{else}}
  {{#each employeeNumbers }}
     {{this}}
  {{/each}}
 {{/if}}
<TEMPLATE>

0
// on the server
Meteor.publish('posts', function() {

    return Posts.find();

});

// on the client
Meteor.subscribe('posts');
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.