Hiểu về Meteor Xuất bản / Đăng ký


84

Tôi đã thiết lập một ứng dụng đơn giản hiển thị danh sách Projects. Tôi đã xóa autopublishgói để không gửi mọi thứ cho khách hàng.

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

Khi autopublishđược bật, điều này sẽ hiển thị tất cả các dự án:

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

Khi nó bị xóa, tôi phải làm thêm:

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

Vì vậy, có chính xác không khi nói rằng find()phương pháp phía máy khách chỉ tìm kiếm các bản ghi đã được xuất bản từ phía máy chủ? Nó đã làm tôi vấp ngã vì tôi cảm thấy mình chỉ nên gọi find()một lần.

Câu trả lời:


286

Các bộ sưu tập, ấn phẩm và đăng ký là một lĩnh vực khó khăn của Meteor mà tài liệu có thể thảo luận chi tiết hơn, để tránh nhầm lẫn thường xuyên , đôi khi bị khuếch đại bởi thuật ngữ khó hiểu .

Đây là Sacha Greif (đồng tác giả của DiscoverMeteor ) giải thích các ấn phẩm và đăng ký trong một trang trình bày:

đăng ký

Để hiểu đúng lý do tại sao bạn cần gọi find()nhiều lần, bạn cần hiểu cách hoạt động của các bộ sưu tập, ấn phẩm và đăng ký trong Meteor:

  1. Bạn xác định bộ sưu tập trong MongoDB. Chưa có Meteor nào tham gia. Những bộ sưu tập chứa các hồ sơ cơ sở dữ liệu (hay còn gọi là "tài liệu" bởi cả hai Mongo và Meteor , nhưng một "tài liệu" là tổng quát hơn mức kỷ lục cơ sở dữ liệu, ví dụ, một đặc điểm kỹ thuật cập nhật hoặc chọn truy vấn là các tài liệu quá - JavaScript đối tượng chứa field: valuecặp).

  2. Sau đó, bạn xác định các bộ sưu tập trên máy chủ Meteor với

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

    Các bộ sưu tập này chứa tất cả dữ liệu từ các bộ sưu tập MongoDB và bạn có thể chạy MyCollection.find({...})trên chúng, nó sẽ trả về một con trỏ (một tập hợp các bản ghi, với các phương thức để lặp qua chúng và trả về chúng).

  3. Con trỏ này (hầu hết thời gian) được sử dụng để xuất bản (gửi) một tập hợp các bản ghi (được gọi là "tập bản ghi" ). Bạn có thể tùy ý xuất bản chỉ một số trường từ các bản ghi đó. Nó là các bộ hồ sơ ( không phải bộ sưu tập) mà khách hàng đăng ký . Việc xuất bản được thực hiện bởi một hàm xuất bản , được gọi mỗi khi một khách hàng mới đăng ký và có thể nhận các tham số để quản lý bản ghi nào sẽ trả về (ví dụ: id người dùng, để chỉ trả lại tài liệu của người dùng đó).

  4. Trên máy khách , bạn có bộ sưu tập Minimongo phản chiếu một phần một số bản ghi từ máy chủ. "Một phần" vì chúng có thể chỉ chứa một số trường và "một số bản ghi" bởi vì bạn thường chỉ muốn gửi cho máy khách những bản ghi mà nó cần, để tăng tốc độ tải trang và chỉ những bản nó cần có quyền truy cập.

    Minimongo về cơ bản là một triển khai trong bộ nhớ, không liên tục của Mongo trong JavaScript thuần túy. Nó phục vụ như một bộ nhớ cache cục bộ chỉ lưu trữ tập hợp con của cơ sở dữ liệu mà ứng dụng khách này đang làm việc. Các truy vấn trên máy khách (tìm) được phục vụ trực tiếp từ bộ nhớ cache này mà không cần trao đổi với máy chủ.

    Các bộ sưu tập Minimongo này ban đầu trống. Chúng được lấp đầy bởi

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

    cuộc gọi. Lưu ý rằng tham số để đăng ký không phải là tên bộ sưu tập; đó là tên của một tập hợp bản ghi mà máy chủ đã sử dụng trong publishcuộc gọi. Lệnh subscribe()gọi đăng ký máy khách vào một tập hợp bản ghi - một tập hợp con các bản ghi từ bộ sưu tập máy chủ (ví dụ: 100 bài đăng blog gần đây nhất), với tất cả hoặc một tập hợp con của các trường trong mỗi bản ghi (ví dụ: chỉ titledate). Làm thế nào Minimongo biết được vào bộ sưu tập nào để đặt các bản ghi đến? Tên của bộ sưu tập sẽ là collectionlập luận được sử dụng trong các công bố của bộ xử lý added, changedremovedcallbacks, hoặc nếu những người đang mất tích (đó là trường hợp hầu hết thời gian), nó sẽ là tên của bộ sưu tập MongoDB trên máy chủ.

