-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy path04s-publications-and-subscriptions.md.erb
executable file
·217 lines (134 loc) · 19.5 KB
/
04s-publications-and-subscriptions.md.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
---
title: Публікації та Підписки
slug: publications-and-subscriptions
date: 0004/01/02
number: 4.5
sidebar: true
contents: Зрозумієте принцип роботи публікацій та підписок.|Ознайомитесь із роботою пакета Autopublish.|Побачите більше прикладів шаблонів публікації.
paragraphs: 52
---
Публікації та підписки – це одна з основних концепцій в Meteor, проте іноді, коли ви тільки починаєте мати справу з нею буває складно зрозуміти її повністю.
Це призвело до багатьох помилок, таких як впевненість в тому, що Meteor – небезпечний, або що Meteor-додатки не в змозі справитися з великою кількістю даних.
Здебільшого це відбувається через те, що людям ці концепції спершу здавалися незрозумілими – це саме та сама "магія", що Meteor робить за нас. Хоча ця сама магія врешті дуже корисна, інколи за нею не видно того, що відбуваються за лаштунками (на те воно і магія). Тож спробуймо зазирнути глибше і зрозуміти, що ж насправді відбувається.
### Давні дні
Але спершу давайте повернемося в старий добрий 2011 рік, коли Meteor ще не було. Скажімо, що ви вирішили створити простий Rails додаток. Коли користувач заходить на свій сайт, клієнт (тобто його браузер) надсилає запит на ваш додаток, що існує на сервері.
Перше, що має зробити додаток – це з’ясувати, які дані користувач повинен побачити. Це може бути і 12-а сторінка пошуку, сторінка профілю якоїсь Мері, останні 20 твітів Боба тощо. Ви можете це сприймати як те, що продавець книжкового магазину шукає на полицях книгу, яку ви попросили.
Як тільки потрібна інформація знайдена, наступне, що робить додаток – це надає цю інформацію у вигляді HTML (або JSON у випадку API).
У метафорі про книжковий магазин, то зараз ви якраз обгорнули книгу, яку ви купили і кладете її у пакет. Це – частина "View" у популярній архітектурі "Model-View-Controller".
Нарешті, додаток бере і відправляє готовий HTML код у браузер користувача. Завдання додатку виконане, і тепер, коли все це знаходиться поза його віртуальних рук, він може просто розслабитися з пивом в очікуванні наступного запиту.
### The Meteor Way
Погляньмо, що ж таке особливе робить Meteor у порівнянні з попереднім підходом. Як ми вже бачили, ключова інновація Meteor полягає в тому, що в той час як додаток Rails живе тільки **на сервері**, Meteor має ще й клієнтську частину, яка виконується **на клієнті** (у браузері).
<%= diagram "client-server", "Pushing a subset of the database to the client.", "pull-right" %>
Це як якщо б у нашому попередньому прикладі, продавець не просто б знайшов вам потрібну книгу, але також прийшов би до вас додому, щоб почитати її вам на ніч (визнаємо, звучить моторошно).
Така архітектура дозволяє Метеор робити багато класних речей, головна з яких - те, що по суті ми маємо [базу даних і на сервері, і на клієнті](http://docs.meteor.com/#sevenprinciples). У загальних рисах, Метеор просто бере частину вашої бази даних і *копіює її на клієнт*.
Такий підхід має 2 великих відмінності: по-перше, замість того, щоб відправляти вже готовий HTML, Метеор відправляє **власне самі дані**, і дає коду на клієнті можливість самому розпоряджатися цими даними як він побажає ([дані на дроті](http://docs.meteor.com/#sevenprinciples)). По-друге, операції з цими даними будуть відбуватися практично миттєво, так як вам не потрібно буде **постійно слати запити** на сервер ([латентність компенсації](http://docs.meteor.com/#sevenprinciples)).
### Публікації
База даних додатку може мати десятки тисяч документів, деякі з яких можуть навіть бути приватними або дуже важливими. Тож, очевидно, що з причин безпеки та продуктивності нам не варто відображати на клієнті всю нашу базу даних.
Тому ми повинні мати спосіб сказати Meteor яка підмножина даних можуть бути відправлена на клієнт. Ми будемо виконувати це через публікації (publications).
Повернімося до Microscope. Ось всі наші пости, які є у базі даних:
<%= diagram "collections-1", "All the posts contained in our database.", "pull-center" %>
Хоча, цієї функції, за загальним визнанням, наразі ще немає у Microscope, ми уявимо, що деякі пости були відзначені як образливі. Хоча ми хочемо зберегти їх у базі даних, вони все ж не повинні бути доступними користувачам (тобто відправляти їх на клієнт).
Нашим першим завданням буде повідомити Meteor, які дані ми *в першу чергу* хочемо відправити на клієнт. Наприклад, тільки ті, що **відмічені** як образливі:
<%= diagram "collections-2", "Excluding flagged posts.", "pull-center" %>
Ось сервісний код, який відповідає за сервер:
~~~js
// on the server
Meteor.publish('posts', function() {
return Posts.find({flagged: false});
});
~~~
Це гарантує, що **немає ніякої можливості**, що клієнт буде мати доступ до зазначеного посту. Саме так ви можете зробити Meteor-додатки безпечними: просто переконайтеся, що ви публікуєте тільки ті дані, до яких ви хочете надати доступ на клієнті.
<% note do %>
### DDP
Базово можна розглядати публікації/підписки як трубу, яка транспортує дані з колекції на сервері (source) у колекцію на клієнті (target).
Протокол, який власне і є такою трубою, називається **DDP** (Distributed Data Protocol - протокол розподілення данних). Щоб дізнатися більше про нього, ви можете подивитися ось [цю Real-time конференцію](http://2012.realtimeconf.com/video/matt-debergalis) одного із засновників Meteor Метт Дебергалісом, або [цей скрінкаст](http://www.eventedmind.com/posts/meteor-subscriptions-and-ddp) за авторством Chris Mather, який більш детально розкриє вам основні концепції цього протоколу.
<% end %>
### Підписки
Навіть незважаючи на те, що ми хочемо відправити пости клієнту непоміченими, ми все ж не можемо відправляти їх тисячами відразу. Нам потрібен спосіб, щоб клієнт вказував, яка частина даних йому потрібна в даний момент, що саме і є місцем застосування **підписок**.
Будь-які дані, на які ви підписуєтеся, будуть **відображені** на клієнті Meteor завдяки Minimongo.
Наприклад, припустимо, що ми переглядаємо профіль якогось Боба Сміта, і хочемо, щоб відображались тільки *його* пости.
<%= diagram "collections-3", "Subscribing to Bob's posts will mirror them on the client.", "pull-center" %>
Спершу, ми трохи змінимо нашу публікацію так, щоб вона мала параметр:
~~~js
// on the server
Meteor.publish('posts', function(author) {
return Posts.find({flagged: false, author: author});
});
~~~
Коли ми *підписуємося* до цієї публікації в клієнтський код, ми визначаємо цей параметр в:
~~~js
// on the client
Meteor.subscribe('posts', 'bob-smith');
~~~
Ось як ви робите Meteor-додаток гнучким на клієнті: замість того, щоб підписуватися на *всі* доступні дані, просто натисніть та оберіть лише ті дані, які вам зараз потрібні. Таким чином, ви уникнете перевантаження пам'яті браузера, незалежно від величини вашої бази даних на клієнті.
### Вибірка
Тепер пости Боба розподілені по багатьох категоріях (наприклад: "JavaScript", "Ruby" і "Python"). Може ми і хочемо завантажити всі його пости в пам'ять браузера, але прямо зараз ми хочемо відображати тільки ті, що належать категорії "JavaScript". Якраз ось тут і потрібна "вибірка":
<%= diagram "collections-4", "Selecting a subset of documents on the client.", "pull-center" %>
Так само як ми і робили на сервері, ми будемо використовувати для цього метод `Posts.find()`:
~~~js
// on the client
Template.posts.helpers({
posts: function(){
return Posts.find({author: 'bob-smith', category: 'JavaScript'});
}
});
~~~
Тепер у нас є гарне уявлення про те, що роль публікації та підписки полягає у грі, тож давайте копати глибше і розглянемо кілька загальних шаблонів реалізації.
### Автоматична публікація
Якщо ви створюєте Meteor-проект з нуля (використовуючи `meteor create`), він відразу включатиме в себе пакет `autopublish`. У якості відправної точки, поговорим про те, що саме він робить.
Головне призначення модуля `autopublish` – це зробити максимально простим і зручним початок процесу розробки вашого Meteor-додатка, він автоматично відображає _всі серверні дані_ на клієнті, подбавши за вас про публікації та підписки.
<%= diagram "autopublish", "Autopublish", "pull-center"%>
Як це працює? Припустимо, у вас є колекція на сервері під назвою `'posts'`. Тоді пакет `autopublish` автоматично відправить кожен пост, який він знайде у колекції MongoDB в однойменну колекцію на клієнті (звичайно якщо вона існує).
Якщо ви використовуєте пакет `autopublish`, то вам не потрібно думати про публікації. Оскільки дані доступні скрізь, все просто. Звичайно ж будуть очевидні проблеми, якщо ми копіюватимемо всю нашу базу даних у кеш браузерів всіх користувачів. Тому ми цього не будемо робити.
З цієї причини, використання автоматичної публікації має сенс тільки на початку розробки, коли ще не думаєте про публікації.
### Публікація повних колекцій
Як тільки ви приберете пакет `autopublish`, ви швидко побачите, що всі дані зникли з клієнта. Найпростіший спосіб повернути їх – це просто відтворити те ж, що робить autopublish – опублікувати повну колекцію. Наприклад:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find();
});
~~~
<%= diagram "fullcollection", "Publishing a full collection", "pull-center" %>
Ми досі публікуємо повні колекції, але принаймні ми маємо контроль над тими колекціями, які ми публікуємо, а які ні. У даному випадку, ми публікуємо колекцію `Posts`, але не публікуємо `Comments`.
### Публікація частин колекції
Наступний рівень контролю – публікація тільки _частини_ колекції. Наприклад, лише постів, що належать конкретному автору:
~~~js
Meteor.publish('somePosts', function(){
return Posts.find({'author':'Tom'});
});
~~~
<%= diagram "partialcollection", "Publishing a partial collection", "pull-center" %>
<% note do %>
### За лаштунками
Якщо ви читали [документацію Meteor-публікацій](http://docs.meteor.com/#publishandsubscribe), ви напевно ознайомились з методами `added()` та `ready()` для того, щоб щоб задавати атрибути записів на клієнті і щосили намагалися зрозуміти чому у всіх Meteor-додатках, які ви бачили, вони ніколи не використовуються.
Причина полягає в тому, що Meteor надає вам дуже важливий метод: `_publishCursor()`. Ви його також ще не зустрічали? Можливо і не напряму, проте коли ви повертаєте [курсор публікації](/chapter/meteor-vocabulary/) (наприклад, `Posts.find({'author':'Tom'})`) — це якраз те, що використовує Meteor.
Коли Meteor бачить, що публікація `somePosts` повернула курсор, фреймфорк викликає метод `_publishCursor()`, щоб, як ви здогадалися, опублікувати курсор автоматично.
Ось що робить `_publishCursor()`:
- Перевіряє ім'я колекції на сервері.
- Дістає всі відповідні документи з курсора та надсилає їх в колекцію на клієнті *з таким самим ім’ям*. (Для цього він використовує метод `.added()`).
- Кожен раз, коли документ доданий, видалений або змінений, він повідомляє про ці зміни колекції на клієнті. (Він використовує метод `.observe()` на курсорі і `.added()`, `.changed()` та `removed()`).
У зазначеному вище прикладі ми можемо бути впевнені в тому, що користувач отримує тільки ті пости, в яких він зацікавлений (ті, що написав Том), і вони доступні в кеші його браузера.
<% end %>
### Часткова публікація характеристики документів
Ми побачили як можна опублікувати тільки частину всіх наших постів, але ми можемо зробити їх ще меншими! Давайте подивимося, як публікувати лише окремі *характеристики документів*.
Так само як і минулого разу, ми використаємо `find()`, щоб повернути курсор, але на цей раз ми виключимо деякі поля:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({}, {fields: {
date: false
}});
});
~~~
<%= diagram "partialproperties", "Publishing partial properties", "pull-center" %>
Звичайно, ми також можемо поєднувати обидві техніки. Наприклад, якщо б ми хотіли отримати усі пости Тома, але при цьому залишаючись осторонь їх дат, ми б написали:
~~~js
Meteor.publish('allPosts', function(){
return Posts.find({'author':'Tom'}, {fields: {
date: false
}});
});
~~~
### Підсумовування
Отже, ми побачили як перейти від публікації усіх характеристик усіх документів усіх колекцій (за допомогою `autopublish`) до публікації _частини_ властивостей _частини_ документів _частини_ колекцій.
Це охоплює основи того, що можна зробити з Meteor-публікаціями. І цих простих технік цілком достатньо для більшості випадків.
Іноді вам доведеться йти ще далі об'єднуючи, пов’язуючи або поєднуючи публікації. Про це ми поговоримо у наступному розділі