這篇文章會提供基本的「模型─視圖─控制器」(Model View Controller,MVC)軟體架構模式概念介紹,接著解釋熱門的 app 開發框架 Ember.js 是如何實做該架構的。

「模型─視圖─控制器」背後的理論

模型─視圖─控制器(MVC)是一種軟體架構模式,通常用於用戶界面(user interfaces)的實做:也因此。它成了建設 web apps 的熱門選擇。通常,它把程式的邏輯切分成三個部份,因而提昇了程式的模塊化(modularity)、也利於協作與重複利用(reuse)、還令程式更靈活、對迭代更友善(welcoming to iterations)。

要把它解釋清楚,讓我們想像一下購物清單的 app 吧。我們對這個清單的需求是:針對這個星期,每個要買的商品,列出它的名字、數量、和價格。接著我們將敘述如何透過 MVC 實做這些功能。

模型

模型決定了 app 該包含哪些資料。如果資料的狀態更動,模型通常會告知視圖,令顯示依需求更改;而如果需要透過不同的邏輯,來控制視圖更新的時候,模型也會告知控制器。

把重點拉回購物清單 app,模型會指定清單項目該包含哪些資料——例如項目價格之類的——以及哪些項目清單已經包含了。

視圖

視圖決定了 app 的資料該如何呈現。

以我們的購物清單 app 來說,視圖會決定清單該如何在用戶眼前呈現、還有接收來自模型的資料。

控制器

控制器包含了模型更新的邏輯,與(或),針對 app 用戶輸入的回應。

再回來談專案:我們的購物清單,會有個能增加與刪減項目的輸入表單、還有按鈕。這些動作要透過模型更新,所以輸入會送往控制器。控制器接著會操縱適合的模型、並將資料送往視圖。

你可能會接著想更新視圖,令資料以不同的格式呈現:例如按字母順序、或由價格高低排列之類的。控制器能在不改變模型的情況下,處理這類的事情。

網路 MVC 的演化

身為 web 開發者,就算從未有意識地用這種模式開發過,你還是會對此感到熟悉:你的資料模型可能儲存在某種資料庫裡面(通常是如 MySQL 的伺服器端資料庫,或是如 IndexedDB 的客戶端解決方案)你 app 的控制碼通常以 HTML/JavaScript 撰寫、用戶介面則可能用 HTML/CSS/或其他你常用的語言寫下來。這聽來很像 MVC,不過 MVC 令這些組件遵從了更嚴格的模式。

早期 MVC 架構通常在伺服器端實做,客戶端則透過表單或連結發出更新請求。接收已更新的視圖後,顯示在瀏覽器。不過近來隨著客戶端的資料儲存、以及能透過需求,只更新一部份網頁的 XMLHttpRequest 來臨,客戶端推送(pushed)了更多的實現邏輯。

當前的熱門網路框架,如 AngularJSEmber.jsBackbone 都實做了 MVC 架構,雖然方法大不相同。

Ember.js 如何實做 MVC

我們在這個系列會使用的框架 Ember 根據一套嚴格的概念與規則實做了 MVC。這份簡短摘要,會涵蓋以目前而言,你應該知道的東西。你可以在 Ember 的文件看到更多東西,Core Concepts 很適合作為開始的地方。

你應該對 Ember 的 naming conventions 了然於心:嚴格執行的話,能令物件之間不用額外連結,也能交流愉快。

最後,因為我們會透過 Ember CLI(命令行界面)自動完成許多過程,你也應該把 Ember CLI documentation 放在手邊。

Ember 的視圖層

在 Ember 裡面的視圖,與其他 MVC 的視圖有相當大的不同:以 MVC 的術語上來說,我們會把視圖想成「整個 UI(使用者介面)層」;但在 Ember 裡面,視圖與路徑(route)及模板(template)一樣,是 UI 的個體元件之一

