[TOC]
## 概況
### 背景
看到項目上的移動框架,網上尋找了一下,發現原來這些一開始都有。于是,找了個示例開始構建一個移動平臺的CMS——[墨頎 CMS](http://cms.moqi.mobi/),方便項目深入理解的同時,也可以自己維護一個CMS系統。
### Showcase
GitHub:?[http://github.com/phodal/moqi.mobi](http://github.com/phodal/moqi.mobi)
Demo:?[墨頎 CMS](http://cms.moqi.mobi/)
### jQuery + Backbone + UnderScore + Require.JS
嘗試過用AngularJS和EmberJS,發現對于使用AngularJS以及EmberJS來說,主要的問題是要使用自己熟悉的東西沒那么容易引入。而且考慮到谷歌向來對自己的項目的支持不是很好~~,所以便放棄了AngluarJS的想法。
于是開始尋找一些方案,但是最后還是選擇了一個比較通用的方案。
* RequireJS
* jQuery
* Underscore
* Backbone
相對于AngularJS來說,Backbone是一個輕量級的方案,從大小上來說。對于自己來說,靈活性算是其中好的一點,也就是自己可以隨意的加入很多東西。
**關于Backbone**
> Backbone.js是一套JavaScript框架與RESTful JSON的應用程式接口。也是一套大致上符合MVC架構的編程范型。Backbone.js以輕量為特色,只需依賴一套Javascript 函式庫即可運行。
具體功能上應該是
* Backbone 輕量級,支持jquery,自帶路由,對象化視圖,強大的sync機制減少頁面大小從而加快頁面顯示。
* jQuery jQuery使用戶能更方便地處理HTML(標準通用標記語言下的一個應用)、events、實現動畫效果,并且方便地為網站提供AJAX交互。不過主要是jQuery能夠使用戶的html頁面保持代碼和html內容分離,只需定義id即可。
* Underscore是Backbone的依賴庫 Underscore 是一個JavaScript實用庫,提供了類似Prototype.js的一些功能,但是沒有繼承任何JavaScript內置對象。
* RequireJS 你可以順序讀取僅需要相關依賴模塊。
前臺UI,使用的是Pure CSS,一個輕量級的CSS框架,但是最后感覺,總體用到一起,大小還是相當的。只是可以有一個更好的移動體驗。
****其他可替換的框架**
**AngularJS**,考慮到某些因素,可能會替換掉Backbone,但是還不是當前可行的方案。為了學習是一方案,也為了更好的普及某些東西。
**handlebars**?Handlebars 是Mustache的改進,顯示與邏輯分離,語法兼容Mustache,可以編譯成代碼,改進Mustache對路徑的支持,但是若需要在服務端運行需要使用服務端Javascript引擎如Node.js。
**項目**
前后端分離設計,后臺對前臺只提供JSON數據,所以在某種意義上來說可能會只適合瀏覽,和這個要配合后臺的框架。總的來說,適合于閱讀類的網站。
**源碼**
代碼依然是放在Github上,基本功能已經可以Works了。
[https://github.com/gmszone/moqi.mobi](https://github.com/gmszone/moqi.mobi)
## 步驟
### Step 1: 使用Require.js管理依賴
**庫及依賴**
這里用的是bower的JS來下載庫,詳細可以參考一下[bower install js使用bower管理js](http://www.phodal.com/blog/use-bower-to-install-js-plugins/)?這篇文章。
需要下載的庫有
* RequireJS
* Backbone
* Underscore
* Mustache
* jQuery
引用官網的示例
~~~
<!DOCTYPE html>
<html>
<head>
<title>My Sample Project</title>
<!-- data-main attribute tells require.js to load
scripts/main.js after require.js loads. -->
<script data-main="js/main" src="lib/require.js"></script>
</head>
<body>
<h1>My Sample Project</h1>
</body>
</html>
~~~
我們需要一個require.js和一個main.js放在同一個目錄,在main.js中用使用require()來載入需要加載的腳本。
~~~
require.config({
baseUrl: 'lib/',
paths: {
jquery: 'jquery-2.1.1.min'
},
shim: {
underscore: {
exports: '_'
}
}
});
require(['../app'], function(App){
App.initialize();
});
~~~
在config中可以配置好其他的庫,接著調用了app.js。
~~~
define(['jquery', 'underscore'], function($, _){
var initialize = function() {
console.log("Hello World");
}
return {
initialize: initialize
};
});
~~~
當打開index.html的時候便會在console中輸出`Hello World`。這樣我們就完成一個基本的框架,只是還沒有HTML, 文件列表如下所示
~~~
.
|____app.js
|____backbone.js
|____HomeView.js
|____index.html
|____jquery.js
|____main.js
|____mustache.js
|____require.js
|____router.js
|____text.js
|____underscore.js
~~~
在這里有些混亂,但是為了少去其中的一些配置的麻煩,就先這樣講述。
### Step 2: 添加路由
用Backbone的一個目的就在于其的路由功能,于是便添加這樣一個js——`router.js`,內容如下所示:
~~~
define([
'jquery',
'underscore',
'backbone',
'HomeView.js'
], function($, _, Backbone, HomeView) {
var AppRouter = Backbone.Router.extend({
routes: {
'index': 'homePage',
'*actions': 'homePage'
}
});
var initialize = function() {
var app_router = new AppRouter;
app_router.on('route:homePage', function() {
var homeView = new HomeView();
homeView.render();
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
~~~
在這里我們先忽略掉HomeView.js,因為這是下面要講的,在router.js中,我們定義了一個AppRouter,
* `index`指向的是在初始化時候定義的homePage,這樣就可以將主頁轉向HomeView.js。
* `*actions`便是將其他未匹配的都轉向homePage。
接著我們需要修改一下`app.js`,讓他一運行地時候便可以進入路由選擇
~~~
define(['jquery', 'underscore', 'router'], function($, _, Router) {
var initialize = function() {
Router.initialize();
};
return {
initialize: initialize
};
});
~~~
也就是初始化一下路由。
### Step 3: 創建主頁View
使用Mustache的優點在于,后臺僅僅只需要提供數據,并在前臺提供一個位置。因此我們修改了下HTML
~~~
<!DOCTYPE html>
<html>
<head>
<title>My Sample Project</title>
<script data-main="main" src="require.js"></script>
</head>
<body>
<div id="aboutArea">{{project}}</div>
</body>
</html
~~~
創建了aboutArea這樣一個ID,于是我們便可以很愉快地在HomeView.js中添加project的數據。
~~~
define([
'jquery',
'underscore',
'mustache',
'text!/index.html'
], function($, _, Mustache, indexTemplate) {
var HomeView = Backbone.View.extend({
el: $('#aboutArea'),
render: function() {
var data = {
project: "My Sample Project"
};
this.$el.html(Mustache.to_html(indexTemplate, data));
}
});
return HomeView;
});
~~~
在HomeView.js中,定義了data這樣一個object,代碼最終的效果便是用“My Sample Project”替換到HTML中的{{project}}。
這樣我們便完成了一個真正意義上的移動web CMS平臺的Hello,World,剩下的便是添加一個又一個的腳手架。
當看到[墨頎 CMS](http://cms.moqi.mobi/)的菜單,變成一個工具欄的時候,變覺得這一切有了意義。于是就繼續看看這樣一個CMS的邊欄是怎么組成的。
**RequireJS與jQuery 插件示例**
一個簡單的組合示例如下所示,在main.js中添加下面的內容
~~~
requirejs.config( {
"shim": {
"jquery-cookie" : ["jquery"]
}
} );
~~~
接著在另外的文件中添加
~~~
define(["jquery"],
function($){
//添加函數
});
~~~
這樣我們就可以完成一個簡單的插件的添加。
### Step 4: jQuery Sidr
> The best jQuery plugin for creating side menus and the easiest way for doing your menu responsive
這是一個創建響應式側邊欄的最好的也是最簡單的工具,于是我們需要下載jQuery.sidr.min.js到目錄中,接著修改一下main.js:
~~~
require.config({
baseUrl: 'lib/',
paths: {
'text': 'text',
jquery: 'jquery-2.1.1.min',
async: 'require/async',
json: 'require/json',
mdown: 'require/mdown',
router: '../router',
templates: '../templates',
jquerySidr: 'jquery.sidr.min',
markdownConverter : 'require/Markdown.Converter'
},
shim: {
jquerySidr:["jquery"],
underscore: {
exports: '_'
}
}
});
require(['../app'], function(App){
App.initialize();
});
~~~
添加jquery.sidr.min到里面。
引用官方的示例代碼
~~~
$(document).ready(function() {
$('#simple-menu').sidr();
});
~~~
我們需要將上面的初始化代碼添加到app.js的初始化中,
~~~
define([
'jquery',
'underscore',
'backbone',
'router',
'jquerySidr'
], function($, _, Backbone, Router){
var initialize = function(){
$(document).ready(function() {
$('#menu').sidr();
});
Router.initialize();
};
return {
initialize: initialize
};
});
~~~
這樣打開[墨頎 CMS](http://cms.moqi.mobi/)便可以看到最后的效果。
正在一步步完善[墨頎 CMS](http://cms.moqi.mobi/),在暫時不考慮其他新的功能的時候,先和[自己的博客](http://www.phodal.com/)整合一下。
### Step 5: Django Tastypie示例
之前用AngluarJS做的全部文章的時候是Tastypie做的API,只是用來生成的是博客的內容。只是打開的速度好快,可以在1秒內打開,獻上URL:
[http://www.phodal.com/api/v1/url/?offset=0&limit=20&format=json](http://www.phodal.com/api/v1/url/?offset=0&limit=20&format=json)
之前只是拿Tastypie生成一些簡單的JSON數據,如keywords_string,slug,title這些簡單的數據。
因為這里的Blogpost是來自mezzanine,原來的`api.py`,如下所示:
~~~
from tastypie.resources import ModelResource
from mezzanine.blog.models import BlogPost, BlogCategory
class AllBlogSlugResource(ModelResource):
class Meta:
queryset = BlogPost.objects.published()
resource_name = "url"
fields = ['keywords_string', 'slug', 'title']
allowed_methods = ['get']
class BlogResource(ModelResource):
class Meta:
queryset = BlogPost.objects.published()
resource_name = "blog"
fields = ['keywords_string', 'slug', 'title', 'content', 'description']
allowed_methods = ['get']
~~~
而這時為了測試方便,還需要解決跨域請求的問題,生成的內容大致如下所示:
~~~
{
"meta": {
"limit": 1,
"next": "/api/v1/url/?offset=1&limit=1&format=json",
"offset": 0,
"previous": null,
"total_count": 290
},
"objects": [
{
"keywords_string": "jquery backbone mustache underscore siderbar",
"resource_uri": "/api/v1/url/369/",
"slug": "use-jquery-backbone-mustache-build-mobile-app-cms-add-jquery-plugins",
"title": "構建基于Javascript的移動web CMS——添加jQuery插件"
}
]
}
~~~
#### 跨域支持
于是網上搜索了一下,有了下面的代碼:
~~~
from tastypie.resources import Resource, ModelResource
from mezzanine.blog.models import BlogPost, BlogCategory
from django.http.response import HttpResponse
from tastypie.exceptions import ImmediateHttpResponse
from tastypie import http
from tastypie.serializers import Serializer
class BaseCorsResource(Resource):
def create_response(self, *args, **kwargs):
response = super(BaseCorsResource, self).create_response(*args, **kwargs)
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Headers'] = 'Content-Type'
return response
def post_list(self, request, **kwargs):
response = super(BaseCorsResource, self).post_list(request, **kwargs)
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Expose-Headers'] = 'Location'
return response
def method_check(self, request, allowed=None):
if allowed is None:
allowed = []
request_method = request.method.lower()
allows = ','.join(map(lambda s: s.upper(), allowed))
if request_method == 'options':
response = HttpResponse(allows)
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Headers'] = 'Content-Type'
response['Access-Control-Allow-Methods'] = "GET, PUT, POST, PATCH"
response['Allow'] = allows
raise ImmediateHttpResponse(response=response)
if not request_method in allowed:
response = http.HttpMethodNotAllowed(allows)
response['Allow'] = allows
raise ImmediateHttpResponse(response=response)
return request_method
class AllBlogSlugResource(BaseCorsResource, ModelResource):
class Meta:
queryset = BlogPost.objects.published()
resource_name = "url"
fields = ['keywords_string', 'slug', 'title']
allowed_methods = ['get']
serializer = Serializer()
class BlogResource(BaseCorsResource, ModelResource):
class Meta:
queryset = BlogPost.objects.published()
resource_name = "blog"
fields = ['keywords_string', 'slug', 'title', 'content', 'description']
allowed_methods = ['get']
serializer = Serializer()
~~~
接著便可以很愉快地、危險地跨域。
#### 整合
接著修改了一下代碼中configure.json的blogListUrl,以及模塊
~~~
<div class="l-box blogPosts">
<h2>動 態</h2>
{{#objects}}
<p>
<!--<span class="date">{{created}}</span>-->
<a href="#/blog/{{slug}}" alt="{{title}}">{{title}}</a>
</p>
{{/objects}}
</div>
~~~
便可以請求到結果了。
一開始對于可配置的選擇是正確的.
### Step 6: RequireJS Plugins
網上搜索到一個叫RequireJS Plugins的repo。
里面有這樣的幾個插件:
* **async**?: Useful for JSONP and asynchronous dependencies (e.g.?Google Maps).
* **font**?: Load web fonts using the?[WebFont Loader API](https://code.google.com/apis/webfonts/docs/webfont_loader.html)?(requires?`propertyParser`)
* **goog**?: Load?[Google APIs](http://code.google.com/apis/loader/)?asynchronously (requires?`async!`?plugin and?`propertyParser`).
* **image**?: Load image files as dependencies. Option to “cache bust”.
* **json**?: Load JSON files and parses the result. (Requires?`text!`?plugin).
* **mdown**?: Load Markdown files and parses into HTML. (Requires?`text!`?plugin and a markdown converter).
* **noext**?: Load scripts without appending “.js” extension, useful for dynamic scripts.
于是,我們可以用到這里的json用來加載JSON文件,雖然也可以用Requirejs的text插件,但是這里的json有對此稍稍的優化。
在后面的部分中我們也用到了mdown,用于顯示一個md文件,用法上大致是一樣的。
將json.js插件放到目錄里,再配置好main.js。
~~~
require.config({
paths: {
'text': 'text',
jquery: 'jquery',
json: 'require/json'
},
shim: {
underscore: {
exports: '_'
}
}
});
require(['app'], function(App) {
App.initialize();
});
~~~
于是我們將HomeView.js中的data變為configure的數據,這樣便可以直接使用這個json文件。
~~~
define([
'jquery',
'underscore',
'mustache',
'text!/index.html',
'json!/configure.json'
], function($, _, Mustache, indexTemplate, configure) {
var HomeView = Backbone.View.extend({
el: $('#aboutArea'),
render: function() {
this.$el.html(Mustache.to_html(indexTemplate, configure));
}
});
return HomeView;
});
~~~
configure.json的代碼如下所示:
~~~
{
"project": "My Sample Project"
}
~~~
最后實現的效果和模板結束是一樣的,只會在頁面上顯示
~~~
My Sample Project
~~~
在[墨頎 CMS](http://cms.moqi.mobi/)中的動態的文章是從我博客的API加載過來的,因為當前沒有其他好的CMS當接口。之前直接拿博客的DB文件+Nodejs+RESTify生成了一個博客的API,而且可以支持跨域請求。
### Step 6: 簡單的博客
這次我們可以簡單的做一個可以供移動平臺閱讀的博客,除了不能寫作以外(ps:不能寫作還能叫博客么)。對于寫博客的人來說更多的只是寫,而對于讀者來說,他們只需要讀,所以在某種意義上可以將博客的寫和讀分離開來。
對于用戶來說,博客是由兩個頁面構建的:
* 博文列表(blogposts list)
* 博客內容(blogposts detail)
在這里我們先關注博文列表
**博文列表**
博文列表的內容一般有:
* 作者(Author)
* 標題(title)
* 創建時間/修改時間(Time)
* 關鍵詞(Keywords)
* 摘要(Description)
* 鏈接(Slug)
一個簡單的示例如下,也就是我們接下來要用到的**1.json**中的一部分。
~~~
[{
"title": "構建基于Javascript的移動web CMS入門——簡介",
"slug": "use-jquery-backbone-mustache-build-mobile-app-cms",
"description": "看到項目上的移動框架,網上尋找了一下,發現原來這些一開始都有。于是,找了個示例開始構建一個移動平臺的CMS——墨頎 CMS,方便項目深入理解的同時,也可以自己維護一個CMS系統。",
"keywords": [
"backbone",
"jquery",
"underscore",
"mustache"
],
"created": "2014-07-17 14:16:18.035763"
}]
~~~
這里基本上也就有了上面的要素,除了作者,當然因為作者只有一個,所以在里面寫作者就是在浪費流量和錢啊。接著我們就是要把上面的內容讀取出來放到CMS里。和之前不同的是,雖然我們可以用和[墨頎CMS文件 JSON文件](http://www.phodal.com/blog/use-jquery-backbone-mustache-build-mobile-app-cms-json-configure/)一樣的方法,但是顯然這種方法很快就會不適用。
#### 獲取在線數據
這里會用到Backbone的Model,我們先創建一個Model
~~~
var BlogPostModel = Backbone.Model.extend({
name: 'Blog Posts',
url: function(){
return this.instanceUrl;
},
initialize: function(props){
this.instanceUrl = props;
}
});
~~~
我們需要在初始化的時候傳入一個URL,以便在`getBlog`的時候用到,為了方便調試將url改為同路徑的1.json文件
~~~
getBlog: function() {
var url = '/1.json';
var that = this;
collection = new BlogPostModel;
collection.initialize(url);
collection.fetch({
success: function(collection, response){
that.render(response);
}
});
},
~~~
這樣當成功獲取數據的時候便render頁面。最后的HomeView.js代碼如下所示:
~~~
define([
'jquery',
'underscore',
'mustache',
'text!/index.html',
'text!/blog.html'
], function($, _, Mustache, indexTemplate, blogTemplate) {
var BlogPostModel = Backbone.Model.extend({
name: 'Blog Posts',
url: function(){
return this.instanceUrl;
},
initialize: function(props){
this.instanceUrl = props;
}
});
var HomeView = Backbone.View.extend({
el: $('#aboutArea'),
initialize: function(){
this.getBlog();
},
getBlog: function() {
var url = '/1.json';
var that = this;
collection = new BlogPostModel;
collection.initialize(url);
collection.fetch({
success: function(collection, response){
that.render(response);
}
});
},
render: function(response) {
this.$el.html(Mustache.to_html(blogTemplate, response));
}
});
return HomeView;
});
~~~
這樣也就意味著我們需要在index.html中創建一個id為aboutArea的div。接著我們需要創建一個新的Template——blog.html,它的內容就比較簡單了,只是簡單的Mustache的使用。
~~~
{{#.}}
<h2><a href="{{slug}}" alt="{{title}}">{{title}}</a></h1>
<p>{{description}}</p>
{{/.}}
~~~
`{{#.}}`及`{{/.}}`可以用于JSON數組,即循環,也可以是判斷是否存在。
最后的結果便是:
~~~
<h2><a href="use-jquery-backbone-mustache-build-mobile-app-cms" alt="構建基于Javascript的移動web CMS入門——簡介">構建基于Javascript的移動web CMS入門——簡介</a></h2>
<p>看到項目上的移動框架,網上尋找了一下,發現原來這些一開始都有。于是,找了個示例開始構建一個移動平臺的CMS——墨頎 CMS,方便項目深入理解的同時,也可以自己維護一個CMS系統。</p>
~~~
把description去掉,再修改一個CSS,便是我們在首頁看到的結果。
下一次我們將打開這些URL。
#### 如何查看是否支持JSON跨域請求
本次代碼下載:[https://github.com/gmszone/moqi.mobi/archive/0.1.1.zip](https://github.com/gmszone/moqi.mobi/archive/0.1.1.zip)
一個簡單的工具就是
~~~
curl I -s http://example.com
~~~
在這里我們查看
~~~
curl -I -s http://api.phodal.net/blog/page/1
~~~
應該要返回 Access-Control-Allow-Origin: *
~~~
HTTP/1.1 200 OK
Server: mokcy/0.17.0
Date: Thu, 24 Jul 2014 00:38:19 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 3943
Connection: keep-alive
Vary: Accept-Encoding
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: X-Requested-With
Cache-Control: max-age=600
~~~
在有了上部分的基礎之后,我們就可以生成一個博客的內容——BlogPosts Detail。這樣就完成了我們這個[移動CMS](http://cms.moqi.mobi/)的幾乎主要的功能了,有了上節想必對于我們來說要獲取一個文章已經不是一件難的事情了。
#### 獲取每篇博客
于是我們照貓畫虎地寫了一個`BlogDetail.js`
~~~
define([
'jquery',
'underscore',
'mustache',
'text!/blog_details.html'
],function($, _, Mustache, blogDetailsTemplate){
var BlogPostModel = Backbone.Model.extend({
name: 'Blog Posts',
url: function(){
return this.instanceUrl;
},
initialize: function(props){
this.instanceUrl = props;
}
});
var BlogDetailView = Backbone.View.extend ({
el: $("#content"),
initialize: function () {
},
getBlog: function(slug) {
url = "http://api.phodal.net/blog/" + slug;
var that = this;
collection = new BlogPostModel;
collection.initialize(url);
collection.fetch({
success: function(collection, response){
that.render(response);
}
});
},
render: function(response){
this.$el.html(Mustache.to_html(blogDetailsTemplate, response));
}
});
return BlogDetailView;
});
~~~
又寫了一個`blog_details.html`,然后,然后
~~~
<div class="information pure-g">
{{#.}}
<div class="pure-u-1 ">
<div class="l-box">
<h3 class="information-head"><a href="#/blog/{{slug}}" alt="{{title}}">{{title}}</a></h3>
<p>
發布時間:<span>{{created}}</span>
<p>
{{{content}}}
</p>
</p>
</div>
</div>
{{/.}}
</div>
~~~
我們顯然需要稍微地修改一下之前`blog.html`的模板,為了讓他可以在前臺跳轉
~~~
{{#.}}
<h2><a href="#/{{slug}}" alt="{{title}}">{{title}}</a></h2>
<p>{{description}}</p>
{{/.}}
~~~
問題出現了,我們怎樣才能進入最后的頁面?
#### 添加博文的路由
在上一篇結束之后,每個博文都有對應的URL,即有對應的slug。而我們的博客的獲取就是根據這個URL,獲取的,換句話說,這些事情都是由API在做的。這里所要做的便是,獲取博客的內容,再render。這其中又有一個問題是ajax執行的數據無法從外部取出,于是就有了上面的getBlog()調用render的方法。
我們需要傳進一個參數,以便告訴BlogDetail需要獲取哪一篇博文。
~~~
routes: {
'index': 'homePage',
'blog/*slug': 'blog',
'*actions': 'homePage'
}
~~~
`*slug`便是這里的參數的內容,接著我們需要調用getBlog(slug)對其進行處理。
~~~
app_router.on('route:blog', function(blogSlug){
var blogDetailsView = new BlogDetail();
blogDetailsView.getBlog(blogSlug);
});
~~~
最后,我們的`router.js`的內容如下所示:
~~~
define([
'jquery',
'underscore',
'backbone',
'HomeView',
'BlogDetail'
], function($, _, Backbone, HomeView, BlogDetail) {
var AppRouter = Backbone.Router.extend({
routes: {
'index': 'homePage',
'blog/*slug': 'blog',
'*actions': 'homePage'
}
});
var initialize = function() {
var app_router = new AppRouter;
app_router.on('route:homePage', function() {
var homeView = new HomeView();
homeView.render();
});
app_router.on('route:blog', function(blogSlug){
var blogDetailsView = new BlogDetail();
blogDetailsView.getBlog(blogSlug);
});
Backbone.history.start();
};
return {
initialize: initialize
};
});
~~~
接著我們便可以很愉快地打開每一篇博客查看里面的內容了。
當前[墨頎CMS](http://cms.moqi.mobi/)的一些基礎功能設計已經接近尾聲了,在完成博客的前兩部分之后,我們需要對此進行一個簡單的重構。為的是提取出其中的獲取Blog內容的邏輯,于是經過一番努力之后,終于有了點小成果。
### Step 7: 重構
我們想要的結果,便是可以直接初始化及渲染,即如下的結果:
~~~
initialize: function(){
this.getBlog();
},
render: function(response){
var about = {
about:aboutCMS,
aboutcompany:urlConfig["aboutcompany"]
};
response.push(about);
this.$el.html(Mustache.to_html(blogPostsTemplate, response));
}
~~~
為的便是簡化其中的邏輯,將與View無關的部分提取出來,最后的結果便是都放在初始化里,顯然我們需要一個`render`,只是暫時放在`initialize`應該就夠了。下面便是最后的結果:
~~~
initialize: function(){
var params='#content';
var about = {
about:aboutCMS,
aboutcompany:configure["aboutcompany"]
};
var blogView = new RenderBlog(params, '/1.json', blogPostsTemplate);
blogView.renderBlog(about);
}
~~~
我們只需要將id、url、template傳進去,便可以返回結果,再用getBlog部分傳進參數。再渲染結果,這樣我們就可以提取出兩個不同View里面的相同的部分。
#### 構建函數
于是,我們就需要構建一個函數RenderBlog,只需要將id,url,template等傳進去就可以了。
~~~
var RenderBlog = function (params, url, template) {
this.params = params;
this.url = url;
this.template = template;
};
~~~
用Javascript的原型繼承就可以實現這樣的功能,雖然還不是很熟練,但是還是勉強用了上來。
~~~
RenderBlog.prototype.renderBlog = function(addInfo) {
var template = this.template;
var params = this.params;
var url = this.url;
var collection = new BlogPostModel;
collection.initialize(url);
collection.fetch({
success: function(collection, response){
if(addInfo !== undefined){
response.push(addInfo);
}
RenderBlog.prototype.render(params, template, response);
}
});
};
RenderBlog.prototype.render = function(params, template, response) {
$(params).html(Mustache.to_html(template, response));
};
~~~
大致便是將原來的函數中的功能抽取出來,再調用自己的方法。于是就這樣可以繼續進行下一步了,只是暫時沒有一個明確的方向。
在和幾個有興趣做**移動CMS**的小伙伴討論了一番之后,我們覺得當前比較重要的便是統一一下RESTful API。然而最近持續斷網中,又遭遇了一次停電,暫停了對API的思考。在周末無聊的時光了看了《人間失格》,又看了會《一個人流浪,不必去遠方》。開始思考所謂的技術以外的事情,或許這將是下一篇討論的話題。
正在我對這個[移動CMS](http://cms.moqi.mobi/)的功能一籌莫展的時候,幫小伙伴在做一個圖片滑動的時候,便想著將這個功能加進去,很順利地找到了一個庫。
### Step 8: 移動CMS滑動
我們所需要的兩個功能很簡單
* 當用戶向右滑動的時候,菜單應該展開
* 當用戶向左滑動的時候,菜單應該關閉
在官網看到了一個簡單的示例,然而并不是用于這個菜單,等到我完成之后我才知道:為什么不用于菜單?
找到了這樣一個符合功能的庫,雖然知道要寫這個功能也不難。相比于自己寫這個庫,還不如用別人維護了一些時候的庫來得簡單、穩定。
> jQuery Plugin to obtain touch gestures from iPhone, iPod Touch and iPad, should also work with Android mobile phones (not tested yet!)
然而,它并不會其他一些設備上工作。
**添加jQuery Touchwipe**
添加到requirejs的配置中:
~~~
require.config({
baseUrl: 'lib/',
paths: {
jquery: 'jquery-2.1.1.min',
router: '../router',
touchwipe: 'jquery.touchwipe.min'
},
shim: {
touchwipe: ["jquery"],
underscore: {
exports: '_'
}
}
});
require(['../app'], function(App){
App.initialize();
});
~~~
(注:上面的代碼中暫時去掉了一部分無關本文的,為了簡單描述。)
接著,添加下面的代碼添加到app.js的初始化方法中
~~~
$(window).touchwipe({
wipeLeft: function() {
$.sidr('close');
},
wipeRight: function() {
$.sidr('open');
},
preventDefaultEvents: false
});
~~~
就變成了我們需要的代碼。。
~~~
define([
'jquery',
'underscore',
'backbone',
'router',
'jquerySidr',
'touchwipe'
], function($, _, Backbone, Router){
var initialize = function(){
$(window).touchwipe({
wipeLeft: function() {
$.sidr('close');
},
wipeRight: function() {
$.sidr('open');
},
preventDefaultEvents: false
});
$(document).ready(function() {
$('#sidr').show();
$('#menu').sidr();
$("#sidr li a" ).bind('touchstart click', function() {
if(null != Backbone.history.fragment){
_.each($("#sidr li"),function(li){
$(li).removeClass()
});
$('a[href$="#/'+Backbone.history.fragment+'"]').parent().addClass("active");
$.sidr('close');
window.scrollTo(0,0);
}
});
});
Router.initialize();
};
return {
initialize: initialize
};
});
~~~
便可以實現我們需要的
* 當用戶向右滑動的時候,菜單應該展開
* 當用戶向左滑動的時候,菜單應該關閉