如今,咱們常常均可以看到複雜的JavaScript應用程序,因爲這些應用程序變得愈來愈複雜,一長串的jQuery回調語句或者經過應用程序在各個狀態執行不一樣的函數調用,這些作法都會變得沒法再讓人接受,這致使了JavaScript開發人員開始尋找一種組織和效率更優秀的開發方式。javascript
實現組織和效率的其中一個最經常使用的架構模式,就是咱們熟知的Model View Controller (MVC)模式,這種模式鼓勵開發人員將其應用程序的不一樣部分分割爲更易於管理的模塊,咱們沒必要使用一個函數直接調用數據庫,經過建立了一個Model(模型或實體)來管理數據庫;經過模板(Template)或視圖(View)來簡化顯示代碼; 最後,經過使用控制器(Controller)來處理咱們的應用程序的請求,MVC模式儘可能下降每一個模塊之間的耦合度,提供程序的開發效率。php
咱們熟知的Javascript MVC框架有:Ember.js、Backbone.js、Knockout.js、Spine.js、Batman.js 和 Angular.js等。css
圖1 Javascript MVC frameworkhtml
經過上圖,咱們咱們能夠清楚地瞭解Javascript MVC框架之間的特性,複雜度和學習曲線的區別,從左到右咱們瞭解到各個Javascript MVC框架是否支持數據綁定(Data Binding)、模板(Templating)和持久化等特性,從下到上MVC框架的複雜性遞增,說實話我並無去對比每一個框架之間的優劣,若是你們有作過相關的對比或看過有關的文章也不吝賜教。html5
在接下來的博文中,咱們將介紹Ember.js的使用。java
Ember.js是一個JavaScript的MVC框架,它由Apple前僱員建立的SproutCore 2.0更名進化而來,Ember已經發布到1.0.0-RC.3。jquery
在介紹Ember以前,首先讓咱們回顧一下MVC模式,下面咱們講經過一個例子介紹MVC模式在程序設計中的做用,例如:git
1. 用戶執行一個操做,好比敲擊鍵盤或單擊鼠標按鈕。github
2. 控制器(Controller)接收輸入並觸發一個消息給模型(Model)。ajax
3. 模型根據消息修改其內容(CRUD操做)。
4. 視圖(View)監視模型中的變動,並將相應地更新呈現到用戶界面中。
經過上面,咱們瞭解到MVC中各個部件之間的做用和聯繫,在瞭解 MVC 模式的工做方式後,咱們能夠更加明確是否須要在咱們的項目中引入Javascript的MVC框架。
在構建Ember應用程序時,咱們會使用到六個主要部件:應用程序(Application)、模型(Model)、視圖(View)、模板(Template)、路由(Routing)和控制器(Controller)。
接下來,咱們將經過實現一個具體的程序來介紹Ember的使用。
首先,咱們須要引用一系列Javascript庫,因此咱們在程序中添加js文件,而且把如下js文件保存到該文件夾中:
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程序,可是它尚未具體功能,接下來,咱們將給程序添加功能。
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頁面並無顯示模板中的信息。
這是因爲咱們尚未定義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}}中的內容該如何加載顯示呢?
因爲{{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版本信息
模型是一個用來表示應用程序數據的對象,它多是一個簡單的數組或經過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程序的基本功能了,提供用戶查下員工的信息了。
本文經過Demo例子介紹了Ember的使用,主要介紹了Ember的模型,控制器、模板和路由,因爲Ember是Javascript MVC框架,並且做爲初學者很容易困惑於它的自動生成和默認規則,因此我極力推薦你們要仔細看一遍Routing和Controller的官方文檔。
咱們經過介紹Ember的Handlerbars模板引擎,定義了Demo程序的頁面,而後經過路由控制器定義路由行爲,根據路由行爲選擇控制器,控制器負責數據加載和顯示。但咱們的例子中尚未設計的Ember視圖模塊,若是想進一步學習請參考官方文檔或書籍。