單頁面應用SPA架構

我的認爲單頁面應用的優點至關明顯:javascript

  1. 先後端職責分離,架構清晰:前端進行交互邏輯,後端負責數據處理。
  2. 先後端單獨開發、單獨測試。
  3. 良好的交互體驗,前端進行的是局部渲染。避免了沒必要要的跳轉和重複渲染。

固然,SPA也有它自身的缺點,例如不利於搜索引擎優化等等,這些問題也有其相應的解決方案。php

下面要介紹的這種方式能夠說是一種模式或者工做流,和前端使用什麼框架無關,也和後端使用什麼語言、數據庫無關。不能說是The Best Practice,我相信通過更多人的討論和思考會有A Better Practice。:)css

概覽

下圖展現了這種模式的整個先後端及各自的主要組成:html

看起來有點複雜,接下來會仔細地對上面每個部分進行解釋。看完本文,就應該能理解上圖中的各部件之間的交互流程。前端

前端架構

把上圖的前端部分單獨抽出來進行研究:java

前端中大體分爲四種類型的模塊:git

  1. components:前端UI組件
  2. services:前端數據緩存和操做層
  3. databus:封裝一系列Ajax操做,和後端進行數據交互的部件
  4. common/utils:以上組件的共用部件,可複用的函數、數據等

components

component指的是頁面上的一個可複用UI交互單元,例如一個博客的評論功能:github

咱們能夠把博客評論作爲一個組件,這個組件有本身的結構(html),外觀(css),交互邏輯(js),因此咱們能夠單獨作一個叫comment的component,由如下文件組成:ajax

  1. comment.html
  2. comment.css
  3. comment.js

(每一個component能夠想象成一個工程,甚至能夠有本身的README、測試等)數據庫

components tree

一個component能夠依賴另一個component,這時候它們是父子關係;component之間也能夠互相組合,它們就是兄弟關係。最後的結果就相似DOM tree,component能夠組成components tree。

例如,如今要給這個博客添加兩個功能:

  1. 顯示評論回覆。
  2. 鼠標放到評論或者回復的用戶頭像上能夠顯示用戶名片。

咱們構建兩個組件,reply和user-info-card。由於每一個comment都要有本身的回覆列表,因此comment組件是依賴於reply組件的,comment和reply組件是嵌套關係。

而user-info-card能夠出如今comment或者reply當中,而且爲了之後讓user-info-card複用性更強,它應該不屬於任何一個組件,它和其餘組件是組合關係。因此咱們就獲得一個簡單的componenets tree:

components之間的通訊

怎麼能夠作到鼠標放到評論和回覆的用戶頭像上顯示名片呢?這其實牽涉到組件之間是如何進行通訊的問題。

最佳的方式就是使用事件機制,全部組件之間能夠經過一個叫eventbus通用組件進行信息的交互。因此,要作到上述功能:

  1. user-info-card能夠在eventbus監聽一個user-info-card:show的事件。
  2. 而當鼠標放到comment和reply組件的頭像上的時候,組件可使用eventbus觸發user-info-card:show事件。

user-info-card:

