├── .gitignore ├── .meteor ├── .finished-upgraders ├── .gitignore ├── .id ├── cordova-plugins ├── packages ├── platforms ├── release └── versions ├── README.md ├── canonically_diff_docs.sh ├── client ├── api-box.html ├── api-box.js ├── basic │ ├── api.md │ ├── basic-content.html │ ├── introduction.html │ ├── sections │ │ ├── accounts.md │ │ ├── collections.md │ │ ├── commandline.md │ │ ├── environment.md │ │ ├── file-structure.md │ │ ├── methods.md │ │ ├── mobile.md │ │ ├── packages.md │ │ ├── pubsub.md │ │ ├── session.md │ │ ├── templates.md │ │ └── tracker.md │ ├── toc.html │ ├── toc.js │ └── toc.less ├── common │ ├── principles.md │ └── quick-start.md ├── data.js ├── docs.less ├── full-api │ ├── api.md │ ├── api │ │ ├── accounts.md │ │ ├── assets.md │ │ ├── blaze.md │ │ ├── check.md │ │ ├── collections.md │ │ ├── connections.md │ │ ├── core.md │ │ ├── ejson.md │ │ ├── email.md │ │ ├── http.md │ │ ├── methods.md │ │ ├── mobile-config.md │ │ ├── packagejs.md │ │ ├── passwords.md │ │ ├── pubsub.md │ │ ├── reactive-var.md │ │ ├── session.md │ │ ├── templates.md │ │ ├── timers.md │ │ └── tracker.md │ ├── commandline.html │ ├── concepts.html │ ├── docs.html │ ├── docs.js │ ├── introduction.html │ ├── nameToId.js │ ├── packages.html │ ├── packages │ │ ├── accounts-ui.html │ │ ├── appcache.html │ │ ├── audit-argument-checks.html │ │ ├── coffeescript.html │ │ ├── jquery.html │ │ ├── less.html │ │ ├── markdown.html │ │ ├── oauth-encryption.html │ │ ├── random.html │ │ ├── underscore.html │ │ └── webapp.html │ ├── tableOfContents.html │ ├── tableOfContents.js │ └── toc.less ├── helpers.js ├── layout.html ├── layout.js ├── lib │ └── navigate.js ├── link-redirect.js ├── links.js ├── names.json ├── responsive.js ├── search.html ├── search.js └── splash.less ├── lib └── appcache-config.js ├── private └── canonicalize.js └── public ├── favicon.png ├── forkme_right_red_aa0000.png ├── logo.png └── robots.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | notices-for-facebook-graph-api-2 9 | -------------------------------------------------------------------------------- /.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | tqr1dmhdrd0990n71v 8 | -------------------------------------------------------------------------------- /.meteor/cordova-plugins: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # 3 | # 'meteor add' and 'meteor remove' will edit this file for you, 4 | # but you can also edit it by hand. 5 | 6 | standard-app-packages 7 | jquery 8 | underscore 9 | showdown 10 | spiderable 11 | appcache 12 | reload-safetybelt 13 | simple:markdown-templating 14 | simple:highlight.js 15 | less 16 | -------------------------------------------------------------------------------- /.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | ios 4 | -------------------------------------------------------------------------------- /.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@1.1.0.2 2 | -------------------------------------------------------------------------------- /.meteor/versions: -------------------------------------------------------------------------------- 1 | appcache@1.0.4 2 | autoupdate@1.2.1 3 | base64@1.0.3 4 | binary-heap@1.0.3 5 | blaze@2.1.2 6 | blaze-tools@1.0.3 7 | boilerplate-generator@1.0.3 8 | callback-hook@1.0.3 9 | check@1.0.5 10 | ddp@1.1.0 11 | deps@1.0.7 12 | ejson@1.0.6 13 | fastclick@1.0.3 14 | geojson-utils@1.0.3 15 | html-tools@1.0.4 16 | htmljs@1.0.4 17 | http@1.1.0 18 | id-map@1.0.3 19 | jquery@1.11.3_2 20 | json@1.0.3 21 | launch-screen@1.0.2 22 | less@1.0.14 23 | livedata@1.0.13 24 | logging@1.0.7 25 | markdown@1.0.4 26 | meteor@1.1.6 27 | meteor-platform@1.2.2 28 | minifiers@1.1.5 29 | minimongo@1.0.8 30 | mobile-status-bar@1.0.3 31 | mongo@1.1.0 32 | observe-sequence@1.0.6 33 | ordered-dict@1.0.3 34 | random@1.0.3 35 | reactive-dict@1.1.0 36 | reactive-var@1.0.5 37 | reload@1.1.3 38 | reload-safetybelt@1.0.3 39 | retry@1.0.3 40 | routepolicy@1.0.5 41 | session@1.1.0 42 | showdown@1.0.4 43 | simple:highlight.js@1.0.9 44 | simple:markdown-templating@1.2.6 45 | spacebars@1.0.6 46 | spacebars-compiler@1.0.6 47 | spiderable@1.0.7 48 | standard-app-packages@1.0.5 49 | templating@1.1.1 50 | tracker@1.0.7 51 | ui@1.0.6 52 | underscore@1.0.3 53 | url@1.0.4 54 | webapp@1.2.0 55 | webapp-hashing@1.0.3 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # meteor-doc-cn 2 | Meteor中文文档 3 | 4 | 文档在线地址:[http://docs.meteorhub.org/](http://docs.meteorhub.org/) 5 | 6 | Meteor中文社区[MeteorHub](http://www.meteorhub.org/),新社区,建设中。感兴趣的童鞋可以一起参与进来。 7 | 8 | QQ群:327885034 9 | 10 | **如果想贡献翻译,先到 [MeteorHub](http://www.meteorhub.org/t/meteor-meteor/40) 认领待翻译的章节,然后按照贡献流程进行。** 11 | 12 | ## 翻译进度 13 | 14 | - basic 15 | - 快速开始 (完成) by [zicai] 16 | - Meteor理念 (完成) by [zicai] 17 | - 学习资源 (完成) by [zicai] 18 | - 命令行工具 (完成) by [zicai] 19 | - 文件结构 (完成) by [zicai] 20 | - 开发手机应用 (完成) by [zicai] 21 | - 模板 (完成) by [zicai] 22 | - Session (完成) by [zicai] 23 | - Tracker (完成) by [zicai] 24 | - 集合 (完成) by [zicai] 25 | - 账户 (完成) by [zicai] 26 | - Methods (完成) by [zicai] 27 | - 发布/订阅 (完成) by [fooying] 28 | - Environment (完成) by [zicai] 29 | - full api 30 | - Concepts 31 | - what is Meteor 32 | - structring your app 33 | - Data and security 34 | - Reactivity 35 | - Live HTMl templates 36 | - Using packages 37 | - Namespacing 38 | - Deploying 39 | - Writing packages 40 | - API 41 | - Core (进行中) by [Huibean] 42 | - Publish and subscribe 43 | - Methods 44 | - Check 45 | - Server connections 46 | - Collections 47 | - Session 48 | - Accounts 49 | - Passwords 50 | - Templates 51 | - Blaze 52 | - Timers 53 | - Tracker 54 | - ReactiveVar 55 | - EJSON 56 | - HTTP 57 | - Email 58 | - package.js 59 | - mobile-config.js 60 | - Packages 61 | - Command line 62 | 63 | 64 | ## 贡献力量 65 | 66 | 如果想做出贡献的话,你可以: 67 | 68 | - 帮忙校对,挑错别字、病句等等 69 | - 提出修改建议 70 | - 提出术语翻译建议 71 | 72 | ## 贡献流程 73 | 74 | ### 如果是挑错别字,病句 75 | 76 | 直接在仓库中找到对应的文件,在线编辑修改提交即可。 77 | 78 | ### 如果是提修改建议 79 | 80 | 直接提issue就OK 81 | 82 | ### 如果是贡献翻译 83 | 84 | 这里有一个简单的流程,供大家参考: 85 | 86 | 1. 首先fork MeteorHub 的meteor-doc-cn仓库 87 | 2. 把fork过去的项目也就是你的项目clone到你的本地 88 | 3. 在命令行运行 `git branch develop` 来创建一个新分支 89 | 4. 运行 `git checkout develop` 来切换到新分支 90 | 5. 运行 `git remote add upstream git@github.com:MeteorHub/meteor-doc-cn.git` 把MeteorHub的库添加为远端库 91 | 6. 运行 `git remote update`更新 92 | 7. 运行 `git fetch upstream master` 拉取meteor-doc-cn的更新到本地 93 | 8. 运行 `git rebase -i upstream/master` 将meteor-doc-cn的更新合并到你的分支 94 | 95 | 这是一个初始化流程,只需要做一遍就行,之后请一直在develop分支进行修改。 96 | 97 | 如果修改过程中meteor-doc-cn有了更新,请重复6、7、8步。 98 | 99 | 修改之后,首先push到你的库,然后登录GitHub,在你的库的首页可以看到一个 `pull request` 按钮,点击它,填写一些说明信息,然后提交即可。 100 | 101 | 102 | [zicai]:https://github.com/zicai 103 | [fooying]:https://github.com/fooying 104 | [Huibean]:https://github.com/Huibean -------------------------------------------------------------------------------- /canonically_diff_docs.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | echo "Please run this script from the docs app folder." 3 | echo "Make sure you have phantomjs installed!" 4 | 5 | DOCS_FOLDER=$(pwd); 6 | METEOR_FOLDER=$(dirname ${DOCS_FOLDER}); 7 | 8 | # make temporary folder 9 | mkdir /tmp/docsdiff 10 | cd /tmp/docsdiff 11 | 12 | # trigger phantomjs to give us actual HTML 13 | curl "localhost:3000/?_escaped_fragment_=key1=value1" > new 14 | curl "docs.meteor.com/?_escaped_fragment_=key1=value1" > old 15 | 16 | # use our handy canonicalize script copy-pasted from the test-helpers package 17 | # maybe there is a way to use the actual package? 18 | ${METEOR_FOLDER}/scripts/node.sh "${DOCS_FOLDER}/private/canonicalize.js" new > new1 19 | ${METEOR_FOLDER}/scripts/node.sh "${DOCS_FOLDER}/private/canonicalize.js" old > old1 20 | 21 | # remove some of the things we want to ignore, you might want to change these 22 | cat new1 | sed "s/new-api-box//g" | sed "s/ class=\"api-title\"//g" > new2 23 | cat new2 | sed "s/

