Meteor是一个基于Nodejs+Websocket+MongoDB的Web应用程序开发平台。
Metror的七个特性
1.只有数据:不在网络上传输HTML,只传输数据并让客户端决定如何渲染
2.一种语言:前后端接口统一使用js
3.无处不在的数据库:客服端,服务器使用相同,透明的API访问数据库
4.延迟补偿:客户端上使用预读和模式模拟技术,使它看起来与数据库的连接是零延迟的
5.完整的响应式:默认实时模式,从数据库到模板的所有层面上,都要使事件驱动的接口有效
6.包容一切:开源的,能与现有的工具和框架整合
7.简单等于生产力
Meteor 1.1开始,支持 Windows/MongoDB 3.0
我们先以win7下的meteor安装为例
安装器下载地址为:
https://install.meteor.com/windows
点击打开之后会自动下载安装,时间有点长,之后需要注册Meteor开发者账号,可以跳过
这样就安装成功了
通过在命令行输入
meteor help
可以查看所有的Meteor指令
下面我们先来创建一个meteor项目
meteor create mypro
之后根据提示输入下列指令来启动meteor
cd mypro
meteor
meteor会先启动proxy,再启动mongoDB,最后启动应用程序
之后在浏览器输入http://localhost:3000/就可以访问meteor应用了
虽然Meteor是基于nodejs的,但是nodejs的包不能直接在Meteor中使用,在Meteor目录下运行如下指令可以查看当前可用的包
meteor list
下面我们通过实例来分析meteor项目的结构,通过
https://github.com/meteor/meteor
下载meteor的源码,在examples里我们进入leaderboard目录,执行
meteor
在浏览器中打开这个项目
这是一个简单的meteor应用,点击名字,下面会出现给这个人加5分的按钮,点击之后会加5分并且自动重新排序
首先,我们看一下leaderboard.html
Meteor默认使用Handelbars模板系统
1.模板的加载
{{> 模板名}}
2.模板的定义
<template name="模板名">...</template>
3.遍历数据集合
{{#each 集合名}}...{{/each}}
4.判断
{{#if 条件为真}}...{{/if}}.....{{#unless 条件为假}}...{{/unless}}
{{#if 条件为真}}...{{/else}}...{{/if}}
Meteor的数据操作共享和延迟补偿技术说明
每个meteor客户端包含一个内存数据库缓存Minimongo,在操作客户端的数据变更时,其实是操作的Minimongo,同时客户端会向服务器发送一个变更请求,Meteor使用DDP(Distributed Data Protocol 分布式数据协议)来传递数据。
Meteor有个机制,客户端向服务器端发起一个写操作时,会立即更新本地缓存而不必等待服务器的响应,如果服务器拒绝变更请求,Meteor会修正客户端缓存,这种技术就叫“延迟补偿”,客户端持有最新的数据,同时服务器对于变更请求有最终的定夺的权利。
在客户端修改数据库确实方便,不过会有安全性问题,所以一般发布到生产环境会移除Meteor的autopublish和insecure包,它们能够模拟每个客户端对服务器上的数据拥有完全读/写权限的效果。
下面来看一下核心代码leaderboard.js
1._id规则
Players = new Mongo.Collection("players");
Meteor在MongoDB中生成的_id是17位的简化id,如果要用mongodb默认的24位id则可以通过
new Meteor.Collection(name,{idGeneration:'MONGO'});
也可以通过下列方法创建ObjectID
new Meteor.Collection.ObjectID(hexString)
2.客户端服务器运行不同的代码
因为leaderboard.js是客户端和服务器都加载的,所以如果要判断是客户端的操作还是服务器端的操作需要通过
if(Meteor.isClient){
//客户端执行的代码
}
if(Meteor.isServer){
//服务器端执行的代码
}
目录说明:
服务器端
JavaScript/所有js
private/所有js
server/所有js
客户端
client/所有js
公用资源
public/资源文件
其他目录下的文件会被客户端和服务器端两者加载
3.Meteor可以使用的数据库操作API
find,findOne,insert,update,upsert,remove,allow,deny
4.Session
Session只能用于客户端
Session.set(key,value); //设置key的value
Session.setDefault(key,value); //设置key默认的value
Session.get(key) //获取key的value
Session.equals(key,value) //判断key的值是否等于value
5.startup
Meteor.startup()是Meteor提供的核心API,可以用在客户端和服务器端,当用在服务器端时,会在服务器进程启动完毕后立即执行,客户端时,会在DOM准备就绪时立即执行。
通常,Meteor.startup()用来做一些初始化的事情,比如往数据库里插入文档或从数据库读取文档并进行展示。
Meteor信奉响应式编程的理念
我们可以在控制台直接给Session赋值,就可以在页面上直接看到效果
能够响应式计算的有
Templates
Meteor.render,Meteor.renderList
Deps.autorun
能够触发变化的响应式数据源有
Session内的变量
Collections内的数据库查询
Meteor.status
Meteor.subscribe内的ready()方法
Meteor.user
Meteor.userId
Meteor.loggingIn
insecure包
模拟每个客户端对服务器上的数据库拥有完全读/写权限的效果,通常生产环境需要移除这个包
meteor remove insecure
这时如果给玩家加5分,会发现玩家排名和分数上移后又瞬间回到原样了,控制台显示update failed: Access denied
这就是延迟补偿,去掉了insecure包,需要修改代码
方法一
在服务器端执行的代码增加
Players.allow({
update: function(userId,doc,fieldNames,modifier){
return true;
}
});
如果返回true,允许客户端执行update操作,false时拒绝,也可以使用collection.deny方法来拒绝客户端修改数据库的请求
如果只允许得分大于30分的进行修改
Players.allow({
update: function(userId,doc,fieldNames,modifier){
return doc.score >= 30 ? true : false;
}
});
只有在客户端试图修改数据时才会触发,而服务器端不受这个限制
方法二.
通过Meteor.methods和Meteor.call的组合来实现客户端修改数据
//server
Meteor.methods({
add5:function(selectedPlayer){
Players.update(selectedPlayer,{$inc:{score:5}});
}
});
//client
Template.leaderboard.events({
'click .inc': function () {
Meteor.call('add5',Session.get("selectedPlayer"));
}
});
autopublish包
使用autopublish包,Meteor会客户端Minimongo中的内容和服务器端的MongoDB同步(除了users集合部分的内容和索引)通常生产环境也需要移除这个包
meter remove autopublish
这时候客户端和服务器端的数据库就不同步了,需要修改代码
//server
Meteor.publish("leaderboard",function(){
return Players.find();
});
//client
Meteor.subscribe("leaderboard");
这样就可以了,假如我们有多个集合,可以选择性地从服务器端发布,然后从客户端订阅
Meteor.publish只能用于服务器端
Meteor.subscribe只能用于客户端
这里要说一下,leaderboard.js所有的修改之后Meteor服务器会自动重启应用,这样就能看到最新的修改结果,还是很方便的
Meteor文件的加载顺序
lib目录下的文件先加载
main.*的文件最后加载
子目录的文件在根目录的文件之前加载
同目录的文件按文件名的字母顺序加载
下面我们以搭建一个微博为例子来讲解需要注意的地方
我们采用的meteor组件是jquery+bootstrap+backbone+accounts-password
Meteor添加包
Meteor添加bootstrap依赖包
meteor add bootstrap
无须在<head>中引入bootstrap即可使用它,也无需引用jquery,jquery已经内置在Meteor的核心包中
在Meteor的页面里必须包含head,body标签,不然会报错,在template模板页面里不需要
我们不用在html页面里引入只含有template标签的模板页面,因为Meteor会将每个模板转化成js函数并加载它们
添加backbone包来实现路由功能
meteor add backbone
backbone的详细使用可以参考
http://backbonejs.org/
这里需要注意meteor session的用法
session只能用于客户端,当session值改变时,如果需要更新页面,需要在helpers里返回
Session.setDefault("currentUrl",{index:"active",reg:""});
var urlRouter = Backbone.Router.extend({
routes:{
"":"index",
"reg":"reg"
},
index:function(){
console.log("index");
Session.set("currentUrl",{index:"active",reg:""});
},
reg:function(){
console.log("reg");
Session.set("currentUrl",{index:"",reg:"active"});
},
redirect:function(url){
this.navigate(url,true);
}
});
Router = new urlRouter;
Template.container.helpers({
currentUrl: function () {
return Session.get('currentUrl');
}
});
Meteor.startup(function(){
Backbone.history.start({pushState:true});
});
这样在html里才能访问到currentUrl
{{#if currentUrl.index}}
{{> index}}
{{/if}}
{{#if currentUrl.reg}}
{{> reg}}
{{/if}}
还有一种方法是直接通过模板名下的变量名来返回,但是浏览器会警告
Template.info.info = function(){
return Session.get("info");
}
添加accounts-password包构建用户认证系统,他可以帮助你快速的完成用户的注册,登陆
meteor add accounts-password
Template.reg.events({
'click #submit': function(e){
e.preventDefault();
Accounts.createUser({username:$("#username").val(),password:$("#password").val()});
}
});
这样就会在数据库中保存用户名和密码,密码是经过SRP加密的
{{currentUser}}存储了当前在线用户的信息(其实是调用了Meteor.user()),可以用来判断用户是否在线,不在线返回null
{{#if currentUser}}
<a href="/logout" class="btn btn-primary-large">退出</a>
{{else}}
<a href="/reg" class="btn btn-primary-large">注册</a>
{{/if}}
Meteor.userId()可以用来判断用户是否在线,存储当前在线用户的_id,不在线返回null
logout:function(){
if(Meteor.userId()){
Meteor.logout();
this.navigate("/",true);
Session.set("info",{success:"退出成功",error:""});
}else{
this.navigate("/",true);
Session.set("info",{success:"",error:"用户不在线"});
}
},
Meteor.loginWithPassword可以实现用户登录功能
Meteor.loginWithPassword($("#username").val(),$("#password").val(),function(err){
if(err){
Session.set("info",{success:"",error:err.reason});
}else{
Session.set("info",{success:"登陆成功",error:""});
Router.redirect("/");
}
});
如果密码错误,err.reason会返回
如果用户不存在,err.reason返回
这些认证判断都是Meteor为我们做好的
Meteor对html标签的闭合要求是很高的,如果有没有闭合的标签,就会提示出错,还是很智能的
如果我们在客户端的js文件里定义了数据集
Posts = new Meteor.Collection("posts");
之后直接调用insert方法会报错
因为数据集在服务器端没有定义,我们需要的服务器端的js文件里也增加一句,然后可以通过allow来控制谁可以操作数据
Posts = new Meteor.Collection("posts");
Posts.allow({
insert:function(userId,doc){
return userId && (doc.user._id === userId);
}
});
添加markdown支持
Meteor提供了一个showdown包,只需将Markdown文本添加到{{#markdown}}...{{/markdown}}标签内
meteor add showdown
该Meteor微博的完整源码我已上传到Github上
https://github.com/ccfromstar/meteor_weibo
最后说一下Meteor的部署
有两种方法
1.部署到Meteor提供的免费服务器上
meteor deploy weibocc.meteor.com
删除站点重新部署,如果想清空数据库
meteor deploy weibocc.meteor.com -D
如果希望是自己私有的,可以设置一个密码,这样下次更新代码时候就需要密码才能更新
meteor deploy weibocc.meteor.com -P
2.将应用打包部署到自己的服务器上
meteor bundle weibocc.tgz
要运行这个应用,需要Nodejs和MongoDB
PORT=3000 MONGO_URL=mongodb://localhost:27017/weibo node bundle/main.js
这里需要注意的是我们必须重新编译fibers包
cd bundle/programs/server/node_modules
rm -r fibers
npm install fibers@1.0.1
这样就可以了