var eventbus = require("eventbus") eventbus.on("user-info-card:show", function(user) { // 顯示用戶名片 }) 

comment or reply:

var eventbus = require("eventbus") $avatar.on("mouseover", function(event) { eventbus.emit("user-info-card:show", userData) }) 

components之間用事件進行通訊的優點在於:

  1. 組件之間沒有強的依賴,組件之間被解耦。
  2. 組件之間能夠單獨開發、單獨測試。數據和事件均可以簡單的進行僞造進行測試(mocking)。

總結:component之間有嵌套和組合的關係,構成components tree;component之間經過事件進行信息、數據的交換。

services

component的渲染和顯示依賴於數據(model)。例如上面的評論,就會有一個評論列表的model。

comments: [ {user:.., content:.., createTime: ..}, {user:.., content:.., createTime: ..}, {user:.., content:.., createTime: ..} ] 

每一個評論的component會對應一個comment(comments數組中的對象)進行渲染,渲染完之後就會正確地顯示在頁面上。

由於可能在其餘component中也會須要用到這些數據,因此comment component不會本身直接保存這些comment model。這些model都會保存在service當中,而component會從service拿取數據。components和services之間是多對多的關係:一個component可能會從不一樣的services中拿取數據,而一個service可能爲多個components提供數據。

services除了用於緩存數據之外,還提供一系列對數據的一些操做接口。能夠提供給components進行操做。這樣的好處在於保持了數據的一直性,假如你使用的是MVVM框架進行component的開發,對數據的操做還能夠直接對多個視圖產生數據綁定,當services中的數據變化了,多個components的視圖也會相應地獲得更新。

總結:services是對前端數據(也就是model)的緩存和操做。

databus

而services中緩存的數據是從哪裏來的呢?固然也許想到的第一個方案是在services中直接發送Ajax請求去服務器中拉去數據。而這裏建議不直接這樣作,而是把各類和後端的API進行交互的接口封裝到一個叫databus的模塊當中,這裏的databus至關因而「對後端數據進行原子操做的集合」。

如上面的comment service須要從後端進行拉取數據,它會這樣作:

var databus = require("databus") var comments = null databus.getAllComments(function(cmts) { // 調用databus方法進行數據拉取 comments = cmts }) 

而databus中則封裝了一層Ajax

databus.getAllCommetns = function(callback) { utils.ajax({ url: "/comments", method: "GET", success: callback }) } 

這樣作是由於,不一樣的services之間可能會用到一樣的接口對後端進行操做,把操做封裝起來能夠提升接口的複用性。注意,若是databus中的某些操做不涉及到servcies的數據,這操做也能夠被components所調用(例如退出、登陸等)。

總結:databus封裝了提供給services和component和後端API進行交互的接口。

common/utils

這兩個模塊均可以被其餘組件所依賴。

common,故名思議,組件之間的共用數據和一些程序參數能夠緩存在這裏。

utils,封裝了一些可複用的函數,例如ajax等。

eventbus

全部組件(特別是components之間)的經過事件機制進行數據、消息通訊的接口。能夠簡單地使用EventEmitter這個庫來實現。

後端架構

傳統的網頁頁面通常都是由後端進行頁面的渲染,而在咱們的架構當中,後端只渲染一個頁面,其後,後端只是至關於一個Web Service,前端使用Ajax調用其接口進行數據的調取和操做,使用數據進行頁面的渲染。

這樣的好處就是,後端不只僅能處理Web端的頁面的請求,並且處理提供移動端、桌面端的請求或者做爲第三方開放接口來使用。大大提升後端處理請求的靈活性。

後端對比起前端的架構來講會簡單不少,可是這只是其中一種模式,對於不一樣複雜程度的應用可能會作相應的調整。後端大概分爲三層:

  1. CGI:設置不一樣的路由規則,接受前端來的請求,處理數據,返回結果。
  2. business:這一層封裝了對數據庫的一些操做,business能夠被CGI所調用。
  3. database:數據庫,進行數據的持久化。

例如上面的comments的例子,CGI能夠接收到前端發送的請求:

var commentsBusiness = require("./businesses/comments") app.get("/comments", function(req, res) { // 此處調用comments的business數據庫操做 commentsBusiness.getAllComments(function(comments) { // 返回數據結果 res.json(comments) }) }) 

後端的API能夠採用更規範的RESTful API的方式,而RESTful不在本文的討論範圍內。有興趣的能夠參考Best Practices for Designing a Pragmatic RESTful API

先後端的架構都基本清晰了,咱們來看看文章開頭的圖:

看着圖來,咱們總結一下整個先後端的交互流程:

  1. 前端向服務端請求第一個頁面,後端渲染返回。
  2. 前端加載各個component,components從services拿數據,services經過databus發送Ajax請求向後端取數據。
  3. 後端的CGI接收到前端databus發送過來的請求,處理數據,調用business操做數據庫,返回結果。
  4. 前端接收到後端返回的結果,把數據緩存到service,component拿到數據進行前端組件的渲染、顯示。

工做流

一個好的工做流可讓開發事半功倍。上面的這種單頁面應用也有其相應的一種開發工做流,固然這種工做流也適合非單頁面應用:

  1. 進行產品功能、原型設計。
  2. 後端數據庫設計。
  3. 根據產品肯定先後端的API(or RESTful API),以文檔方式紀錄。
  4. 先後端就能夠針對API文檔同時進行開發。
  5. 先後端最後進行鏈接測試。

先後端分離開發。建議均可以採用TDD(測試驅動開發)的方式來單獨測試、單獨開發(關於Web APP測試這一塊能夠單獨進行討論研究),提升產品的可靠性、穩定性。

相關文章
相關標籤/搜索