<\/p>//g" > new3 24 | cat new3 | sed "s/i>/em>/g" > new4 25 | 26 | cat old1 | sed "s/new-api-box//g" | sed "s/ class=\"api-title\"//g" > old2 27 | cat old2 | sed "s/

<\/p>//g" > old3 28 | cat old3 | sed "s/i>/em>/g" > old4 29 | 30 | # git diff is more colorful than regular diff 31 | /usr/bin/git diff -U10 --no-index --ignore-blank-lines -w old4 new4 -------------------------------------------------------------------------------- /client/api-box.html: -------------------------------------------------------------------------------- 1 | 18 | 19 | 64 | 65 | 80 | -------------------------------------------------------------------------------- /client/api-box.js: -------------------------------------------------------------------------------- 1 | var apiData = function (options) { 2 | options = options || {}; 3 | if (typeof options === "string") { 4 | options = {name: options}; 5 | } 6 | 7 | var root = DocsData[options.name]; 8 | 9 | if (! root) { 10 | console.log("API Data not found: " + options.name); 11 | } 12 | 13 | if (_.has(options, 'options')) { 14 | root = _.clone(root); 15 | var includedOptions = options.options.split(';'); 16 | root.options = _.filter(root.options, function (option) { 17 | return _.contains(includedOptions, option.name); 18 | }); 19 | } 20 | 21 | return root; 22 | }; 23 | 24 | var typeLink = function (displayName, url) { 25 | return "" + displayName + ""; 26 | }; 27 | 28 | var toOrSentence = function (array) { 29 | if (array.length === 1) { 30 | return array[0]; 31 | } else if (array.length === 2) { 32 | return array.join(" or "); 33 | } 34 | 35 | return _.initial(array).join(", ") + ", or " + _.last(array); 36 | }; 37 | 38 | var typeNameTranslation = { 39 | "function": "Function", 40 | EJSON: typeLink("EJSON-able Object", "#ejson"), 41 | EJSONable: typeLink("EJSON-able Object", "#ejson"), 42 | "Tracker.Computation": typeLink("Tracker.Computation", "#tracker_computation"), 43 | MongoSelector: [ 44 | typeLink("Mongo Selector", "#selectors"), 45 | typeLink("Object ID", "#mongo_object_id"), 46 | "String" 47 | ], 48 | MongoModifier: typeLink("Mongo Modifier", "#modifiers"), 49 | MongoSortSpecifier: typeLink("Mongo Sort Specifier", "#sortspecifiers"), 50 | MongoFieldSpecifier: typeLink("Mongo Field Specifier", "#fieldspecifiers"), 51 | JSONCompatible: "JSON-compatible Object", 52 | EventMap: typeLink("Event Map", "#eventmaps"), 53 | DOMNode: typeLink("DOM Node", "https://developer.mozilla.org/en-US/docs/Web/API/Node"), 54 | "Blaze.View": typeLink("Blaze.View", "#blaze_view"), 55 | Template: typeLink("Blaze.Template", "#blaze_template"), 56 | DOMElement: typeLink("DOM Element", "https://developer.mozilla.org/en-US/docs/Web/API/element"), 57 | MatchPattern: typeLink("Match Pattern", "#matchpatterns"), 58 | "DDP.Connection": typeLink("DDP Connection", "#ddp_connect") 59 | }; 60 | 61 | Template.autoApiBox.helpers({ 62 | apiData: apiData, 63 | typeNames: function typeNames (nameList) { 64 | // change names if necessary 65 | nameList = _.map(nameList, function (name) { 66 | // decode the "Array." syntax 67 | if (name.slice(0, 7) === "Array.<") { 68 | // get the part inside angle brackets like in Array 69 | name = name.match(/<([^>]+)>/)[1]; 70 | 71 | if (name && typeNameTranslation.hasOwnProperty(name)) { 72 | return "Array of " + typeNameTranslation[name] + "s"; 73 | } 74 | 75 | if (name) { 76 | return "Array of " + name + "s"; 77 | } 78 | 79 | console.log("no array type defined"); 80 | return "Array"; 81 | } 82 | 83 | if (typeNameTranslation.hasOwnProperty(name)) { 84 | return typeNameTranslation[name]; 85 | } 86 | 87 | if (DocsData[name]) { 88 | return typeNames(DocsData[name].type); 89 | } 90 | 91 | return name; 92 | }); 93 | 94 | nameList = _.flatten(nameList); 95 | 96 | return toOrSentence(nameList); 97 | }, 98 | signature: function () { 99 | var signature; 100 | var escapedLongname = _.escape(this.longname); 101 | 102 | if (this.istemplate || this.ishelper) { 103 | if (this.istemplate) { 104 | signature = "{{> "; 105 | } else { 106 | signature = "{{ "; 107 | } 108 | 109 | signature += escapedLongname; 110 | 111 | var params = this.params; 112 | 113 | var paramNames = _.map(params, function (param) { 114 | var name = param.name; 115 | 116 | name = name + "=" + name; 117 | 118 | if (param.optional) { 119 | return "[" + name + "]"; 120 | } 121 | 122 | return name; 123 | }); 124 | 125 | signature += " " + paramNames.join(" "); 126 | 127 | signature += " }}"; 128 | } else { 129 | var beforeParens; 130 | if (this.scope === "instance") { 131 | beforeParens = "" + apiData(this.memberof).instancename + "." + this.name; 132 | } else if (this.kind === "class") { 133 | beforeParens = "new " + escapedLongname; 134 | } else { 135 | beforeParens = escapedLongname; 136 | } 137 | 138 | signature = beforeParens; 139 | 140 | // if it is a function, and therefore has arguments 141 | if (_.contains(["function", "class"], this.kind)) { 142 | var params = this.params; 143 | 144 | var paramNames = _.map(params, function (param) { 145 | if (param.optional) { 146 | return "[" + param.name + "]"; 147 | } 148 | 149 | return param.name; 150 | }); 151 | 152 | signature += "(" + paramNames.join(", ") + ")"; 153 | } 154 | } 155 | 156 | return signature; 157 | }, 158 | id: function () { 159 | if (Session.get("fullApi") && nameToId[this.longname]) { 160 | return nameToId[this.longname]; 161 | } 162 | 163 | // fallback 164 | return this.longname.replace(/[.#]/g, "-"); 165 | }, 166 | paramsNoOptions: function () { 167 | return _.reject(this.params, function (param) { 168 | return param.name === "options"; 169 | }); 170 | }, 171 | fullApi: function () { 172 | return Session.get("fullApi"); 173 | } 174 | }); 175 | 176 | Template.apiBoxTitle.helpers({ 177 | link: function () { 178 | return '#/' + (Session.get("fullApi") ? 'full' : 'basic') + '/' + this.id; 179 | } 180 | }); 181 | 182 | Template.autoApiBox.onRendered(function () { 183 | this.$('pre code').each(function(i, block) { 184 | hljs.highlightBlock(block); 185 | }); 186 | }); 187 | 188 | -------------------------------------------------------------------------------- /client/basic/api.md: -------------------------------------------------------------------------------- 1 | {{#template name="basicApi"}} 2 | 3 |

