Backbonejs RESTful 实践

更新日期: 2015-12-12 阅读次数: 7746 分类: BackboneJS

最近接手的工作项目都是些列表展示,增删改查类的前端。所以先抽空总结一下, 磨刀不费砍柴功。

使用 bower 简化 backbonejs 项目初始化

使用 backbonejs 这类框架比较恶心的一点是,每次项目初始化都要手动下载一遍各种依赖。 以 backbonejs 为例,其依赖

  • underscore
  • jquery (Backbone.View 中的 DOM 操作)

平常的做法是,手动下载 backbonejs, underscore, jquery 依次放到 js/lib 目录下。 完成后,5分钟就过去了。特别打击新建项目的积极性。于是上帝创造了 Bower

sudo npm install -g bower

新建 bower 的配置文件

bower init

安装 backbone

bower install backbone --save

可以看到当前目录下多了一个 bower_components 目录, tree bower_components 目录结果如下

bower_components/
├── backbone
│   ├── backbone.js
│   ├── backbone-min.js
│   ├── backbone-min.map
│   ├── bower.json
│   └── LICENSE
└── underscore
    ├── bower.json
    ├── LICENSE
    ├── README.md
    ├── underscore.js
    ├── underscore-min.js
    └── underscore-min.map

但是没有 jquery 的踪影,需要手动执行

bower install jquery --save

问题来了,bower 会将所有的 jquery 源码文件下载到 bower_components 目录下。 是否需要将这部分文件置于 git 的管理之下呢?

参考 git - Should bower_components be gitignored? - Stack Overflow 这里的讨论。

git 管理 bower_components 目录的好处是,在无法访问三方依赖 git 仓库时,不会影响部署。 不妥之处也很明显,git 仓库会变的异常臃肿。

纠结之后,我决定还是将 bower_components 置于 git 仓库管理之下,就是担心部署时出问题。 单独为 bower_components 写几条 gulp 配置反而增加了初始化复杂度。

结合 requirejs 规划 backbone 的代码组织

为何要规划代码组织?要理清这个问题,首先要明确功能点有哪些:

以招聘类网站后台为例,

  • 招聘信息列表展示
  • 新增招聘信息
  • 更新招聘信息
  • 删除招聘信息
  • 查询招聘信息

功能很简单,看上去写在一个 js 文件里就可以了嘛!实则不然,用户发布招聘信息页面 和后台发布招聘信息页面实际上都要用到同一个 model。拆分出一个独立的 model 文件进行复用已是必然。

├── collections
│   └── jobs.js
├── models
│   └── job.js
├── templates
│   ├── edit_job.html
│   ├── list_job.html
│   └── tr_job.html
└── views
│   ├── edit_job.js
│   ├── list_job.js
│   ├── new_job.js
│   └── tr_job.js
├── main.js   // Bootstrap, 用于设置 requirejs 的配置, 定义 Router, 并初始化 router

讨论:

之所以这里没有定义专门的 router.js 文件,而是合并入 main.js,主要目的是精简文件。

假设,不采用精简的方式,而是拆分出

  • main.js 用于设置 requirejs 的配置
  • app.js 用于加载 Router
  • router.js 用于定义 router

这个方式看似合理,但是实际上并没有让代码结构清晰。 例如,app.js 就是个摆设,实际没有价值。还不如在 main.js 里加载 router.js, 而 router.js 又没有任何复用价值,所以直接在 main.js 定义并初始化即可。

实际上 main.js 即是一个 app.js

后端加载页

新建一个 html 页面

<div id="container">
</div>

<script type="text/javascript" data-main="/static/src/js/main.js" src="/static/bower_components/requirejs/require.js"></script> 

main.js 代码结构

require.config({
	baseUrl: '/static/src/js/',

	paths: {
		jquery: '../../bower_components/jquery/dist/jquery.min',
		underscore: '../../bower_components/underscore/underscore-min',
		backbone: '../../bower_components/backbone/backbone-min',
		text: '../../bower_components/text/text',
		moment: '../../bower_components/moment/min/moment.min',

		admin_app: './admin_app'
	},

	// http://requirejs.org/docs/api.html#config-shim
	// 由于 underscore.js 和 backbone.js 并不支持 AMD, 并且未使用 define 方法,
	// 所以需要使用 shim 配置.
	// 历史上,backbone 是支持 requirejs 的,但是作者认为一个类库专门为特定的
	// 加载器添加支持是错误的做法,所以去掉了对 requirejs 的支持。支持作者:)
	shim: {
		underscore: {
			deps: [],
			exports: "_"
		},
		backbone: {
			deps: ['jquery', 'underscore'],
			exports: "Backbone"
		}
	},
});


define(function (require) {
	var Backbone = require('backbone');
	var JobListView = require('views/list_job');
	var NewJobView = require('views/new_job');
	var EditJobView = require('views/edit_job');

	var AdminRouter = Backbone.Router.extend({
		routes: {
			'new-job': 'newJob',
			'edit-job/:jobID':	'editJob',

			// Default
			'*actions': 'index'
		},

		newJob: function() {
			var newJobView = new NewJobView();
			newJobView.render();
		},

		editJob: function(jobID) {
			var editJobView = new EditJobView(jobID);
			editJobView.render();
		},

		index: function() {
			var jobListView = new JobListView();
			jobListView.render();
		}
	});

	var router = new AdminRouter();
	Backbone.history.start();
});

list_job.js 代码结构

define(function(require) {
	var Backbone = require('backbone');
	var	Jobs = require('collections/jobs');
	var JobView = require('views/tr_job');
	var jobListTemplate = require('text!templates/list_job.html');

	var JobListView = Backbone.View.extend({
		el: "#container",
		template: _.template(jobListTemplate),

		events: {

		},

		initialize: function () {
			this.jobs = new Jobs();
			this.listenTo(this.jobs, 'reset', this.render, this);
			this.jobs.fetch({
				reset: true,
				success: this.onDataHandler,
				error: this.onErrorHandler});
		},

		render: function () {
			this.$el.html(this.template());
			var ul = this.$el.find('#jobs-list');
			this.jobs.each(function(job) {
				var jobView = new JobView(job);
				ul.append(jobView.render().el);
			}, this);
		},

		onDataHandler: function() {
			console.log('onData');
			$('.am-progress-bar').width('100%');
			setTimeout(function() {
				$('.am-progress').hide();
			}, 1000);
		},

		onErrorHandler: function() {
			console.log('onError');
			$('.am-progress-bar').html('网络错误,拉取数据失败,请重试');
		}
	});

	return JobListView;
});

参考

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式