meteor 开发者程序员

Meteor学习笔记

2016-03-22  本文已影响2153人  IT小C

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

这样就可以了

上一篇下一篇

猜你喜欢

热点阅读