The Meteor API

4 | 5 | Javascript代码可以运行在两种环境:客户端(浏览器),和服务端(服务器上的[Node.js](http://nodejs.org/)容器)。 6 | 在API 参考中,我们会指出每个函数是否可以在客户端,或是服务端,或是*Anywhere(客户端和服务端)*被调用。 7 | 8 | {{> basicTemplates}} 9 | {{> basicSession}} 10 | {{> basicTracker}} 11 | {{> basicCollections}} 12 | {{> basicAccounts}} 13 | {{> basicMethods}} 14 | {{> basicPubsub}} 15 | {{> basicEnvironment}} 16 | 17 | {{/template}} -------------------------------------------------------------------------------- /client/basic/basic-content.html: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /client/basic/introduction.html: -------------------------------------------------------------------------------- 1 | 59 | -------------------------------------------------------------------------------- /client/basic/sections/accounts.md: -------------------------------------------------------------------------------- 1 | {{#template name="basicAccounts"}} 2 | 3 |

Accounts

4 | 5 | 要增加账户功能,用`meteor add`添加下面的一个或多个包: 6 | 7 | - `accounts-ui`:这个包允许你通过在模板中使用`{{dstache}}> loginButtons}}`,来添加自动生成的登录UI, 8 | 用户可以登录。社区中有其它的替代选择,或者你也可以结合使用[advanced Accounts methods](#accounts) 9 | - `accounts-password`: 这个包允许用户通过密码登录。添加之后,`loginButtons`下拉框会自动增加邮箱和密码文本域。 10 | - `accounts-facebook`, `accounts-google`, `accounts-github`, `accounts-twitter`, 11 | 以及其它由社区贡献的第三方登录包,让你的用户可以通过第三方网站登录。 12 | 它们会自动添加登录按钮到`loginButtons`下拉框中。 13 | 14 |

15 | {{dstache}}> loginButtons}} 16 | Client 17 |

18 | 19 | 在HTML中引入`loginButtions`模板,就可以使用Meteor默认的登录UI。使用前,需要先添加`accounts-ui`包: 20 | 21 | ``` 22 | $ meteor add accounts-ui 23 | ``` 24 | 25 | {{> autoApiBox "Meteor.user"}} 26 | 27 | 从 [`Meteor.users`](#meteor_users) 集合中获取当前登录用户。等同于`Meteor.users.findOne(Meteor.userId())`。 28 | 29 | {{> autoApiBox "Meteor.userId"}} 30 | 31 | {{> autoApiBox "Meteor.users"}} 32 | 33 | 这个集合包含了所有注册用户,每个用户是一个文档。例如: 34 | 35 | ``` 36 | { 37 | _id: "bbca5d6a-2156-41c4-89da-0329e8c99a4f", // Meteor.userId() 38 | username: "cool_kid_13", // unique name 39 | emails: [ 40 | // each email address can only belong to one user. 41 | { address: "cool@example.com", verified: true }, 42 | { address: "another@different.com", verified: false } 43 | ], 44 | createdAt: Wed Aug 21 2013 15:16:52 GMT-0700 (PDT), 45 | profile: { 46 | // The profile is writable by the user by default. 47 | name: "Joe Schmoe" 48 | }, 49 | services: { 50 | facebook: { 51 | id: "709050", // facebook id 52 | accessToken: "AAACCgdX7G2...AbV9AZDZD" 53 | }, 54 | resume: { 55 | loginTokens: [ 56 | { token: "97e8c205-c7e4-47c9-9bea-8e2ccc0694cd", 57 | when: 1349761684048 } 58 | ] 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | 一个用户文档可以包含任何你想保存的用户相关的数据。不过,Meteor会特殊对待下面的几个字段: 65 | 66 | - `username`: 一个唯一的字符串,可以标识用户。 67 | - `emails`: 一个对象的数组。对象包含属性 68 | `address` 和 `verified` 。一个邮箱地址只能属于一个用户。`verified`是一个布尔值,如果用户已经[验证 69 | 邮箱地址](#accounts_verifyemail)则为true。 70 | - `createdAt`: 用户文档创建时间。 71 | - `profile`: 一个对象,默认情况下用户可以用任何数据新建和更新该字段。 72 | - `services`: 包含第三方登录服务使用的数据的对象。例如,它的`reset`字段包含的token,用于 73 | [忘记密码](#accounts_forgotpassword)的超链接,它的`resume`字段包含的token,用于维持用户登录状态。 74 | 75 | 和所有的[Mongo.Collection](#collections)一样,在服务端,你可以获取用户集合 76 | 的所有文档,但是在客户端只能获取那些服务端发布的文档。 77 | 78 | 默认情况下,当前用户的`username`,`emails`,和`profile`会发布到客户端。 79 | 可以使用下面的代码发布当前用户的其它字段: 80 | 81 | // server 82 | Meteor.publish("userData", function () { 83 | if (this.userId) { 84 | return Meteor.users.find({_id: this.userId}, 85 | {fields: {'other': 1, 'things': 1}}); 86 | } else { 87 | this.ready(); 88 | } 89 | }); 90 | 91 | // client 92 | Meteor.subscribe("userData"); 93 | 94 | 如果安装了autopublish包,那么所有用户的信息都会发布到所有客户端。包括`username`, 95 | `profile` ,以及`service`中所有可以公开的字段(例如:`services.facebook.id`, 96 | `services.twitter.screenName`)。另外,使用autopublish时,对于当前登录用户会发布更多的信息, 97 | 包括access token。这样就可以直接从客户端发起API调用。 98 | 99 | 100 | 默认情况下,用户可以通过[`Accounts.createUser`](#accounts_createuser)声明自己的`profile`字段, 101 | 也可以通过`Meteor.users.update`来修改它。要允许用户修改更多的字段,使用[`Meteor.users.allow`](#allow) 102 | ,要禁止用户对自己的文档做任何修改,使用: 103 | 104 | Meteor.users.deny({update: function () { return true; }}); 105 | 106 | 107 | {{> autoApiBox "currentUser"}} 108 | 109 | {{/template}} 110 | -------------------------------------------------------------------------------- /client/basic/sections/collections.md: -------------------------------------------------------------------------------- 1 | {{#template name="basicCollections"}} 2 | 3 |

Collections

4 | 5 | Meteor用*集合*保存数据。集合里保存的Javascript对象叫做文档。使用`new Mongo.Collection`声明一个集合。 6 | 7 | {{> autoApiBox name="Mongo.Collection" options=""}} 8 | 9 | 调用`Mongo.Collection`构造函数创建一个集合对象,它的行为就像MongoDB 的集合。如果在创建集合时传入了一个name参数,那么声明的就是一个持久性集合 — 保存在服务端并可以发布到客户端。 10 | 11 | 要想使客户端代码和服务端代码都可以通过相同的API访问相同的集合,最好在一个客户端和服务端都能加载到的javascript文件中把集合声明为全局变量。 12 | 13 | 示例:声明两个命名的,持久性的集合作为全局变量: 14 | 15 | ``` 16 | // In a JS file that's loaded on the client and the server 17 | Posts = new Mongo.Collection("posts"); 18 | Comments = new Mongo.Collection("comments"); 19 | ``` 20 | 21 | 如果传入`null`作为name参数,那么创建出来的就是一个本地集合。本地集合不会在客户端和服务端之间进行同步;它只是javascript对象的临时集合,支持Mongo-style `find`, `insert`, `update`, 和 `remove`操作。 22 | 23 | 默认情况下,Meteor会自动发布所有集合里的文档到每一个连接上的客户端。要禁用此行为,必须移除`autopublish`包: 24 | 25 | ``` 26 | $ meteor remove autopublish 27 | ``` 28 | 29 | 然后,使用[`Meteor.publish`](#meteor_publish) 和 30 | [`Meteor.subscribe`](#meteor_subscribe)来指定集合的哪一部分发送到哪些客户端。 31 | 32 | 使用`findOne` 和 `find`从集合里检索文档。 33 | 34 | {{> autoApiBox name="Mongo.Collection#findOne" options="sort;skip;fields"}} 35 | 36 | `findOne`方法可以从集合里查找特定的文档。调用`findOne`时,通常都会传入一个特定文档的`_id`: 37 | 38 | ``` 39 | var post = Posts.findOne(postId); 40 | ``` 41 | 42 | 然而,也可以给`findOne`传入一个Mongo 选择器,Mongo选择器是一个对象,指明了目标文档要满足的一系列属性。 43 | 例如,下面的选择器 44 | 45 | ``` 46 | var post = Posts.findOne({ 47 | createdBy: "12345", 48 | title: {$regex: /first/} 49 | }); 50 | ``` 51 | 52 | 会匹配到下面的文档 53 | 54 | ``` 55 | { 56 | createdBy: "12345", 57 | title: "My first post!", 58 | content: "Today was a good day." 59 | } 60 | ``` 61 | 62 | 关于MongoDB query operators 例如`$regex`, `$lt` (小于), 63 | `$text` (文本搜索)的更多信息参见[MongoDB 64 | documentation](http://docs.mongodb.org/manual/reference/operator/query/)。 65 | 66 | 一个非常有用但是不那么明显的功能就是Mongo 选择器可以匹配数组里的元素。例如:下面的选择器 67 | 68 | ``` 69 | Post.findOne({ 70 | tags: "meteor" 71 | }); 72 | ``` 73 | 74 | 会匹配到下面的文档 75 | 76 | ``` 77 | { 78 | title: "I love Meteor", 79 | createdBy: "242135223", 80 | tags: ["meteor", "javascript", "fun"] 81 | } 82 | ``` 83 | 84 | `findOne`方法和[`Session.get`](#session_get)一样,也是响应式的,也就是说,如果你在[template helper](#template_helpers)或是 [`Tracker.autorun`](#tracker_autorun)回调函数中使用了`findOne`,如果`findOne`返回的文档发生了变化,那么模板就会自动重新渲染,计算会重新执行。 85 | 86 | 注意,如果`findOne`没有找到任何文档,则返回`null`,通常发生在文档还没加载或是已经从集合中移除,所以要有处理`null`值的准备。 87 | 88 | 89 | {{> autoApiBox name="Mongo.Collection#find" options="sort;skip;limit;fields"}} 90 | 91 | `find`方法和`findOne`类似,不同的是,它不返回单一文档,而是返回一个MongoDB *游标*。游标是一个特殊的对象,代表一个查询里会被返回的文档列表。可以在模板Helper里返回游标,或是其它可以返回数组的地方: 92 | 93 | ``` 94 | Template.blog.helpers({ 95 | posts: function () { 96 | // this helper returns a cursor of 97 | // all of the posts in the collection 98 | return Posts.find(); 99 | } 100 | }); 101 | ``` 102 | 103 | ``` 104 | 105 | 111 | ``` 112 | 113 | 要想从一个游标里检索当前的文档列表时,调用游标的`.fetch()`方法: 114 | 115 | ``` 116 | // get an array of posts 117 | var postsArray = Posts.find().fetch(); 118 | ``` 119 | 120 | 记住,虽然调用`fetch`的计算会在数据变化时重新运行,但是the resulting array will not be reactive if it is 121 | passed somewhere else. 122 | 123 | 通过调用`insert`,`update`, 或 `remove`来修改保存在`Mongo.Collection`里的数据。 124 | 125 | {{> autoApiBox "Mongo.Collection#insert"}} 126 | 127 | 下面的例子展示了如何插入文档到集合里: 128 | 129 | ``` 130 | Posts.insert({ 131 | createdBy: Meteor.userId(), 132 | createdAt: new Date(), 133 | title: "My first post!", 134 | content: "Today was a good day." 135 | }); 136 | ``` 137 | 138 | 每个`Mongo.Collection`里的每个文档都有一个`_id`字段。它必须是唯一的,如果你没有提供,会自动生成。[`collection.findOne`](#findOne)使用`_id`可以用来检索特定的文档。 139 | 140 | {{> autoApiBox "Mongo.Collection#update"}} 141 | 142 | 这里的选择器和你传给`find`方法的是一致的,它可以匹配多个文档。修改器是一个对象,它指明了对匹配到的文档要做的修改。注意:除非你使用了`$set`操作符,否则`update`方法会直接用修改器替换整个匹配到的文档。 143 | 144 | 下面的例子展示了,设置所有标题包含"first"的文章的内容字段 145 | 146 | ``` 147 | Posts.update({ 148 | title: {$regex: /first/} 149 | }, { 150 | $set: {content: "Tomorrow will be a great day."} 151 | }); 152 | ``` 153 | 154 | 关于支持的所有operators参见[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/update/)。 155 | 156 | 有一点需要注意:当在客户端调用`update`的时候,只能通过`_id`来查找文档。要使用所有可用的选择器,必须在服务端代码或是 157 | [method](#meteor_methods)中调用`update`。 158 | 159 | {{> autoApiBox "Mongo.Collection#remove"}} 160 | 161 | `remove`方法使用和`find`、`update`一样的选择器,并且从数据库中移除所有匹配到的文档。请小心使用`remove` — 删掉的数据没办法恢复。 162 | 163 | 和`update`一样,客户端代码只能通过`_id`移除文档,而服务端代码和[methods](#meteor_methods)可以使用任何选择器移除文档。 164 | 165 | {{> autoApiBox name="Mongo.Collection#allow" options="insert, update, remove"}} 166 | 167 | 在新创建的APP中,Meteor允许任何客户端和服务端代码调用`insert`, `update`, 和 168 | `remove` 。这是因为用`meteor create`创建的APP默认包含了`insecure`包,目的是简化开发。很显然,如果任何用户都可以修改数据库,这是很不安全的,所以移除`insecure`包,并声明一些权限规则是很重要的: 169 | 170 | ``` 171 | $ meteor remove insecure 172 | ``` 173 | 174 | 一旦你移除了`insecure`包,就可以用`allow`和`deny`来控制谁可以执行哪些数据库操作。默认情况下,客户端所有的操作都被禁止,所以,需要添加一些`allow`规则。记住:服务端代码和[methods](#meteor_methods) 不受`allow`和`deny`影响 175 | — 这些规则只应用于当不可信的客户端代码调用`insert`, `update`, 和 `remove`时。 176 | 177 | 例如,假设只有当`createBy`字段为当前用户ID时,才允许用户插入新文章,这样用户就不能冒充其他人 178 | 179 | ``` 180 | // In a file loaded on the server (ignored on the client) 181 | Posts.allow({ 182 | insert: function (userId, post) { 183 | // can only create posts where you are the author 184 | return post.createdBy === userId; 185 | }, 186 | remove: function (userId, post) { 187 | // can only delete your own posts 188 | return post.createdBy === userId; 189 | } 190 | // since there is no update field, all updates 191 | // are automatically denied 192 | }); 193 | ``` 194 | 195 | `allow`方法接受三个回调函数:`insert`,`remove`,`update`。三个回调函数的第一个参数是登录用户的`_id`,其余的参数如下: 196 | 197 | 1. `insert(userId, document)` 198 | 199 | `document` 是将要插入到数据库的文档。如果允许插入,则返回`true`,否则返回`false` 200 | 201 | 2. `update(userId, document, fieldNames, modifier)` 202 | 203 | `document` 是将要被修改的文档。`fieldNames`是一个数组,包含了受这次修改影响的一级字段。 204 | `modifier`是传给`collection.update`的第二个参数[Mongo Modifier](#mongo_modifiers)。 205 | 如果使用这个回调无法实现正确的校验,那么推荐使用[methods](#meteor_methods)。 206 | 如果允许修改,则返回`true`,否则返回`false` 207 | 208 | 3. `remove(userId, document)` 209 | 210 | `document`是将要从数据库中移除的文档。如果允许移除则返回`true`,否则返回`false` 211 | 212 | 213 | {{> autoApiBox name="Mongo.Collection#deny" options="insert, update, remove"}} 214 | 215 | `deny`方法允许你选择性的重写`allow`规则。只要有一个`allow`回调函数返回`true`,就允许修改,但必须所有`deny`规则都返回false,才允许修改。 216 | 217 | 例如,我们要重写上面定义的`allow`规则:排除特定标题的文章: 218 | 219 | ``` 220 | // In a file loaded on the server (ignored on the client) 221 | Posts.deny({ 222 | insert: function (userId, post) { 223 | // Don't allow posts with a certain title 224 | return post.title === "First!"; 225 | } 226 | }); 227 | ``` 228 | 229 | {{/template}} 230 | -------------------------------------------------------------------------------- /client/basic/sections/commandline.md: -------------------------------------------------------------------------------- 1 | {{#template name="commandLine"}} 2 | 3 |

命令行工具

4 | 5 | #### `meteor help` 6 | 7 | 获取 `meteor` 命令行使用帮助。运行 `meteor help` 会列出`meteor`所有命令。运行` meteor help 8 | `会打印出关于`meteor `的详细帮助。 9 | 10 | #### `meteor create ` 11 | 12 | 创建一个名为``的子目录,并在里面新建一个Meteor应用。 13 | 14 | #### `meteor run` 15 | 16 | 使用Meteor本地开发服务器运行当前应用,地址为:[http://localhost:3000](http://localhost:3000) 17 | 18 | #### `meteor debug` 19 | 20 | 附带着Node Inspector 运行项目,这样你就可以一步一步跟踪服务端代码。更多信息查看[`meteor debug`](#/full/meteordebug) 21 | 22 | #### `meteor deploy ` 23 | 24 | 打包你的应用,并发布到``。如果你发布到`.meteor.com`,Meteor提供免费的主机,只要``的名字还未被其他人使用。 25 | 26 | #### `meteor update` 27 | 28 | 升级Meteor到最新发布版,然后(如果`meteor update`是在一个应用目录中执行的)升级当前项目使用的包到最新兼容版。 29 | 30 | #### `meteor add` 31 | 32 | 添加一个包(或多个)到Meteor项目中。要查询可用的包,使用`meteor search`命令。 33 | 34 | #### `meteor remove` 35 | 36 | 移除之前添加到项目中的包。查看项目使用的包列表,用`meteor list` 命令。 37 | 38 | #### `meteor mongo` 39 | 40 | 打开一个MongoDB shell来查看、操作数据库中的集合。注意:你必须先运行当前应用(在另外的命令行),`meteor mongo`才能连接到应用的数据库 41 | 42 | #### `meteor reset` 43 | 44 | 重置当前项目为初始状态。移除所有本地数据。 45 | 46 | 如果你经常使用`meteor reset` ,但是又不想丢失一些初始数据,考虑使用[`Meteor.startup`](#/basic/Meteor-startup) 在服务第一次启动时重建这些数据。 47 | 48 | ``` 49 | if (Meteor.isServer) { 50 | Meteor.startup(function () { 51 | if (Rooms.find().count() === 0) { 52 | Rooms.insert({name: "Initial room"}); 53 | } 54 | }); 55 | } 56 | ``` 57 | 58 | {{/template}} 59 | -------------------------------------------------------------------------------- /client/basic/sections/environment.md: -------------------------------------------------------------------------------- 1 | {{#template name="basicEnvironment"}} 2 | 3 |

Environment

4 | 5 | {{> autoApiBox "Meteor.isClient"}} 6 | {{> autoApiBox "Meteor.isServer"}} 7 | 8 | {{#note}} 9 | `Meteor.isServer`可以用来限制代码的运行位置,但是它不会阻止代码发送到客户端。任何你不想发送到客户端的敏感代码,例如包含密码或是认证机制的代码,都应该放到`server`文件夹。 10 | {{/note}} 11 | 12 | {{> autoApiBox "Meteor.startup"}} 13 | 14 | 在服务端,只要服务进程启动完成,回调函数就会执行。在客户端,只要页面ready,回调函数就会执行。 15 | 16 | 最佳实践是:把模板事件,模板Helper,`Meteor.methods`, `Meteor.publish`, 或是 17 | `Meteor.subscribe` 之外的代码包裹进 `Meteor.startup`,这样APP的代码就不会在环境准备好之前运行。 18 | 19 | 例如:当服务端启动时,如果数据库为空则创建一些初始数据,可以用下面的方式: 20 | 21 | ``` 22 | if (Meteor.isServer) { 23 | Meteor.startup(function () { 24 | if (Rooms.find().count() === 0) { 25 | Rooms.insert({name: "Initial room"}); 26 | } 27 | }); 28 | } 29 | ``` 30 | 31 | 如果在服务端进程启动完成之后,或是客户端,页面ready之后调用`Meteor.startup`,回调函数会立即执行。 32 | 33 | {{/template}} 34 | -------------------------------------------------------------------------------- /client/basic/sections/file-structure.md: -------------------------------------------------------------------------------- 1 | {{#template name="basicFileStructure"}} 2 | 3 | ## [文件结构](#filestructure) 4 | 5 | 对于该如何组织应用的文件结构,Meteor是非常灵活的。它会自动加载所有文件,所以不需要再用`