Sửa đổi hồ sơ

Đây là lúc Meteor làm cho mọi thứ trở nên rất thuận tiện: khi bạn sửa đổi bản ghi (tài liệu) trong bộ sưu tập Minimongo trên máy khách, Meteor sẽ cập nhật ngay lập tức tất cả các mẫu phụ thuộc vào nó và cũng sẽ gửi các thay đổi trở lại máy chủ, lần lượt sẽ lưu trữ các thay đổi trong MongoDB và sẽ gửi chúng đến các ứng dụng khách thích hợp đã đăng ký một bộ hồ sơ bao gồm tài liệu đó. Đây được gọi là bù độ trễ và là một trong bảy nguyên tắc cốt lõi của Meteor .

Nhiều đăng ký

Bạn có thể có một loạt các đăng ký kéo các bản ghi khác nhau, nhưng tất cả chúng sẽ kết thúc trong cùng một bộ sưu tập trên máy khách nếu chúng đến từ cùng một bộ sưu tập trên máy chủ, dựa trên chúng _id. Điều này không được giải thích rõ ràng, nhưng được ngụ ý bởi các tài liệu về Meteor:

Khi bạn đăng ký một tập hợp bản ghi, nó sẽ yêu cầu máy chủ gửi các bản ghi đến máy khách. Các cửa hàng khách hàng những hồ sơ trong các bộ sưu tập Minimongo địa phương, với tên giống như collectionlập luận được sử dụng trong các công bố của bộ xử lý added, changedremovedcallbacks. Meteor sẽ xếp hàng các thuộc tính đến cho đến khi bạn khai báo Mongo.Collection trên máy khách với tên bộ sưu tập phù hợp.

Có gì không được giải thích là những gì xảy ra khi bạn không sử dụng một cách rõ ràng added, changedremoved, hoặc xuất bản xử lý ở tất cả - đó là hầu hết thời gian. Trong trường hợp phổ biến nhất này, đối số tập hợp (không có gì đáng ngạc nhiên) được lấy từ tên của tập hợp MongoDB mà bạn đã khai báo trên máy chủ ở bước 1. Nhưng điều này có nghĩa là bạn có thể có các ấn phẩm và đăng ký khác nhau với các tên khác nhau và tất cả các bản ghi sẽ kết thúc trong cùng một bộ sưu tập trên máy khách. Xuống đến cấp của các trường cấp cao nhất , Meteor đảm nhận việc thực hiện liên kết tập hợp giữa các tài liệu, sao cho các đăng ký có thể chồng chéo lên nhau - xuất bản các chức năng vận chuyển các trường cấp cao nhất khác nhau cho máy khách làm việc song song với máy khách, tài liệu trong bộ sưu tập sẽ làsự kết hợp của hai nhóm trường .

Ví dụ: nhiều đăng ký lấp đầy cùng một bộ sưu tập trên máy khách

Bạn có một bộ sưu tập BlogPosts, mà bạn khai báo theo cùng một cách trên cả máy chủ và máy khách, mặc dù nó thực hiện những việc khác nhau:

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

