HTML5
撰寫商業導向的 JavaScript Web 應用程式
Frank Prößdorf
Microsoft 將許多的精力放入索引鍵升級 HTML5 JavaScript,對於 Windows 開發人員,而且會有相當多的高品質的程式庫和架構可用來建置實際執行的應用程式。在本文中我們將建立基本商務導向應用程式,以做為起點的深入更深入的現有的選項,並可讓您體驗 JavaScript 可以是很有趣的程式碼品質。
將 catalogue 產品應用程式,並將其分割成類別,並建立、 更新和刪除 (CRUD),如所示產品與類別圖 1。除了這些典型的 CRUD 作業,應用程式會處理其他一般工作: 國際化、 驗證的應用程式的輸入和鍵盤的控制項。其中一個最重要層面的應用程式是為了要讓離線編輯它會使用 HTML5 區域儲存區。我們不會進入這個這裡,所有的詳細資料,但您可以在這個範例應用程式找到完整的程式碼 codeplex。
圖 1] 樣式的 [概觀] 清單
快速入門
請撰寫商務導向 JavaScript 應用程式的相關呢?信譽良好的一種方法是使用模型-檢視-控制器 (MVC) 結構。這有已成功地採用像 [注音標示上滑軌、 Django 或 ASP 架構中。NET MVC。MVC 可以嚴格的結構和分離應用程式,例如檢視和商務邏輯的問題。這是 JavaScript 特別重要,因為它是很容易撰寫混淆的程式碼,在同一個地方的一切動作。我們通常需要提醒自己,若要這樣做,請不和,試著撰寫簡潔、 容易閱讀且可重複使用的程式碼。有幾種架構,最值得注意的是針對 「 MVC 結構中, Backbone.js, Eyeballs.js 和 Sammy.js。
這個範例應用程式中,我們在主要是因為我們已經知道,但是也很小,經過良好撰寫的因為測試,我們要開始的所有項目並不會使用 Sammy.js,它。它並不提供您具有隱含 MVC 結構的結構,但它可讓您輕鬆地建置其基底。只有目前具有的相依性是 jQuery ,這是我們無論如何都要使用 DOM 操作的程式庫。我開始使用的目錄結構看起來像這樣:
- public
- js
app.js
+ controllers
+ models
+ helpers
+ views
+ templates
- vendor
- sammy
sammy.js
- jquery
jquery.js
我們將可能會呈現執行中的 [範本] 目錄中,JavaScript 程式碼的所有範本檔案和所有的 JavaScript 程式碼相關的呈現這些檢視目錄中的範本。
應用程式檔案
我們建立實際的 Sammy.js app.js—here 控制站會載入和其路由中的應用程式會初始化 (請參閱圖 2)。 我們傾向將命名空間的所有變數 (控制站、 模型中,等) 建立。 在此情況下,我們選擇呼叫這個命名空間的 karhu,我們要分類的產品的公司名稱。
圖 2 Karhu.app
karhu.app = $.sammy(function() {
this.element_selector = '#main';
this.use(Sammy.Mustache, 'mustache');
this.use(Sammy.NestedParams);
this.use(Sammy.JSON);
this.helpers(karhu.ApplicationHelper);
this.helpers({ store: karhu.config.store });
karhu.Products(this);
});
$(function() {
karhu.app.run('#/products');
});
第一步是載入外掛? 式,例如 Mustache,這是範本呈現引擎。 然後您可以初始化協助程式 (karhu。ApplicationHelper) 和控制站 (karhu。產品)。 一旦應用程式定義,並載入所有的 DOM 項目,您可以執行應用程式,並將它導向至初始的路由,所有產品的都索引。
寫入測試
先告訴您,如何運作的產品控制站,並顯示所有的產品,我們要簡短進入如何 JavaScript 應用程式的品質可以大幅增加透過測試。 當我們瀏有關開發範例應用程式,每個主要的步驟之前先撰寫以接受性測試,以確保會實際執行程式碼。 如此將造成迴歸測試,確保正確地寫入之前也靜態函式的所有項目。 更複雜的程式碼,我們要撰寫單元測試,然後試著涵蓋大部分的情況下,執行程式碼時,可能會發生。 撰寫接受度測試最簡單且最可讀取的方法之一是使用 Capybara 與 Selenium,不過一次遠端控制的瀏覽器喜歡 PhantomJS 是為 Capybara 驅動程式可用,則可能較適合使用的 Selenium,而不是因為它們是快速許多。
第一個案例 (圖 3),讓我們來測試是否我們會看到一份產品清單。
圖 3 遭到測試案例
Feature: Products
In order to know which products I have
As a user
I want to see a list of products
Scenario: list products
Given a category "Trees" with the description "Plants"
And a product "Oak" with the description "Brown"
and the price "232.00€"
that is valid to "12/20/2027"
and belongs to the category "Trees"
And a product "Birch" with the description "White"
and the price "115.75€"
that is valid to "03/01/2019"
and belongs to the category "Trees"
When I go to the start page
Then I should see "Trees"
And I should see "Oak"
And I should see "Brown"
And I should see "232.00€"
And I should see "12/20/2027"
And I should see "Birch"
單元測試,有許多不同的可能性。 我們用來處理 jspec,因為它很類似 [注音標示的 rspec,我們曾使用過的。 現在已過時之喜好設定 jspec Jasmine,所以我們會此處使用。 它相當不錯,並且包含耙工作,以輕鬆地與接受度測試一起執行。 以下是其中一個範例應用程式的單元測試外觀如下:
describe("Product", function() {
describe("attachCategory", function() {
it("should assign itself its category", function() {
var categories = [{id: 1, name: 'Papiere'}, {id: 2, name: 'Baeume'}];
var attributes = {id: 1, name: 'Fichte', category_id: 2};
var product = new karhu.Product(attributes, categories);
expect(product.category.
name).toEqual('Baeume');
});
});
});
定義控制站
一旦我們完成案例我們一開始寫入的控制站,也就是非常簡單第一次:
karhu.Products = function(app) {
app.get('#/products', function(context) {
context.get('/categories', {}, function(categories) {
context.get('/products', {}, function(products) {
products = products.map(function(product) { return new karhu.Product(
product, categories); });
context.partial('templates/products/index.mustache', {products: products});
});
});
});
};
此時只有一個定義的路由,這是 # / 產品路由上的取得。一旦在 URL 中的位置雜湊會變更為 /products,將會執行回呼。 因此如果您將路徑附加至您的 URL (例如 http://localhost:4567/index.html#/products) 時,便會執行附加的回呼。 相同會發生應用程式第一次啟動時,因為在 app.js 中我們定義初始路徑指向相同的路由。
路由中擷取的類別及產品,透過協助程式,不要只是基本艾傑克斯 GET 要求為我們後端。 一旦擷取這些資料,我們會將它對應至 JavaScript 物件,然後呈現 index.mustache 範本內的那些物件。 這將會呈現在 < div id ="main"> HTML 標記,定義為根 element_selector,app.js 檔案中。
定義的模型
我們需要 JavaScript 物件對應的資料,因此我們可以與它們屬於和呈現類別旁邊的產品,看起來像這樣的名稱類別產生關聯的產品:
karhu.Product = function(attributes, categories) {
_.extend(this, attributes);
attachCategory(this, categories);
function attachCategory(product, categories) {
product.category = _.find(categories, function(category) {
return parseInt(category.id, 10) === parseInt(product.category_id, 10);
});
}
};
我們將延伸的產品的所有屬性的物件,然後我們將產品的類別附加到物件。我們的規則,使其私用函式的終止內的 attachCategory。請注意這段程式碼中使用底線函式,所提供的 Underscore.js。此文件庫定義為 enumerables,協助程式,並可協助您撰寫容易閱讀且簡潔的程式碼。
圖 4 模型會顯示給使用者。
圖 4,與在 Web 應用程式中的 [產品] 模型互動
呈現範本
剛剛展示的模型,我們不需要額外的檢視圖層物件因為之轉譯邏輯是很基本 — 它只會逐一查看產品我們所建立的物件,並顯示每項目,包括我們事先附加的類別名稱的屬性。將呈現邏輯釋放 mustache 範本看起來就像在顯示的內容圖 5。
[圖 5 Mustache 範本
<h2>Products</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>Valid To</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{{#products}}
<tr>
<td>{{name}}</td>
<td>{{description}}</td>
<td>{{unit_price}}</td>
<td>{{valid_to}}</td>
<td>{{#category}}{{name}}{{/category}}</td>
</tr>
{{/products}}
</tbody>
</table>
呈現的輸出會顯示在圖 6。
圖 6 呈現 HTML 輸出從 Mustache 範本
移動至模型的模型特定控制站的程式碼
它是一種喜好提供多少責任的控制站] 和 [至模型的程式碼重構多少可以。如果我想要撰寫的程式碼圖 5 多模型為主的方式,我可以做像圖 7。
[圖 7 多個模型為主的方式
控制者 (Controller)
karhu.Products = function(app) {
app.get('#/products', function(context) {
karhu.Product.all(function(products) {
context.partial('templates/products/index.mustache', {products: products});
});
});
};
機型
karhu.Product.all = function(callback) {
karhu.backend.get('/categories', {}, function(categories) {
karhu.backend.get('/products', function(products) {
products = products.map(function(product) { return new karhu.Product(product, categories); });
callback(products);
});
});
};
標準的工作
有數種極為常見的 Web 開發和處理 JavaScript 應用程式時,將重複的工作。 我們要說明我們如何處理這些工作,以及解決我們遇到的問題。 像往常一樣,有數種不同的方式,方法有問題。
驗證大部分的應用程式,包括此範本,藉由讓使用者登入加入基本的安全性。 由於 HTTP 是沒有狀態,您必須重新傳送每個要求的驗證。 您可以處理這個儲存語彙基元,當使用者第一次登入,然後在此之後的每個要求使用該語彙基元。 我的選擇是語彙基元儲存在本機存放區中,使用者必須成功,登入後,再傳送該語彙基元附加到 XMLHttpRequest 的標頭。 若要執行這項操作的程式碼所示圖 8。 這段程式碼被 stashed 後端模型將協助我們先前所述的程式使用中。
圖 8 儲存的語彙基元,當使用者登入
this.get = function(url, data, success, error) {
sendRequest('get', url, data, success, error);
};
function authenticate(xhr) {
var token = '';
if(karhu.token) {
token = karhu.token;
} else if(karhu.user && karhu.password) {
token = SHA256(karhu.user + karhu.password);
}
karhu.token = token;
xhr.setRequestHeader("X-Karhu-Authentication", 'user="' + karhu.user + '", token="' + karhu.token + '"');
};
function sendRequest(verb, url, data, success, error) {
$.ajax({
url: url,
data: data,
type: verb,
beforeSend: function(xhr) {
authenticate(xhr);
},
success: function(result) {
success(result);
}
});
}
圖 9 會顯示已儲存的使用者語彙基元。
圖 9 X-Karhu-驗證包含在 HTTP 要求
如果使用者只需登入,您必須有使用者名稱及密碼可以使用。如果使用者登入之前,您必須已儲存的語彙基元。不管怎麼說,附加為頁首語彙基元或使用者/密碼組合,並要求成功時,如果您知道在成功驗證使用者。否則後端就只會傳回錯誤。這種方法是相當直接的實作,唯一我們遇到的問題的程式碼變得有點複雜,無法讀取。若要修正這個問題,我們將協助程式重構到不同的模型中。到後端模型抽象化的要求是很常見的做法,作為,例如,使用 Backbone.js 程式庫所在的程式庫的核心部分。驗證程式碼通常是唯一的每個應用程式,它一定會相依於後端和它所預期的前端來傳送。
國際化 (I18n) 國際化是常見的工作,Web 應用程式,以及 jquery.global.js 通常用來完成它 JavaScript 應用程式中。此程式庫提供方法來格式化數字和日期,並可讓您轉換目前的地區設定使用字典的字串。一旦載入此字典,也就是一個簡單的 JavaScript 物件以索引鍵和已轉換的值,的唯一要特別注意您必須數字和日期格式設定。合理的位置,若要這樣做是模型之前,呈現範本物件。產品模型中,它會看起來像這樣:
var valid_to = Date.parse(product.valid_to);
product.valid_to = $.global.format(valid_to, "d");
圖 10 示範德文作為顯示語言。
圖 10] 切換為德文的語言
驗證的 JavaScript 中開發的優點之一是您可以提供使用者即時回應。它可以使用這項潛能驗證的資料傳送到後端之前。請注意您仍然需要確認後端中的資料,因為可能有不使用前端的要求。JQuery 程式庫 jquery.validate.js 通常用於驗證。它提供在表單上的一組規則,並在適當的輸入欄位上顯示錯誤,如果內容不符合規則。合理到模型建構的驗證規則我們已經有,因此,每個模型有一些驗證函式傳回的規則。以下是我們類別模型的驗證可能的外觀:
karhu.Category = function() {
this.validations = function() {
return {
rules: {
'category[name]': {
required: true,
maxlength: 100
}
}
};
};
};
圖 11 顯示可能會顯示錯誤的方式。
建立新的類別上的 [圖 11 A 驗證錯誤
驗證會進一步。瀏覽離開未提交的表單被禁止的。使用者必須實際送出有效的資料或主動取消 [資料輸入 (請參閱圖 12)。
圖 12 紅色的快閃通知會警告使用者的 Unsubmitted 表單資料
離線編輯的快取物件有時候使用者需要離線工作。這允許屬於最中央式且複雜的應用程式。需要進行快取之前的時間],使應用程式離線之後它們可以正確地排序、 分頁和篩選所有的物件。需要有所有的動作發生之前的物件會快取,, 以便這些動作可套用至物件,只要它們快取處理的佇列。也需要有一旦我們實際上已離線,,如此當我們回到線上時,離線完成的所有項目可以透過到後端填滿的第二個佇列。圖 13 示範應用程式離線。
圖 13 當離線時的應用程式回應
有幾個需要複雜已經快取和佇列處理程序除了解決的問題。例如,當離線時建立物件時,無法更新或刪除沒有進一步的程式碼,因為它沒有 id。我們工作周圍,現在只不允許建立物件的這些動作而離線。為這個相同的原因,建立類別時離線無法用來建立產品。我只是不會顯示那些類別中可用的類別建立產品的清單。藉由使用暫存 id 以及重新排列離線的佇列,則可能會解決這類問題。
此外,使用 partials 和範本需要快取中。這可以透過快取資訊清單所定義在 HTML5 如果目標瀏覽器群組支援,或只是透過載入 partials,並將它們放入區域儲存區。這是相當簡單並加上 Sammy.js,看起來像這樣:
context.load('templates/products/index.mustache', {cache: true});
Windows 的整合
Internet Explorer 9 是最適合用於執行 HTML5 應用程式。此外,讓 Web 應用程式能夠原生整合 Windows 7 的工作列,加強應用程式的情況下,若要顯示通知、 整合功能,並提供跳躍清單的支援。跳躍清單的整合很簡單,在最簡單的表單只需中繼標籤屬性的宣告。這是完全 Karhu,可以方便使用者存取他們的需求所用的方法。您可以直接使用跳躍清單任務移到檢視中新增 [產品]、 [加入類別、 產品概觀和類別概觀 (請參閱圖 14)。下列程式碼會示範如何宣告簡單的跳躍清單的工作:
<meta name="msapplication-task"
content="name=Products;
action-uri=#/products;
icon-uri=images/karhu.ico" />
圖 14 跳躍清單任務
您可以瞭解所有固定和在 Windows 7 整合建置我的釘選站台,其中有其他的 Web 應用程式,例如瀏覽器通知和動態使用 JavaScript 的 jumplists 的想法。很輕鬆地將更多的功能加入至您的 Web 應用程式,有一點額外的工作。取得更了解主題的其他開始點是 MSDN JavaScript 語言參考和上述的程式庫與架構的文件。
完成
此時,我們已實作的所有基本需求的範例應用程式。具有足夠的時間,我們可能也注意先前所述的問題。工作,例如驗證、 國際化以及處理商務邏輯必須加以編碼,不受影響的架構和程式庫,也就是實際上只是起始點。
如果您永遠撰寫測試,並請注意應用程式結構撰寫實際執行 JavaScript 應用程式可以繼續發展是,依我們之見,唯一可能但值得投資的目標。快速入門很簡單,但請務必保留簡潔的程式碼基底的檢查,必要時。如果符合這些需求,JavaScript 會讓您有機會撰寫非常優雅且易於維護的應用程式。
Frank Prößdorf 是自由的 Web 開發人員和 co-founder 的 NotJustHosting 者愛上使用 (並列文字) 和 JavaScript。發現他的熱情,玩新技術。他定期支援開放原始碼軟體,他是個興奮參與及共用程式碼] 及 [想法] 的機會。除了他的工作,他喜歡旅行、 sailing 和播放網球。透過他認識 Prößdorf github 設定檔或他網站。
Dariusz Parys 是在 Microsoft 德國,開發人員編輯執行 Web 程式開發,以著重於新的技術。目前,他撰寫許多 JavaScript 和 HTML5。您可以透過他的部落格連絡 Parys downtocode.net
感謝給下列技術專家來檢閱這份文件: Aaron Quint