典型的簡易 Ember.js 會使用路徑物件與模板檔案處理視圖。路徑會指派用戶訪問特定 template 時該用什麼 URL,也同時指派某個模型該使用哪個模板。每個路徑都以 JavaScript 檔案表現出來。模板指名使用者介面該長什麼樣子、並由 Handlebars 模板檔案(.hbs)表現出來。這些檔案是帶有特殊動態表達式──它會以兩個大括號表示,亦即 {{ ... }}──的 HTML。兩個大括號,以最簡單的形式來說,會抓取包含在模型(或控制器)裡面的資料/屬性、並把它輸出到使用者介面,例如:

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>

當模型或控制器更動的時候,這些屬性也會在模板內自行更新。

Ember CLI 能透過以下指令產生路徑、並與模板相關聯:

ember generate route my-route-name

它會:

  1. (你-app-的根目錄)/app/routes 產生一個控制路徑的 JavaScript 檔案。
  2. (你-app-的根目錄)/app/templates 產生一個 Handlebar 模板。該模板定義的內容,會出現在你命名的 URL 上面。
  3. (你-app-的根目錄)/tests/unit/routes 產生一個能在你路徑內,指定功能測試(test for the functionality)的單元測試(unit test)檔案。

路徑檔案會包含一個方便撰寫所有必要路徑邏輯(route logic)的基本骨架:

import Ember from 'ember';

export default Ember.Route.extend({
  // 你依照需求完成的功能
});

如同其他 MVC 框架一般,Ember 也提供了能透過擴充,滿足客製化程式需求的程式核心結構基礎。這個程式碼載入了主要的 Ember 物件,接著透過呼叫 extend 方法,讓你能在 Ember 的內置(built-in)物件 Route 上組建功能(build functionality)。在組建應用程序的時候,你會開始理解這個很常見的模式。

物件與模板會一同呼叫某個檔案,例如這裡的 shopping-list.jsshopping-list.hbs。在通常情況下,它們會因此聯繫在一起。視圖接著會以 your-server.com/shopping-list/ 或其他指定呼叫的形式,呈現在用戶眼前。

Ember 控制器

Ember.js 用 Controllers 以代表在 JavaScript 檔案內的控制器物件。

Ember CLI 能透過以下指令產生控制器:

ember generate controller name-of-my-controller

這個指令會:

  1. (你-app-的根目錄)/app/controllers 產生一個控制特定模型/視圖的 JavaScript 檔案。
  2. (你-app-的根目錄)/tests/unit/controllers 產生一個能在控制器內,指定功能測試的單元測試檔案。

以目前的購物清單為例,如果我們有了稱為 shopping-list 的路徑與模板,就應該把要產生的控制器稱為 shopping-list。依此命名的話會自動讓這個控制器與正確的路徑及(或)模型相關連。

透過生成的控制器檔案裡面會有:

import Ember from 'ember';

export default Ember.Controller.extend({
  // 你依照需求完成的功能
});

類似我們的路徑,我們的控制器載入了主要的 Ember 物件並擴充了預設的 Controller 物件,令我們得以創建各種客製化的控制器。

Ember 模型

Ember.js 用 Models 以代表在 JavaScript 檔案內的模型物件。

Ember CLI 能透過以下指令產生模型:

ember generate model name-of-my-model

這個指令會:

  1. (你-app-的根目錄)/app/models 產生一個定義與特定視圖相關聯的 JavaScript 檔案。
  2. (你-app-的根目錄)/tests/unit/models 產生一個能在模型內,指定功能測試的單元測試檔案。

以目前的購物清單為例,就應該把模型稱作 shopping-list。依此命名的話,會自動讓這個模型與正確的路徑及(或)控制器相關連。

產生出來的模型檔案會包含:

import DS from 'ember-data';

export default DS.Model.extend({
  // 你依照需求完成的功能
});

類似我們的路徑與控制器,我們的模型載入了主要的 Ember 物件並擴充了預設的 Model 物件,令我們得以創建各種客製化的模型。

下一步

現在我們講過一些在 Ember 與 MVC 背後的簡單理論,我們接著會把這些東西,實做到真正的 Ember-based MVC 程式上。

文件標籤與貢獻者

 此頁面的貢獻者: iigmir
 最近更新: iigmir,