Derby是一个新的,成熟的MVC框架,作为Express的中间件使用。
Express.js是使用中间件概念增强应用程序功能的一个流行的node框架。
Racer也支持Derby,他是一个数据同步引擎,类似Handlebars的模板引擎,拥有很多其他特性。
Meteor和Sails.js是另一个实时全栈Node.js的MVC框架,可与DerbyJS相媲美。
不过Meteor更保守些,他往往需要依赖于其他专有的解决方案和软件包。
本示例将使用Express.js、DerbyJS、MongoDB和Redis构建一个实时的应用程序。
项目结构:
1.项目依赖和package.json
2.服务器端代码
3.DerbyJS应用程序
4.DerbyJS视图
5.编辑器Tryout
创建项目文件夹editor和文件package.json
{
"name": "editor",
"version": "0.0.1",
"private": true,
"description": "在线实时编辑器",
"main": "index.js",
"scripts": {
"test": "mocha test"
},
"git repository": "https://github.com/client-zy/aml",
"keywords": "editor node derby real-time",
"author": "lijian",
"license": "BSD",
"dependencies": {
"derby": "~0.5.12",
"express": "~3.4.8",
"livedb-mongo": "~0.3.0",
"racer-browserchannel": "~0.1.1",
"redis": "~0.10.0"
}
}
模块说明:
derby(DerbyJS)和Express.js是为了路由
redis,racer-browserchannel和livedb-mongo是为了使DerbyJS用Redis和MongoDB数据库
require('derby').run(__dirname + '/server.js');
这行代码用来启动Derby服务器
创建editor/server.js作为应用的入口文件
//1.引入依赖
var path = require('path'),
express = require('express'),
derby = require('derby'),
racerBrowserChannel = require('racer-browserChannel'),
liveDbMongo = require('livedb-mongo'),
//2.定义Derby应用程序的文件
app = require(path.join(__dirname, 'app.js')),
//3.实例化Express.js应用程序
expressApp = module.exports = express(),
//4.Redis客户端
redis = require('redis').createClient(),
//5.本地MongoDB连接URI
mongoUrl = 'mongodb://localhost:27017/editor';
//6.使用连接URI和redis客户端对象创建一个liveDbMongo对象
var store = derby.createStore({
db: liveDbMongo(mongoUrl + '?auto_reconnect', {
safe: true
}),
redis: redis
});
//7.定义一个公共文件夹
var publicDir = path.join(__dirname, 'public');
//8.用链式调用声明使用Express.js中间件
expressApp
.use(express.favicon())
.use(express.compress())
//9.中间件DerbyJS-specific提供了Derby路由和模型对象
.use(app.scripts(store))
.use(racerBrowserChannel(store))
.use(store.modelMiddleware())
.use(app.router())
//10.常规的Express.js路由中间件
.use(expressApp.router);
//11.在一个服务器端混合使用Express.js和DerbyJS是可以的
//用404涵盖所有路由
expressApp.all('*', function(req, res, next){
return next('404: ' + req.url);
});
DerbyJS应用程序(app.js)巧妙地在浏览器和服务器间共享代码,
所以可以在一个地方(Node.js文件)写函数和方法。
然而,依赖DerbyJS规则可以把app.js中的部分代码变成浏览器JavaScript代码
这种行为可以更好地复用和组织代码,因为不需要复制路由、helper函数和实体方法。
把DerbyJS应用程序文件中的代码变成浏览器代码的一种方法是用app.ready(),
editor/app.js文件
//1.声明变量,创建一个应用程序
var app;
app = require('derby').createApp(module);
//2.声明根路由,当用户访问他时会创建一个新snippet并重定向到路径/:snippetId:
app.get('/', function(page, model, _arg, next){
snippetId = model.add('snippets', {
snippetName: _arg.snippetName,
code: 'var'
});
return page.redirect('/', + snippetId);
});
//3.DerbyJS使用和Express.js相似的路由模型,DerbyJS响应页面,Express.js响应报文(res)
//路由/:snippetId是编辑器显示的地方,调用subscribe以支持DOM的实时更新
app.get('/:snippetId', function(page, model, param, next){
//4.model.at方法在collection_name.ID模式中的参数类似于调用findById(),即我们从存储/数据库中得到对象
var snippet = model.at('snippets. '+param.snippetId);
snippet.subscribe(function(err){
console.log(snippet.get());
//5.model.ref()允许把对象绑定到视图展现层。通常在视图层可以写{{_page.snippet}},他会实时地自我更新。
//我们可以使用Ace编辑器让代码看起来漂亮点,Ace被绑到全局的编辑器对象上。
model.ref('_page.snippet', snippet);
page.render();
});
});
//6.DerbyJS的前端JavaScript代码是写在app.ready的回调里的,我们需要在应用程序启动时从Derby模型设置Ace的内容
app.ready(function(model){
editor.setValue(model.get('_page.snippet.code'));
//7.监听模型的改变(来自其他用户)并用新文本更新Ace编辑器
model.on('change', '_page.snippet.code', function(){
if (editor.getValue() !== model.get('_page.snippet.code')){
//8.process.nextTick是一个函数,该函数声明了在下一个事件循环迭代的回调(作为参数传递给他)
//这可以避免无限循环,即当用户的更新模型在Ace编辑器里触发改变事件时,会触发远程模型的不必要的更新
process.nextTick(function(){
editor.setValue(model.get('_page.snippet.code'), 1);
});
}
});
//9.监听Ace改变,并更新DerbyJS模型
editor.getSession().on('change', function(e){
if (editor.getValue() !== model.get('_page.snippet.code')){
process.nextTick(function(){
//10._page是DerbyJS专有的名字,用来渲染/绑定视图
model.set('_page.snippet.code', editor.getValue());
});
}
});
});
DerbyJS视图包含内置标签,如,大部分内容都是在页面加载完之后由Ace编辑器动态生成的。
views/app.html
<Title:>
在线实时编辑器
<Head:>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1">
<title>Editor</title>
<style type="text/css" media="screen">
body{
overflow: hidden;
}
#editor{
margin:0;
position: absolute;
top: 0;
bottom:0;
left:0;
right:0;
}
</style>
//1.从内容分发网络(CDN)加载jQuery和Ace
<script src="//cdnjs.cloudflare.com/ajax/libs/ace/1.1.01/ace.js"></script>
<script src="//code.jquery.com/jquery-2.1.0.min.js"></script>
//2.在body标签里用一个隐藏的input标签和editor元素
<Body:>
<input type="hidden" value="{_page.snippet.code}" class="code" />
<pre id="editor" value="{_page.snippet.code}"></pre>
//3.初始化Ace编辑器对象,作为一个全局变量(编辑器的变量)
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/twilight");//设置主题
editor.getSession().setMode("ace/mode/javascript");//设置语言
</script>
注意:视图名称(app.html)要和DerbyJS应用程序文件的名称(app.js)保持一致,不然框架就关联不上
项目所有文件app.js,index.js,server.js,views/app.html和package.json
用$npm install
安装模块,
用$mongod
和$redis-server
启动数据库
用$node .
或者$node index
启动应用程序
在浏览器中打开http://localhost:3000/,
他会重定向到一个新的snippet(用URL中的ID)
在第二个浏览器窗口中打开同样的url,并开始打字,
这时第一个窗口中的代码同步更新了。