Trên máy khách, BlogPostscó thể lấy hồ sơ từ:

  1. đăng ký 10 bài đăng trên blog gần đây nhất

    // server
    Meteor.publish('posts-recent', function publishFunction() {
      return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
    
  2. đăng ký các bài đăng của người dùng hiện tại

    // 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. đăng ký các bài đăng phổ biến nhất

  4. Vân vân.

Tất cả các tài liệu này đến từ postsbộ sưu tập trong MongoDB, thông qua BlogPostsbộ sưu tập trên máy chủ và kết thúc trong BlogPostsbộ sưu tập trên máy khách.

Bây giờ chúng tôi có thể hiểu tại sao bạn cần gọi find()nhiều lần - lần thứ hai trên máy khách, vì tài liệu từ tất cả các đăng ký sẽ kết thúc trong cùng một bộ sưu tập và bạn chỉ cần tìm nạp những tài liệu bạn quan tâm. Ví dụ: để nhận các bài đăng gần đây nhất trên máy khách, bạn chỉ cần phản chiếu truy vấn từ máy chủ:

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

Thao tác này sẽ trả về một con trỏ đến tất cả các tài liệu / bản ghi mà khách hàng đã nhận được cho đến nay, cả bài đăng hàng đầu và bài đăng của người dùng. ( cảm ơn Geoffrey ).


10
Điều đó thật tuyệt. Có thể đáng nói là điều gì sẽ xảy ra nếu bạn thực hiện BlogPosts.find({})trên máy khách sau khi đăng ký cả hai ấn phẩm — tức là nó sẽ trả về con trỏ của tất cả các tài liệu / bản ghi hiện có trên máy khách, cả bài đăng hàng đầu và bài đăng của người dùng. Tôi đã thấy các câu hỏi khác trên SO mà người hỏi đã bối rối vì điều này.
Geoffrey Booth

3
Điều đó thật tuyệt. cảm ơn. Ngoài ra, bộ sưu tập Meteor.users () hơi khó hiểu vì nó được xuất bản tự động ở phía máy khách. Có thể thêm một chút vào câu trả lời trên để nêu rõ bộ sưu tập users () không?
Jimmy MG Lim

3
Ngay cả khi nhiều hơn yêu cầu ban đầu, tôi nghĩ @DVG nên đánh dấu bài viết tuyệt vời này là câu trả lời được chấp nhận. Cảm ơn Dan.
vật lý học

1
Cảm ơn @DanDascalescu, Lời giải thích tuyệt vời đã giải thích cho tôi rất nhiều điều duy nhất mà khi theo dõi tài liệu về sao băng về "bộ sưu tập" sau khi đọc lời giải thích của bạn, tôi nghĩ đó BlogPostskhông phải là bộ sưu tập, nó là đối tượng trả về có các phương thức như "insert", "update ".. vv, và bộ sưu tập thực sự cũng nằm poststrong máy khách và máy chủ.
UXE

4
Có thể chỉ gọi cho bộ ghi mà bạn đã đăng ký không? Như trong, có thể lấy trực tiếp bản ghi được đặt trong javascript của tôi, thay vì truy vấn db Minimongo cục bộ không?
Jimmy Knoot

27

Có, find () phía máy khách chỉ trả về các tài liệu có trên máy khách trong Minimongo. Từ tài liệu :

Trên máy khách, một phiên bản Minimongo được tạo. Minimongo về cơ bản là một triển khai trong bộ nhớ, không liên tục của Mongo trong JavaScript thuần túy. Nó phục vụ như một bộ nhớ cache cục bộ chỉ lưu trữ tập hợp con của cơ sở dữ liệu mà ứng dụng khách này đang làm việc. Các truy vấn trên máy khách (tìm) được phục vụ trực tiếp từ bộ nhớ cache này mà không cần trao đổi với máy chủ.

Như bạn nói, công bố () chỉ định các tài liệu mà khách hàng sẽ có.


1

Quy tắc ngón tay cái cơ bản ở đây là publishsubscribedcác tên biến phải giống nhau ở phía máy khách và máy chủ.

Tên bộ sưu tập trên Mongo DB và phía máy khách phải giống nhau.

Giả sử rằng tôi đang sử dụng xuất bản và đăng ký cho bộ sưu tập của mình được đặt tên employeesthì mã sẽ giống như


phía máy chủ

Ở đây việc sử dụng vartừ khóa là tùy chọn (sử dụng từ khóa này để tạo cục bộ cho tập tin này).

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

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

tệp .js phía máy khách

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

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

tệp .html phía máy khách

Ở đây chúng ta có thể sử dụng subcribedDataNotAvailablephương thức helper để biết dữ liệu đã sẵn sàng ở phía máy khách chưa, nếu dữ liệu đã sẵn sàng thì in số nhân viên bằng employeeNumbersphương thức helper.

<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');
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.