Ember.js的一些學習總結(jié)
現(xiàn)在,我們經(jīng)常都可以看到復雜的JavaScript應用程序,由于這些應用程序變得越來越復雜,一長串的jQuery回調(diào)語句或者通過應用程序在 各個狀態(tài)執(zhí)行不同的函數(shù)調(diào)用,這些做法都會變得無法再讓人接受,這導致了JavaScript開發(fā)人員開始尋找一種組織和效率更優(yōu)秀的開發(fā)方式。
實現(xiàn)組織和效率的其中一個最常用的架構模式,就是我們熟知的Model View Controller (MVC)模式,這種模式鼓勵開發(fā)人員將其應用程序的不同部分分割為更易于管理的模塊,我們不必使用一個函數(shù)直接調(diào)用數(shù)據(jù)庫,通過創(chuàng)建了一個 Model(模型或?qū)嶓w)來管理數(shù)據(jù)庫;通過模板(Template)或視圖(View)來簡化顯示代碼; ***,通過使用控制器(Controller)來處理我們的應用程序的請求,MVC模式盡量降低每個模塊之間的耦合度,提供程序的開發(fā)效率。
我們熟知的Javascript MVC框架有:Ember.js、Backbone.js、Knockout.js、Spine.js、Batman.js 和 Angular.js等。
圖1 Javascript MVC framework
通過上圖,我們我們可以清楚地了解Javascript MVC框架之間的特性,復雜度和學習曲線的區(qū)別,從左到右我們了解到各個Javascript MVC框架是否支持數(shù)據(jù)綁定(Data Binding)、模板(Templating)和持久化等特性,從下到上MVC框架的復雜性遞增,說實話我并沒有去對比每個框架之間的優(yōu)劣,如果大家有 做過相關的對比或看過有關的文章也不吝賜教。
在接下來的博文中,我們將介紹Ember.js的使用。
1.1.2 正文
Ember.js是一個JavaScript的MVC框架,它由Apple前雇員創(chuàng)建的SproutCore 2.0改名進化而來,Ember已經(jīng)發(fā)布到1.0.0-RC.3。
MVC定義
在介紹Ember之前,首先讓我們回顧一下MVC模式,下面我們講通過一個例子介紹MVC模式在程序設計中的作用,例如:
1. 用戶執(zhí)行一個操作,比如敲擊鍵盤或單擊鼠標按鈕。
2. 控制器(Controller)接收輸入并觸發(fā)一個消息給模型(Model)。
3. 模型根據(jù)消息修改其內(nèi)容(CRUD操作)。
4. 視圖(View)監(jiān)視模型中的變更,并將相應地更新呈現(xiàn)到用戶界面中。
通過上面,我們了解到MVC中各個部件之間的作用和聯(lián)系,在了解 MVC 模式的工作方式后,我們可以更加明確是否需要在我們的項目中引入Javascript的MVC框架。
在構建Ember應用程序時,我們會使用到六個主要部件:應用程序(Application)、模型(Model)、視圖(View)、模板(Template)、路由(Routing)和控制器(Controller)。
接下來,我們將通過實現(xiàn)一個具體的程序來介紹Ember的使用。
設置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(內(nèi)容分發(fā)網(wǎng)絡)來獲取相應Javascript庫,接下來,讓我們創(chuàng)建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>
現(xiàn)在,我們已經(jīng)實現(xiàn)了***個Ember程序,但是它還沒有具體功能,接下來,我們將給程序添加功能。
#p#
模板(Handlebars)
Ember.js使用的是Handlebars模板引擎,在我們開始使用之前,首先讓我們先簡單介紹一下handlebars.js;如果大家有使 用過jQuery模板或其他腳本模板,那么對于掌握handlebars.js的使用就沒有太大的困難了,如果確實沒有使用過也不用擔心,因為 handlebars.js的使用也是挺簡單的。
它讓開發(fā)人員可以混合原始HTML和Handlebars表達式生成渲染相應的HTML;表達式以包括在{{}}中,我們可以通過兩種方法把 Handlebars模板加載到頁面中,我們可以直接內(nèi)嵌的html頁面中,通過在頁面中添加類型為text/x-handlebars的腳本標記內(nèi);或 保存到以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這里的內(nèi)容要動態(tài)地加載到頁面當中,當我們在瀏覽器中打開index頁面并沒有顯示模板中的信息。
應用程序(Application)
這是由于我們還沒有定義Ember程序,每個Ember應用程序都需要一個Ember應用程序?qū)嵗酉聛碜屛覀冊赼pp.js中創(chuàng)建***個Ember應用程序?qū)嵗桑?/p>
首先,我們創(chuàng)建一個Ember應用程序?qū)嵗唧w實現(xiàn)如下:
- // Creates an application instance.
- App = Ember.Application.create();
上面,我們定義了一個名為 App 的Ember應用程序,當然我們可以把程序命名為任意的,但有一點我們要注意的是Ember要求變量的名稱都以大寫字母開頭。
現(xiàn)在,我們在瀏覽器中打開頁面,可以顯示模板加載的信息了。
圖2 Index頁面
也許有人會問Ember怎么知道哪些模板需要加載呢?更重要的一點是,我們并沒有告訴Ember要加載的模板名稱,我們只是直接把模板application嵌入到頁面中。
其實,這里有個“潛規(guī)則”:如果我們沒有定義ApplicationView(應用程序視圖),那么Ember會自動生成一個 ApplicationView并且默認加載名為application的模板,假設,我們把模板重命名為application1,那么默認的 ApplicationView將找不到要加載的模板。
當然,我們也可以通過定義ApplicationView來指定需要加載的模板名稱,具體實現(xiàn)如
- // Defines an application view, then loading
- // relative templates.
- App.ApplicationView = Ember.View.extend({
- templateName: 'application1'
- });
現(xiàn)在,我們還有一個疑問就是表達式{{outlet}}中的內(nèi)容該如何加載顯示呢?
路由(Routing)
由于{{outlet}}的內(nèi)容是根據(jù)路由選擇后動態(tài)獲取的模板內(nèi)容,所以我們先介紹Ember程序的路由,它可以幫助管理應該程序的狀態(tài)和用戶導航所需資源的資源;當我們的應用程序啟動時,路由是負責顯示模板,加載數(shù)據(jù),以及管理應用程序的狀態(tài)。
現(xiàn)在,我們通過指定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路由的模板,數(shù)據(jù)和應用程序狀態(tài);而employee路由將根據(jù)employee_id訪問每個一個員工的基本信息。
接下來,我們定義home模板,具體實現(xiàn)如下:
- <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中獲取數(shù)據(jù),然后通過模板加載顯示,那么我們可以通過顯市定義Controller來獲取數(shù)據(jù),如果我們不定義的話,Ember會自動生成一個HomeController。
- // Defines a custom controll.
- App.HomeController = Ember.Controller.extend({
- employeeInfo: ['Jackson Huang', 'Ada Li', 'JK Rush']
- });
#p#
上面,我們自定義了HomeController并且初始化了employeeInfo數(shù)組,現(xiàn)在我們刷新一下index頁面。
圖3 Index頁面
現(xiàn)在,我們又有一個疑問了,假如,我們程序有很多資源要訪問,那么我們是否都顯式地定義Controller呢?
其實,我們還可以通過定義路由控制器實現(xiàn)自動選擇控制器,而且Ember會自動生成相應的Controller無需我們編寫任何代碼,具體實現(xiàn)如下:
- // 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)
- }
- });
現(xiàn)在,我們定義了路由控制器App.HomeRoute并且重寫了方法setupController,它接收路由處理程序匹配的控制器作為***個 參數(shù)即HomeController,接著我們給HomeController傳遞model參數(shù),那么HomeController就可以獲取相應的數(shù) 據(jù)并且加載到模板中顯示了。
上面,我們成功把數(shù)據(jù)加載到頁面中,但是數(shù)據(jù)都是直接hardcode在Controller中,我們并沒有定義Model來獲取數(shù)據(jù)。
接下來,我們將實現(xiàn)從Fixtures中獲取數(shù)據(jù),這時我們需要使用ember-data.js庫,具體實現(xiàn)如下。
- // 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)
模型是一個用來表示應用程序數(shù)據(jù)的對象,它可能是一個簡單的數(shù)組或通過RESTful API動態(tài)檢索的數(shù)據(jù);ember-data.js提供加載、映射和更新應用程序模型的API。
ember-data.js為每個應用程序都提供存儲空間,存儲空間負責保持已加載的Model和檢索還未加載的Model。
前面,我們定義了應用程序App,現(xiàn)在,需要給程序提供數(shù)據(jù)也就是員工信息,所以我們要創(chuàng)建程序的模型(實體)Employee,接下來我們將實現(xiàn)模型的定義。
- // 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,模擬從服務器端獲取數(shù)據(jù)。
- // 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'
- }
- ];
#p#
,我們定義了JSON數(shù)組App.Employee.FIXTURES,它包含了一系列員工的基本信息。
接下來,我們修改home和添加employee模板,具體實現(xiàn)如下:
- <!-- 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路由;然后根據(jù)路由選擇在employee模板顯示相應的員工信息。
圖5 程序頁面
現(xiàn)在,我們完成了Employee程序的基本功能了,提供用戶查下員工的信息了。
1.1.3 總結(jié)
本文通過Demo例子介紹了Ember的使用,主要介紹了Ember的模型,控制器、模板和路由,由于Ember是Javascript MVC框架,而且作為初學者很容易困惑于它的自動生成和默認規(guī)則,所以我極力推薦大家要仔細看一遍Routing和Controller的官方文檔。
我們通過介紹Ember的Handlerbars模板引擎,定義了Demo程序的頁面,然后通過路由控制器定義路由行為,根據(jù)路由行為選擇控制器,控制器負責數(shù)據(jù)加載和顯示。但我們的例子中還沒有設計的Ember視圖模塊,如果想進一步學習請參考官方文檔或書籍。
原文鏈接:http://www.cnblogs.com/rush/archive/2013/04/29/3051191.html