Backbone.js系列教程

在这个Backbone.js系列教程中,我们会通过一个单页应用程序实例来讲解,同时包含了Backbone.sync的定制功能相关的应用。在写这教程之前,这个应用早在8月份就已完成,在线上稳定和安全的运行好几个月。

enter image description here

我写这个程序的初衷是为了解决我自己的一个需求,实现一个更好用的gmail to-do list。Gmai原生提供的样式实在是太难看和难用了,所以我就通过backbone.sync加Google的API实现了这个。同时在这个系列教程里部分还会涉及到bootstrap的定制,介绍怎样通过boostrap来构建一个站点。

虽然这个程序不会像Google’s to-do lists一样什么功能都支持,比如:我们没有支持待办项的缩进。但是它会提供我们真正想要的功能,而且足够满足我们日常应用。

在接下来的几个星期里,我将讲到大概内容:

  • 通过Node构建建单页应用程序
  • RequireJS与Backbone.js的使用
  • 谷歌API
  • 编写和运行测试
  • 创建Backbone.js应用程序
  • bootstrap定制
  • 部署到Dropbox的,亚马逊S3,或者其他服务商

环境构建

如果你的重点是客户端脚本,那么我认为这将是对你有用。我们的目标是创建一个开发环境,可以做到以下几点:

  • 允许该客户端的代码被写入作为单独的文件
  • 结合不同的文件转换成适合部署的东西
  • 本地使用单独的文件(以使开发和调试更容易)运行应用程序
  • 管理支持节点模块
  • 运行测试
  • 支持Unix和Windows

实现上述功能我们需要依赖一些工具和库:

  • Node
  • RequireJS
  • Grunt

确定你已经安装Node和NPM包管理和上述的工具后。接下来就是:

1、安装Node模块

创建这个新项目目录,进到目录下创建一个package.json文件,这个json的内容如下:

{
  "name": "btask"
, "version": "0.0.1"
, "private": true
, "dependencies": {
    "requirejs": "latest"
  , "connect": "2.7.0"
  }
, "devDependencies": {
    "mocha": "latest"
  , "chai": "latest"
  , "grunt": "latest"
  , "grunt-exec": "latest"
  }
, "scripts": {
    "grunt": "node_modules/.bin/grunt"
  }
}

建好之后命令行中输入npm install, 依赖模块就都安装到./node_modules目录下了.

文件中有个private属性,主要是用来规定你的npm包是不是公开发布出去,因为我们的应用是闭源的不适合对外公开发布,所以就设置成true

尽管我们不是后端开发,通过npm进行依赖管理来开发项目也很简单,当有新人加入的时候我们只用npm install就搞定了,不用去下载其他乱七八糟的东西

2、本地webserver

项目目录下新建一个app,让后在这个app目录下新建index.html,内容如下:

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <title>bTask</title>
  <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
  <script type="text/javascript" src="js/lib/require.js"></script>
</head>
<body>
</body>
</html>

然后再项目目录下新建server.js:

var connect = require('connect')
  , http = require('http')
  , app
  ;

app = connect()
  .use(connect.static('app'))
  .use('/js/lib/', connect.static('node_modules/requirejs/'))
  .use('/node_modules', connect.static('node_modules'))
  ;

http.createServer(app).listen(8080, function() {
  console.log('Running on http://localhost:8080');
});

这个文件通过connect中间件框架启动指向app目录的一个webserver,如果你不想指向app,你可以通过复制.use(connect.static('app'))app改成你想用的

要运行这个Node服务器,命令行中输入npm start(or node server.js) ,然后浏览器访问http://localhost:8080,你这个时候应该看到的就是一个没有错误的空白页。

3、配置RequireJS

我们项目是遵循的AMD的模块化规范(AMD规范),每个Backbone collection, model, view和其他模块都是以一个独立文件存在,这样我就可以很方便的利用RequireJS管理他们直接依赖关系的列表并根据需要加载它们。

RequireJS项目都会有一个“main”做为入口,通过这个入口加载需要的模块来初始化项目,因此我们需要新建一个包含一些配置的app/js/main.js文件:

requirejs.config({
  baseUrl: 'js',

  paths: {
  },

  shim: {
  }
});

require(['app'],

function(App) {
  window.bTask = new App();
});

