当前位置: 首页 > 工具软件 > Derby.js > 使用案例 >

第9章-WebSocket、Socket.IO和DerbyJS的实时应用程序-9.3.用DerbyJS,Express.js和MongoDB搭建一个在线协作的代码编辑器

龚招
2023-12-01

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

一、项目依赖和package.json

创建项目文件夹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应用程序

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视图

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)保持一致,不然框架就关联不上

五、编辑器Tryout

项目所有文件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,并开始打字,
这时第一个窗口中的代码同步更新了。

 类似资料: