標籤:des style blog http java 使用 io 檔案
現在,我們經常都可以看到複雜的JavaScript應用程式,由於這些應用程式變得越來越複雜,一長串的jQuery回調語句或者通過應用程式在各個狀態執行不同的函數調用,這些做法都會變得無法再讓人接受,這導致了JavaScript開發人員開始尋找一種組織和效率更優秀的開發方式。
實現組織和效率的其中一個最常用的架構模式,就是我們熟知的Model View Controller (MVC)模式,這種模式鼓勵開發人員將其應用程式的不同部分分割為更易於管理的模組,我們不必使用一個函數直接調用資料庫,通過建立了一個Model(模型或實體)來管理資料庫;通過模板(Template)或視圖(View)來簡化顯示代碼; 最後,通過使用控制器(Controller)來處理我們的應用程式的請求,MVC模式盡量降低每個模組之間的耦合度,提供者的開發效率。
我們熟知的Javascript MVC架構有:Ember.js、Backbone.js、Knockout.js、Spine.js、Batman.js 和 Angular.js等。
圖1 Javascript MVC framework
通過,我們我們可以清楚地瞭解Javascript MVC架構之間的特性,複雜度和學習曲線的區別,從左至右我們瞭解到各個Javascript MVC架構是否支援資料繫結(Data Binding)、模板(Templating)和持久化等特性,從下到上MVC架構的複雜性遞增,說實話我並沒有去對比每個架構之間的優劣,如果大家有做過相關的對比或看過有關的文章也不吝賜教。
在接下來的博文中,我們將介紹Ember.js的使用。
目錄
- MVC定義
- 設定Ember
- 模板(Handlebars)
- 應用程式(Application)
- 路由(Routing)
- 模型(Model)
1.1.2 本文
Ember.js是一個JavaScript的MVC架構,它由Apple前僱員建立的SproutCore 2.0改名進化而來,Ember已經發布到1.0.0-RC.3。
MVC定義
在介紹Ember之前,首先讓我們回顧一下MVC模式,下面我們講通過一個例子介紹MVC模式在程式設計中的作用,例如:
1. 使用者執行一個操作,比如敲擊鍵盤或單擊滑鼠按鍵。
2. 控制器(Controller)接收輸入並觸發一個訊息給模型(Model)。
3. 模型根據訊息修改其內容(CRUD操作)。
4. 視圖(View)監視模型中的變更,並將相應地更新呈現到使用者介面中。
通過上面,我們瞭解到MVC中各個組件之間的作用和聯絡,在瞭解 MVC 模式的工作方式後,我們可以更加明確是否需要在我們的項目中引入Javascript的MVC架構。
在構建Ember應用程式時,我們會使用到六個主要組件:應用程式(Application)、模型(Model)、視圖(View)、模板(Template)、路由(Routing)和控制器(Controller)。
接下來,我們將通過實現一個具體的程式來介紹Ember的使用。
設定Ember
首先,我們需要引用一系列Javascript庫,所以我們在程式中添加js檔案,並且把以下js檔案儲存到該檔案夾中:
ember.js: ember 1.0.0-rc.3
ember-data.js: revision 12
handlebars.js: handlebars 1.2.rc.3
jquery.js: jQuery 1.9.1
app.js: 我們的應用程代碼
上面,我們把一系列的js檔案儲存到了本地中,當然我們也可以通過使用CDN(內容分髮網絡)來擷取相應Javascript庫,接下來,讓我們建立index.html頁面。
<!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="description" content="" /> <meta name="description" content="" /> <meta name="keywords" content="" /> <meta name="author" content="" /> <title></title> <link rel="stylesheet" href="" type="text/css" /> <link rel="stylesheet" href="" type="text/css" /></head><body> <!-- Add Javascript libs Reference --> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script src="js/libs/handlebars-1.0.0-rc.3.js"></script> <script src="js/libs/ember-1.0.0-rc.2.js"></script> <script src="js/libs/ember-data.js"></script> <script src="js/app.js"></script></body></html>
現在,我們已經實現了第一個Ember程式,但是它還沒有具體功能,接下來,我們將給程式添加功能。
模板(Handlebars)
Ember.js使用的是Handlebars模板引擎,在我們開始使用之前,首先讓我們先簡單介紹一下handlebars.js;如果大家有使用過jQuery模板或其他指令碼模板,那麼對於掌握handlebars.js的使用就沒有太大的困難了,如果確實沒有使用過也不用擔心,因為handlebars.js的使用也是挺簡單的。
它讓開發人員可以混合原始HTML和Handlebars運算式產生渲染相應的HTML;運算式以包括在{{}}中,我們可以通過兩種方法把Handlebars模板載入到頁面中,我們可以直接內嵌的html頁面中,通過在頁面中添加類型為text/x-handlebars的指令碼標記內;或儲存到以handlebars或hbs為尾碼的檔案中,然後通過Ember.js載入到頁面中。
為了簡單起見,我們把Handlebars指令碼直接嵌入到index.html頁面中。
<script type="text/x-handlebars" data-template-name="application"> <h1>Employee System</h1> {{outlet}}</script>
上面,我們定義了模板application,並且添加了Handlebars運算式{{outlet}},它的作用就類似一個預留位置,告訴Ember這裡的內容要動態地載入到頁面當中,當我們在瀏覽器中開啟index頁面並沒有顯示模板中的資訊。
應用程式(Application)
這是由於我們還沒有定義Ember程式,每個Ember應用程式都需要一個Ember應用程式執行個體,接下來讓我們在app.js中建立第一個Ember應用程式執行個體吧!
首先,我們建立一個Ember應用程式執行個體,具體實現如下:
// Creates an application instance.App = Ember.Application.create();
上面,我們定義了一個名為 App 的Ember應用程式,當然我們可以把程式命名為任意的,但有一點我們要注意的是Ember要求變數的名稱都以大寫字母開頭。
現在,我們在瀏覽器中開啟頁面,可以顯示模板載入的資訊了。
圖2 Index頁面
也許有人會問Ember怎麼知道哪些模板需要載入呢?更重要的一點是,我們並沒有告訴Ember要載入的模板名稱,我們只是直接把模板application嵌入到頁面中。
其實,這裡有個“潛規則”:如果我們沒有定義ApplicationView(應用程式視圖),那麼Ember會自動產生一個ApplicationView並且預設載入名為application的模板,假設,我們把模板重新命名為application1,那麼預設的ApplicationView將找不到要載入的模板。
當然,我們也可以通過定義ApplicationView來指定需要載入的模板名稱,具體實現如
// Defines an application view, then loading// relative templates.App.ApplicationView = Ember.View.extend({ templateName: ‘application1‘});
現在,我們還有一個疑問就是運算式{{outlet}}中的內容該如何載入顯示呢?
路由(Routing)
由於{{outlet}}的內容是根據路由選擇後動態擷取的模板內容,所以我們先介紹Ember程式的路由,它可以協助管理應該程式的狀態和使用者導航所需資源的資源;當我們的應用程式啟動時,路由是負責顯示模板,載入資料,以及管理應用程式的狀態。
現在,我們通過指定URL方式義來定義應用程式的路由,具體定義如下:
// Defines a goal routing home and// the detail information of employee routing.App.Router.map(function() { this.route("home", {path: "/"}); this.route("employee", {path: "/employee/:employee_id"});});
上面,我們定義了兩個路由分別是:應用程式的全域路由home和employee,在index頁面進行載入同時訪問home路由的模板,資料和應用程式狀態;而employee路由將根據employee_id訪問每個一個員工的基本資料。
接下來,我們定義home模板,具體實現如下:
<script type="text/x-handlebars" data-template-name="home"> <h3>Employee Information</h3> <ul> {{#each item in employeeInfo}} <li>item</li> {{each}} </ul></script>
上面,我們定義了home模板,並且使用了each表達來迭代訪問employeeInfo對象中的元素,這時我們又有一個疑問了,那就是employeeInfo對象從哪裡擷取呢?
前面,我們提到Controller負責從Model中擷取資料,然後通過模板載入顯示,那麼我們可以通過顯市定義Controller來擷取資料,如果我們不定義的話,Ember會自動產生一個HomeController。
// Defines a custom controll. App.HomeController = Ember.Controller.extend({ employeeInfo: [‘Jackson Huang‘, ‘Ada Li‘, ‘JK Rush‘] });
上面,我們自訂了HomeController並且初始化了employeeInfo數組,現在我們重新整理一下index頁面。
圖3 Index頁面
現在,我們又有一個疑問了,假如,我們程式有很多資源要訪問,那麼我們是否都顯式地定義Controller呢?
其實,我們還可以通過定義路由控制器實現自動選擇控制器,而且Ember會自動產生相應的Controller無需我們編寫任何代碼,具體實現如下:
// Defines a routing handler. App.HomeRoute = Ember.Route.extend({ model: function(){ return [‘Jackson Huang‘, ‘Ada Li‘, ‘JK Rush‘]; }, setupController: function(controller, model){ controller.set(‘content‘, model) }});
現在,我們定義了路由控制器App.HomeRoute並且重寫了方法setupController,它接收路由處理常式匹配的控制器作為第一個參數即HomeController,接著我們給HomeController傳遞model參數,那麼HomeController就可以擷取相應的資料並且載入到模板中顯示了。
上面,我們成功把資料載入到頁面中,但是資料都是直接hardcode在Controller中,我們並沒有定義Model來擷取資料。
接下來,我們將實現從Fixtures中擷取資料,這時我們需要使用ember-data.js庫,具體實現如下。
// Customs a store.App.Store = DS.Store.extend({ // Notify the version of ember data api used. revision: 12, // Used FixtureAdapter. adapter: ‘DS.FixtureAdapter‘});
上面,我們在app.js中定義DS.Store的子類App.Store,並且申明我們程式使用Ember data api的版本是12,當api版本更新或使用的版本太舊時,ember-data.js就會返回相應的錯誤資訊。
例如:當前的ember-data.js版本是12,如果我們在app.js中定義使用的是版本1的api,在控制台中我們就會看到以下的錯誤資訊。
圖4 ember-data.js版本資訊
模型(Model)
模型是一個用來表示應用程式資料的對象,它可能是一個簡單的數組或通過RESTful API動態檢索的資料;ember-data.js提供載入、映射和更新應用程式模型的API。
ember-data.js為每個應用程式都提供儲存空間,儲存空間負責保持已載入的Model和檢索還未載入的Model。
前面,我們定義了應用程式App,現在,需要給程式提供資料也就是員工資訊,所以我們要建立程式的模型(實體)Employee,接下來我們將實現模型的定義。
// Defines a employee model.App.Employee = DS.Model.extend({ name: DS.attr(‘string‘), department: DS.attr(‘string‘), title: DS.attr(‘string‘)})
上面,我們定義了Employee模型,它繼承了DS.Model並且包含三個欄位分別是name,department和title。
接下來,我們通過定義App.Employee.FIXTURES,類比從伺服器端擷取資料。
// Defines a JSON array.App.Employee.FIXTURES = [{ id: 1, name: ‘Jackson Huang‘, department: ‘IT‘, title: ‘programmer‘},{ id: 2, name: ‘Ada Chen‘, department: ‘purchasing‘, title: ‘buyer‘},{ id: 3, name: ‘JK Rush‘, department: ‘IT‘, title: ‘programmer‘},{ id: 4, name: ‘Lucy Liu‘, department: ‘IT‘, title: ‘tester‘},{ id: 5, name: ‘Julia Liu‘, department: ‘HR‘, title: ‘Manager‘}];
上面,我們定義了JSON數組App.Employee.FIXTURES,它包含了一系列員工的基本資料。
接下來,我們修改home和添加employee模板,具體實現如下:
<!-- Home temp START --><script type="text/x-handlebars" data-template-name="home"> <h3> Employee Information</h3> <ul> {{#each item in content}} <li>{{item}}</li> {{/each}} </ul> <h3> Employee</h3> <ul> {{#each employee in employees}} {{#linkTo "employee" employee}} <p> {{employee.name}} </p> {{/linkTo}} {{/each}} </ul></script><!-- Home temp END --><!-- Employee temp START --><script type="text/x-handlebars" data-template-name="employee"> <div> <h3> Name: {{name}}</h3> <p> Department: {{department}} </p> <p> Title: {{title}} </p> {{#linkTo home class=‘btn btn-primary‘}}Back{{/linkTo}} </div></script><!-- Employee temp END -->
在home模板中,我們添加each運算式迭代訪問employee元素,然後通過linkTo選擇employee路由;然後根據路由選擇在employee模板顯示相應的員工資訊。
圖5 程式頁面
現在,我們完成了Employee程式的準系統了,提供使用者查下員工的資訊了。
1.1.3 總結
本文通過Demo例子介紹了Ember的使用,主要介紹了Ember的模型,控制器、模板和路由,由於Ember是Javascript MVC架構,而且作為初學者很容易困惑於它的自動產生和預設規則,所以我極力推薦大家要仔細看一遍Routing和Controller的官方文檔。
我們通過介紹Ember的Handlerbars模板引擎,定義了Demo程式的頁面,然後通過路由控制器定義路由行為,根據路由行為選擇控制器,控制器負責資料載入和顯示。但我們的例子中還沒有設計的Ember視圖模組,如果想進一步學習請參考官方文檔或書籍。
參考
- http://www.adobe.com/cn/devnet/html5/articles/flame-on-a-beginners-guide-to-emberjs.html
- http://net.tutsplus.com/tutorials/javascript-ajax/getting-into-ember-js/?search_index=3
- http://emberjs.com/guides/
- http://xbingoz.com/emberguides/0.php
- http://andymatthews.net/read/2012/03/07/Getting-Started-With-EmberJS
http://www.cnblogs.com/rush/archive/2013/04/29/3051191.html