上面的require(['app']会加载一个app/js/app.js文件,因此我们需要新建这个文件:

define([], function() {
  var App = function() {
  };

  App.prototype = {
  };

  return App;
});

这就是一个遵循AMD规范的标准模块——define函数由RequireJS内部提供的,他表示你这个模块需要依赖其他模块。

为了实现上面的,首先main.js需要先被加载,所以需要在app/index.html</body>之前通过script标签引入这个js:

<script type="text/javascript" src="js/main.js"></script>

现在浏览器刷新http://localhost:8080并打开脚本调试面板,你应该可以看到bTask已经被实例化了,如下图:

enter image description here

4、测试

完成上面的三步之后,下面我们来看下怎么可以重用创建单元测试套件。Mocha我们已经npm安装好了,现在我们就来创建一些测试用例吧。

首先我们在app目录下新建一个test目录,其中包含一个index.html文件:

<html>
<head>
  <meta charset="utf-8">
  <title>bTask Tests</title>
  <link rel="stylesheet" href="/node_modules/mocha/mocha.css" />
  <style>
.toast-message, #main { display: none }
  </style>
</head>
<body>
  <div id="mocha"></div>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
  <script src="/node_modules/chai/chai.js"></script>
  <script src="/node_modules/mocha/mocha.js"></script>
  <script src="/js/lib/require.js"></script>
  <script src="/js/main.js"></script>
  <script src="setup.js"></script>
  <script src="app.test.js"></script>
  <script>require(['app'], function() { mocha.run(); });</script>
</body>
</html>

我们先确保require/js/app.js加载完成之后,mocha.run再运行

然后再新建一个test/setup.js文件:

var assert = chai.assert;

mocha.setup({
  ui: 'tdd'
, globals: ['bTask']
});

这个文件主要是为了保证让Chai断言assert函数可运行,同时也告知mocha,bTask是一个全局变量

准备好上面的这些之后我们就可以很轻松的写一个测试用例了,在test/app.test.js:

suite('App', function() {
  test('Should be present', function() {
    assert.ok(window.bTask);
  });
});

这个就可以测试RequireJS加载app之后,我们的window.bTask是不是已经定义成功

最后我们需要跟再server端更新这些文件,修改下server.js:

var connect = require('connect')
  , http = require('http')
  , app
  ;

app = connect()
  .use(connect.static('app'))
  .use('/js/lib/', connect.static('node_modules/requirejs/'))
  .use('/node_modules', connect.static('node_modules'))
  .use('/test', connect.static('test/'))
  .use('/test', connect.static('app'))
  ;

http.createServer(app).listen(8080, function() {
  console.log('Running on http://localhost:8080');
});

回到第2步,重启server并访问http://localhost:8080/test/ (别忘了这个/斜线). 这样Mocha就会显示有一个已经通过的单测

5、构建

为我们的“gruntfile”新建一个配置文件grunt.js(grunt默认识别的配置文件Gruntfile.js):

module.exports = function(grunt) {
  grunt.loadNpmTasks('grunt-exec');

  grunt.initConfig({
    exec: {
      build: {
        command: 'node node_modules/requirejs/bin/r.js -o require-config.js'
      }
    }
  });

  grunt.registerTask('copy-require', function() {
    grunt.file.mkdir('build/js/lib');
    grunt.file.copy('node_modules/requirejs/require.js', 'build/js/lib/require.js');
  });

  grunt.registerTask('default', 'exec copy-require');
};

这个文件是用的Jake Harding写得grunt-exec通过执行RequireJS的命令再app下去构建,require-config.js这个文件就是告诉RequireJS怎么去构建:

({
  appDir: 'app/'
, baseUrl: 'js'
, paths: {}
, dir: 'build/'
, modules: [{ name: 'main' }]
})

通过上面的配置,RequireJS将合并所有相关联的文件并最小化;另一个grunt任务就是拷贝一个RequireJS的客户端代码到build/js/lib/require.js,这样我们本地的Connect服务就可以直接找到它。为什么要这样做了?因为这样做之后,我们就通过NPM应用程序更新RequireJS,并可以自动获取其最新版本

要执行Grunt任务,命令行中输入npm run-script grunt.要npm命令run-script执行成功必须有一个package.json.package.json文件我们在第一步的时候就已经创建好了,包含再"grunt": "node_modules/.bin/grunt"里.我一般比较喜欢全局的安装Grunt。

通常情况下在我自己项目中不会使用Grunt,因为我更喜欢的Makefile。事实上,一个Makefile文件也能很简单实现上述的功能。然而对于基于Windows开发的用户是比较尴尬的,所以我还是用了Grunt为了是支持Windows。另外,如果作为一个CS客户端开发者,写相同的nodejs的代码,Grunt相对于GNU Make来说更容易理解和学习。

总结

我们为Backbone.js项目用了Grunt和RequireJS去构建环境,用的Mocha来进行测试。同时我们还学习到怎么用Connect提供一个方便的本地Web服务器。

这是如何建立和管理Backbone.js的单页Web应用程序一个基础,虽然我们还没有写很多代码,但在接下来几周,这个可以让我们很好地去使用Backbone.js和RequireJS

该项目的代码可以在这里找到:dailyjs-backbone-tutorial (2a8517)

译注

由于原文在发表的时候使用的工具版本有很多现在已经更新,如果你check出来的代码不能运行,有下面几点需要修改:

  1. 全局安装grunt命令行工具:npm install -g grunt-cli
  2. 将文件grunt.js中得 grunt.registerTask('default', 'exec copy-require');改为 grunt.registerTask('default', ['exec', 'copy-require']);
  3. 将grunt.js文件重命名为Gruntfile.js(原因请参考gruntjs官网