最近接手的工作项目都是些列表展示,增删改查类的前端。所以先抽空总结一下, 磨刀不费砍柴功。
使用 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;
});
参考
- Bower
- node.js - How to change bower's default components folder? - Stack Overflow
- git - Should bower_components be gitignored? - Stack Overflow
- Developing Backbone.js Applications -
- Organizing your application using Modules (require.js) - backbone.js tutorials - cdnjs.com
- Re-Learning Backbone.js – Require.js (AMD and Shim) | BarDev
- Designing a RESTful API with Python and Flask - miguelgrinberg.com
微信关注我哦 👍
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式