mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
533 lines
20 KiB
HTML
533 lines
20 KiB
HTML
<template name="concepts">
|
|
{{#better_markdown}}
|
|
|
|
<h1 id="concepts">Concepts</h1>
|
|
|
|
We've written our fair share of single-page JS applications by hand.
|
|
Writing an entire application in one language (JavaScript) with one
|
|
data format (JSON) is a real joy. Meteor is everything we wanted
|
|
when writing those apps.
|
|
|
|
{{> structure }}
|
|
{{> data }}
|
|
{{> reactivity }}
|
|
{{> livehtml }}
|
|
{{> templates }}
|
|
{{> packages_concept }}
|
|
{{> deploying }}
|
|
|
|
{{/better_markdown}}
|
|
</template>
|
|
|
|
<template name="structure">
|
|
{{#better_markdown}}
|
|
|
|
<h2 id="structuringyourapp">Structuring your application</h2>
|
|
|
|
A Meteor application is a mix of JavaScript that runs inside a
|
|
client web browser, JavaScript that runs on the Meteor server inside
|
|
a Node.js container, and all the supporting HTML fragments, CSS rules,
|
|
and static assets. Meteor automates the packaging and transmission
|
|
of these different components. And, it is quite flexible about how
|
|
you choose to structure those components in your file tree.
|
|
|
|
The only server asset is JavaScript. Meteor gathers all your JS
|
|
files, excluding anything under the `client`
|
|
and `public` subdirectories, and loads them into a Node.js
|
|
server instance inside a fiber. In Meteor, your server code runs in
|
|
a single thread per request, not in the asynchronous callback style
|
|
typical of Node. We find the linear execution model a better fit for
|
|
the typical server code in a Meteor application.
|
|
|
|
There are more assets to consider on the client side. Meteor
|
|
gathers all JavaScript files in your tree with the exception of
|
|
the `server` and `public` subdirectories for the
|
|
client. It minifies this bundle and serves it to each new client.
|
|
You're free to use a single JS file for your entire application, or
|
|
create a nested tree of separate files, or anything in between.
|
|
|
|
Files outside the `client` and `server`
|
|
subdirectories are loaded on both the client and the server! That's
|
|
the place for model definitions and other functions. Also, instead of
|
|
putting client and server functions in different directories, you can
|
|
use the [`is_client` and `is_server`](#meteor_is_client) variables
|
|
to isolate JS to one or the other side.
|
|
|
|
CSS files work just the same: the client will get a bundle with all
|
|
the CSS in your tree (excluding the `server`
|
|
and `public` subdirectories).
|
|
|
|
In development mode, JS and CSS files are sent individually to make
|
|
debugging easier.
|
|
|
|
HTML files in a Meteor application are treated quite a bit differently
|
|
from a server-side framework. Meteor scans all the HTML files in your
|
|
directory for three top-level elements: `<head>`, `<body>`, and
|
|
`<template>`. The head and body sections are seperately concatenated
|
|
into a single head and body, which are transmitted to the client on
|
|
initial page load.
|
|
|
|
Template sections, on the other hand, are converted into JavaScript
|
|
functions, available under the `Template` namespace. It's
|
|
a really convenient way to ship HTML templates to the client.
|
|
See the [templates](#templates) section for more.
|
|
|
|
Lastly, the Meteor server will serve any files under
|
|
the `public` directory, just like in a Rails or Django
|
|
project. This is the place for images, favicon, robots.txt, and
|
|
anything else.
|
|
|
|
{{/better_markdown}}
|
|
</template>
|
|
|
|
<template name="data">
|
|
{{#better_markdown}}
|
|
|
|
<h2 id="data">Data</h2>
|
|
|
|
Meteor makes writing distributed client code as simple as talking to a
|
|
local database. It's a clean and simple approach, much easier than
|
|
building individual RPC endpoints, slow roundtrips to the server, and
|
|
orchestrating invalidation messages.
|
|
|
|
A Meteor application's main data store is MongoDB, which
|
|
holds <b>collections</b> of individual <b>documents</b>. Your server
|
|
code has direct access to that database. Your client code
|
|
has <i>simulated</i> direct access to that database.
|
|
|
|
Every Meteor client includes an in-memory database cache. Each
|
|
client's cache holds valid copies of some set of documents. When a
|
|
matching document in the server's master database changes, Meteor
|
|
automatically synchronizes that change to every subscribed client.
|
|
|
|
To manage the client caches, your server code <b>publishes</b> sets of
|
|
documents, and your client code <b>subscribes</b> to those sets. For
|
|
example, if you are building a chat system, the server might publish two
|
|
sets: the set of all rooms, and the set of all messages in a given room.
|
|
Each client would subscribe to the master set of available rooms and the
|
|
set of messages in the currently-selected room. Once subscribed, the
|
|
client uses its cache as a fast local database, dramatically simplifying
|
|
your client model code.
|
|
|
|
// server: publish all room documents, and per-room messages
|
|
Meteor.publish("chatrooms");
|
|
Meteor.publish("messages", function (room_id) {
|
|
return Messages.find({room: room_id});
|
|
});
|
|
|
|
// client: subscribe to all rooms, and messages in the first room
|
|
Meteor.subscribe("chatrooms");
|
|
Meteor.subscribe("messages", Chatrooms.find()[0]._id);
|
|
|
|
Document modifications also propagate automatically. To insert, update,
|
|
or remove a document, client code uses the familiar <a target="_blank"
|
|
href="http://www.mongodb.org/display/DOCS/Manual">MongoDB API</a>. That
|
|
change instruction is executed immediately on the client's cached
|
|
data. <i>At the same time</i>, the client sends that instruction up to
|
|
the server, which executes the same change against the master database.
|
|
Usually the client and server agree, but should they differ (permissions
|
|
checking or overlapping with another client, for example), the server's
|
|
result will publish back down to the client. And of course, all other
|
|
clients with a matching subscription automatically receive an updated
|
|
document.
|
|
|
|
// create new message, executes on both client and server.
|
|
Messages.insert({room: 2413, text: "hello!"});
|
|
|
|
Putting it all together, these techniques accomplish <i>latency
|
|
compensation</i>. Clients hold a fresh copy of the data they need, and
|
|
never need to wait for a roundtrip to the server. And when clients
|
|
modify data, those modifications can run locally without waiting for the
|
|
confirmation from the server, while still giving the server final say
|
|
over the requested change.
|
|
|
|
{{/better_markdown}}
|
|
</template>
|
|
|
|
<template name="reactivity">
|
|
{{#better_markdown}}
|
|
<h2 id="reactivity">Reactivity</h2>
|
|
|
|
Meteor embraces the concept of
|
|
<a target="_blank" href="http://en.wikipedia.org/wiki/Reactive_programming">
|
|
reactive programming</a>. This means that you can write your code in a
|
|
simple imperative style, and the result will be automatically
|
|
recalculated whenever data changes that your code depends on.
|
|
|
|
Meteor.autosubscribe(function () {
|
|
Meteor.subscribe("messages", Session.get("currentRoomId"));
|
|
});
|
|
|
|
This example (taken from a chat room client) sets up a data
|
|
subscription based on the session variable `currentRoomId`.
|
|
If the value of `Session.get("currentRoomId")` changes for any reason, the
|
|
function will be automatically re-run, setting up a new subscription that
|
|
replaces the old one.
|
|
|
|
This automatic recomputation is achieved by a cooperation
|
|
between `Session` and `Meteor.autosubscribe`.
|
|
Methods like `Meteor.autosubscribe` establish a "reactive
|
|
context" inside of which data dependencies are tracked, and they are
|
|
prepared to re-run their function argument as necessary. Data
|
|
providers like `Session`, on the other hand, make note of
|
|
the context they are called from and what data was requested, and they
|
|
are prepared to send an invalidation signal when the data changes.
|
|
|
|
This simple pattern has wide applicability. Above, the programmer is
|
|
saved from writing unsubscribe/resubscribe calls and making sure they
|
|
are called at the right time. In general, Meteor can eliminate whole
|
|
classes of data propagation code which would otherwise clog up your
|
|
application with error-prone logic.
|
|
|
|
These Meteor functions run your code in a reactive context:
|
|
|
|
* [`Meteor.ui.render`](#meteor_ui_render) and [`Meteor.ui.chunk`](#meteor_ui_chunk)
|
|
* [`Meteor.autosubscribe`](#meteor_autosubscribe)
|
|
* [Templates](#templates)
|
|
|
|
And the reactive data sources that can trigger changes are:
|
|
|
|
* <a href="#session">Session</a> variables
|
|
* Database queries on <a href="#find">Collections</a>
|
|
* <a href="#meteor_status">`Meteor.status`</a>
|
|
|
|
Meteor's <a href="https://github.com/meteor/meteor/blob/master/packages/deps/deps.js" target="_blank">implementation</a>
|
|
of reactivity is short and sweet, about 50 lines of code. You can
|
|
hook into it yourself to add new reactive contexts or data sources,
|
|
using the <a href="#meteor_deps">Meteor.deps</a> module.
|
|
|
|
{{/better_markdown}}
|
|
</template>
|
|
|
|
<template name="livehtml">
|
|
{{#better_markdown}}
|
|
|
|
<h2 id="livehtml">Live HTML</h2>
|
|
|
|
A big part of any web application is rendering HTML from data, one way
|
|
or another. This is where Meteor's reactivity really shines. Meteor
|
|
provides general mechanisms for tracking the data you use to generate
|
|
your HTML, and automatically updating the HTML in place when the data
|
|
changes.
|
|
|
|
Here is an example of appending reactive HTML to a web page:
|
|
|
|
var fragment = Meteor.ui.render(
|
|
function() {
|
|
var name = Session.get("name") || "Anonymous";
|
|
return "<div>Hello, " + name + "</div>";
|
|
});
|
|
document.body.appendChild(fragment);
|
|
|
|
Session.set("name", "Bob"); // page updates automatically!
|
|
|
|
The reactive updating is performed by `Meteor.ui.render`,
|
|
which takes an HTML generation function and returns a reactive
|
|
fragment. Specifically, `Meteor.ui.render(html_func)` does the following:
|
|
|
|
* Runs `html_func()` in a reactive context, which tracks access to data sources like `Session`
|
|
* Converts the resulting HTML to a DocumentFragment
|
|
* When the data changes, re-runs `html_func()` and updates the DOM in place
|
|
|
|
This simple interface hides a sophisticated implementation:
|
|
|
|
* The generated DOM elements are tracked if they move around the document, or are removed
|
|
* Auto-updating regions may be nested, using `Meteor.ui.chunk`
|
|
* Named form controls and elements with "id" properties are preserved, if possible, if they appear in both the old and new HTML
|
|
* Browser quirks in the conversion from HTML to DOM (for example, IE table bugs) are corrected
|
|
|
|
In the above example, HTML is generated by concatenating strings
|
|
together, and the resulting fragment is appended to the page, but you
|
|
may instead want to use your favorite template language to lay out
|
|
your entire page. If so, you may not need to
|
|
call `Meteor.ui.render` at all, as the body of your app is
|
|
implicitly a reactive context for HTML. Nested templates that you
|
|
define will automatically establish nested reactive contexts that
|
|
update the page incrementally.
|
|
|
|
{{/better_markdown}}
|
|
</template>
|
|
|
|
|
|
<template name="templates">
|
|
{{#better_markdown}}
|
|
|
|
<h2 id="templates">Templates</h2>
|
|
|
|
Meteor's templates are a reactive take on HTML templating. Meteor
|
|
doesn't define its own template language, but rather works with
|
|
existing template languages so that you can write the same template you
|
|
would anyway, and Meteor will make it live-updating for you, using dependency
|
|
tracking and live HTML.
|
|
|
|
To create a template, include a `<template>` tag with
|
|
a name attribute in any HTML file, containing the template contents.
|
|
The template will be compiled and made available as a function on
|
|
the global `Template` object.
|
|
|
|
{{#note}}
|
|
By default, you're using the popular Handlebars templating language,
|
|
with some extensions. But Meteor is designed to work with any template language,
|
|
and other templating systems will be available as
|
|
packages. For now, read up on the
|
|
<a href="http://www.handlebarsjs.com/">Handlebars documentation.</a>
|
|
{{/note}}
|
|
|
|
To render a template called `hello` as a string, you can call the function `Template.hello`:
|
|
|
|
<!-- in myapp.html -->
|
|
<template name="hello">
|
|
<div class="greeting">Hello there, {{dstache}}first}} {{dstache}}last}}!</div>
|
|
</{{! }}template>
|
|
|
|
// in the JavaScript console
|
|
> Template.hello({first: "Alyssa", last: "Hacker"});
|
|
=> "<div class="greeting">Hello there, Alyssa Hacker!</div>"
|
|
|
|
So far, this is not a reactive template. We have passed in an object
|
|
literal, not a reactive data source, and the result is a string value.
|
|
However, templates are in fact instrumented for reactivity, and behave
|
|
specially when used inside of `Meteor.ui.render` (or the
|
|
reactive Meteor body).
|
|
|
|
Each template invocation has its own reactive context and can be
|
|
updated individually, regardless of the HTML around it. In addition,
|
|
iteration constructs like `#each` in Handlebars, when used on database
|
|
query results, produce reactive lists whose elements are efficiently
|
|
updated when the results of the query change.
|
|
|
|
For example, if `top_10_scorers` is a database query result, the following
|
|
template will produce a reactively-updating list:
|
|
|
|
<template name="players">
|
|
{{dstache}}#each top_10_scorers}}
|
|
<div>{{dstache}}name}}</div>
|
|
{{dstache}}/each}}
|
|
</{{! }}template>
|
|
|
|
To invoke this template, we could call `Template.players` with an argument
|
|
that has a `top_10_scorers` property. However, we can also use a "data
|
|
helper" attached to the template function itself, so that the template
|
|
no longer requires an argument:
|
|
|
|
Template.players.top_10_scorers = function () {
|
|
return Users.find({}, {sort: {score: -1}, limit: 10});
|
|
};
|
|
|
|
Helpers can take arguments, and they receive the current template
|
|
data in `this`.
|
|
|
|
Template.players.league_is = function (league) {
|
|
return this.league === league;
|
|
};
|
|
|
|
<!-- Use it like this -->
|
|
<template name="players">
|
|
{{dstache}}#each top_10_scorers}}
|
|
{{dstache}}#if league_is "junior"}}
|
|
<div>Junior: {{dstache}}name}}</div>
|
|
{{dstache}}/if}}
|
|
{{dstache}}#if league_is "senior"}}
|
|
<div>Senior: {{dstache}}name}}</div>
|
|
{{dstache}}/if}}
|
|
{{dstache}}/each}}
|
|
</{{! }}template>
|
|
|
|
{{#note}}
|
|
Handlebars note: `{{dstache}}#if league_is "junior"}}` is
|
|
allowed because of a Meteor extension that allows nested helper
|
|
calls. (Both `if` and `league_is` are
|
|
technically helpers, and stock Handlebars only allows one helper
|
|
invocation per expression.)
|
|
{{/note}}
|
|
|
|
You can also pass in constant data this way.
|
|
|
|
// Works fine with {{dstache}}#each sections}}
|
|
Template.report.sections = ["Situation", "Complication", "Resolution"];
|
|
|
|
Finally, you can set the `events` property of a template function to a
|
|
table of event handlers. The format is documented at [Event
|
|
Maps](#eventmaps). The `this` argument to the event handler will be
|
|
the template data.
|
|
|
|
{{#warning}}
|
|
For now, the event handler gets the template data from the *top
|
|
level* of the current template, not the template data from the
|
|
template context of the element that triggered the event. This will
|
|
be changing.
|
|
{{/warning}}
|
|
|
|
<!-- myapp.html -->
|
|
<template name="scores">
|
|
{{dstache}}#each player}}
|
|
{{dstache}}> player_score}}
|
|
{{dstache}}/each}}
|
|
</{{! }}template>
|
|
|
|
<template name="player_score">
|
|
<div>{{dstache}}name}}: {{dstache}}score}}
|
|
<span class="give_points">Give points</span>
|
|
</div>
|
|
</{{! }}template>
|
|
|
|
<!-- myapp.js -->
|
|
Template.player_score.events = {
|
|
'click .give_points': function () {
|
|
Users.update({_id: this._id}, {$inc: {score: 2}});
|
|
}
|
|
};
|
|
|
|
As mentioned, templates are reactive when wrapped in
|
|
[`Meteor.ui.render`](#meteor_ui_render), meaning that when you add the
|
|
DocumentFragment returned by `render` to the DOM, that region
|
|
of the DOM will update automatically.
|
|
|
|
<!-- in myapp.html -->
|
|
<template name="forecast">
|
|
<div>It'll be {{dstache}}prediction}} tonight</div>
|
|
</{{! }}template>
|
|
|
|
<!-- in myapp.js -->
|
|
// JavaScript: reactive helper function
|
|
Template.forecast.prediction = function () {
|
|
return Session.get("weather");
|
|
};
|
|
|
|
<!-- in the console -->
|
|
> Session.set("weather", "cloudy");
|
|
> document.body.appendChild(Meteor.ui.render(
|
|
function() { return Template.forecast(); }))
|
|
In DOM: <div>It'll be cloudy tonight</div>
|
|
|
|
> Session.set("weather", "cool and dry");
|
|
In DOM: <div>It'll be cool and dry tonight</div>
|
|
|
|
For the details on how updating is performed, see the documentation
|
|
for [`Meteor.ui.chunk`](#meteor_ui_chunk) and
|
|
[`Meteor.ui.listChunk`](#meteor_ui_listchunk), which provide the implementation
|
|
used internally.
|
|
|
|
Auto-updating continues as long as the elements are on the
|
|
screen. The specific rule is: if the elements are not children
|
|
of `document` when
|
|
[`Meteor.flush`](#meteor_flush) runs, then Meteor may
|
|
stop updating them, allowing the browser to garbage-collect them.
|
|
|
|
{{/better_markdown}}
|
|
</template>
|
|
|
|
<template name="packages_concept">
|
|
{{#better_markdown}}
|
|
|
|
<h2 id="smartpackages">Smart Packages</h2>
|
|
|
|
Meteor has an unusually powerful package system. All of the
|
|
functionality you've read about so far is implemented as standard
|
|
Meteor packages.
|
|
|
|
Meteor packages are intelligent: the packages are themselves
|
|
JavaScript programs. They can inject code into the client or the
|
|
server, or hook new functions into the bundler, so they can extend the
|
|
Meteor environment in arbitrary ways. Some examples of packages are:
|
|
|
|
* The <a href="#coffeescript">coffeescript</a> package extends the
|
|
bundler, automatically compiling any <code>.coffee</code> files in
|
|
your tree. Once added, you can write your application in CoffeeScript
|
|
instead of JavaScript.
|
|
|
|
* The <a href="#jquery">jQuery</a>
|
|
and <a href="#backbone">Backbone</a> packages are examples of using
|
|
Meteor to prepackage client JavaScript libraries. You could get
|
|
the same result by copying the JavaScript files into your tree, but
|
|
it's faster to add a package.
|
|
|
|
* The <a href="#underscore">underscore</a> package extends both the
|
|
client and server environments. Many of the core Meteor features,
|
|
including Minimongo, the Session object, and reactive Handlebars
|
|
templates, are implemented as internal packages automatically
|
|
included with every Meteor application.
|
|
|
|
You can see a list of available packages
|
|
with <a href="#meteorlist">meteor list</a>,
|
|
add packages to your project
|
|
with <a href="#meteoradd">meteor add</a>, and remove them
|
|
with <a href="#meteorremove">meteor remove</a>.
|
|
|
|
See the <a href="#packages">Package List</a> section for a description
|
|
of the existing packages.
|
|
|
|
{{#warning}}
|
|
The package API is rapidly changing and isn't documented, so you can't
|
|
make your own packages just yet. Coming soon.
|
|
{{/warning}}
|
|
|
|
{{/better_markdown}}
|
|
</template>
|
|
|
|
<template name="deploying">
|
|
{{#better_markdown}}
|
|
|
|
<h2 id="deploying">Deploying</h2>
|
|
|
|
Meteor is a full application server. We include everything you need
|
|
to deploy your application on the internet: you just provide the JS,
|
|
HTML, and CSS.
|
|
|
|
<h3 class="nosection">Running on Meteor's infrastructure</h3>
|
|
|
|
The easiest way to deploy your application is to use <b>meteor
|
|
deploy</b>. We provide it because it's what, personally, we've always
|
|
wanted: an easy way to take an app idea, flesh it out over a weekend,
|
|
and put it out there for the world to use, with nothing getting in the
|
|
way of creativity.
|
|
|
|
$ meteor deploy myapp.meteor.com
|
|
|
|
Your application is now available at myapp.meteor.com. If
|
|
this is the first time deploying to this hostname, Meteor creates a
|
|
fresh empty database for your application. If you want to deploy an
|
|
update, Meteor will preserve the existing data and just refresh the
|
|
code.
|
|
|
|
You can also deploy to your own domain. Just set up the hostname you
|
|
want to use as a CNAME to <code>origin.meteor.com</code>,
|
|
then deploy to that name.
|
|
|
|
$ meteor deploy www.myapp.com
|
|
|
|
We provide this as a free service so you can try Meteor. It is also
|
|
helpful for quickly putting up internal betas, demos, and so on.
|
|
|
|
<h3 class="nosection">Running on your own infrastructure</h3>
|
|
|
|
You can run also your application on your own infrastructure, or any
|
|
other hosting provider like Heroku.
|
|
|
|
To get started, run
|
|
|
|
$ meteor bundle myapp.tgz
|
|
|
|
This command will generate a fully-contained Node.js application in
|
|
the form of a tarball. To run this application, you need to provide
|
|
Node.js 0.6 and a MongoDB server. You can then run the application by
|
|
invoking node, specifying the HTTP port for the application to listen
|
|
on, and the MongoDB endpoint. If you don't already have a MongoDB
|
|
server, we can recommend our friends at [MongoHQ](http://mongohq.com).
|
|
|
|
$ PORT=3000 MONGO_URL=mongodb://localhost:27017/myapp node bundle/main.js
|
|
|
|
{{#warning}}
|
|
For now, bundles will only run on the platform that the bundle was
|
|
created on. To run on a different platform, you'll need to rebuild
|
|
the native packages included in the bundle. To do that, make sure you
|
|
have <code>npm</code> available, and run the following:
|
|
|
|
$ cd bundle/server
|
|
$ rm -r fibers
|
|
$ npm install fibers@0.6.3
|
|
{{/warning}}
|
|
|
|
{{/better_markdown}}
|
|
</template>
|