摘要:個人認為單頁面應用的優(yōu)勢相當明顯前后端職責分離,架構(gòu)清晰前端進行交互邏輯,后端負責數(shù)據(jù)處理。上面的這種單頁面應用也有其相應的一種開發(fā)工作流,當然這種工作流也適合非單頁面應用進行產(chǎn)品功能原型設計。
未經(jīng)允許,請勿轉(zhuǎn)載。本文同時也發(fā)布在我的博客。
(如果對SPA概念不清楚的同學可以先自行了解相關概念)
平時喜歡做點小頁面來玩玩,并且一直采用單頁面應用(Single Page Application)的方式來進行開發(fā)。這種開發(fā)方式是在之前一年做的一個創(chuàng)業(yè)項目的經(jīng)驗和思考,一直想寫篇博客來總結(jié)一下。
個人認為單頁面應用的優(yōu)勢相當明顯:
前后端職責分離,架構(gòu)清晰:前端進行交互邏輯,后端負責數(shù)據(jù)處理。
前后端多帶帶開發(fā)、多帶帶測試。
良好的交互體驗,前端進行的是局部渲染。避免了不必要的跳轉(zhuǎn)和重復渲染。
當然,SPA也有它自身的缺點,例如不利于搜索引擎優(yōu)化等等,這些問題也有其相應的解決方案。
下面要介紹的這種方式可以說是一種模式或者工作流,和前端使用什么框架無關,也和后端使用什么語言、數(shù)據(jù)庫無關。不能說是The Best Practice,我相信經(jīng)過更多人的討論和思考會有A Better Practice。:)
概覽下圖展示了這種模式的整個前后端及各自的主要組成:
看起來有點復雜,接下來會仔細地對上面每一個部分進行解釋??赐瓯疚模蛻撃芾斫馍蠄D中的各部件之間的交互流程。
前端架構(gòu)把上圖的前端部分多帶帶抽出來進行研究:
前端中大致分為四種類型的模塊:
components:前端UI組件
services:前端數(shù)據(jù)緩存和操作層
databus:封裝一系列Ajax操作,和后端進行數(shù)據(jù)交互的部件
common/utils:以上組件的共用部件,可復用的函數(shù)、數(shù)據(jù)等
componentscomponent指的是頁面上的一個可復用UI交互單元,例如一個博客的評論功能:
我們可以把博客評論做為一個組件,這個組件有自己的結(jié)構(gòu)(html),外觀(css),交互邏輯(js),所以我們可以多帶帶做一個叫comment的component,由以下文件組成:
comment.html
comment.css
comment.js
(每個component可以想象成一個工程,甚至可以有自己的README、測試等)
components tree一個component可以依賴另外一個component,這時候它們是父子關系;component之間也可以互相組合,它們就是兄弟關系。最后的結(jié)果就類似DOM tree,component可以組成components tree。
例如,現(xiàn)在要給這個博客添加兩個功能:
顯示評論回復。
鼠標放到評論或者回復的用戶頭像上可以顯示用戶名片。
我們構(gòu)建兩個組件,reply和user-info-card。因為每個comment都要有自己的回復列表,所以comment組件是依賴于reply組件的,comment和reply組件是嵌套關系。
而user-info-card可以出現(xiàn)在comment或者reply當中,并且為了以后讓user-info-card復用性更強,它應該不屬于任何一個組件,它和其他組件是組合關系。所以我們就得到一個簡單的componenets tree:
components之間的通信怎么可以做到鼠標放到評論和回復的用戶頭像上顯示名片呢?這其實牽涉到組件之間是如何進行通信的問題。
最佳的方式就是使用事件機制,所有組件之間可以通過一個叫eventbus通用組件進行信息的交互。所以,要做到上述功能:
user-info-card可以在eventbus監(jiān)聽一個user-info-card:show的事件。
而當鼠標放到comment和reply組件的頭像上的時候,組件可以使用eventbus觸發(fā)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之間用事件進行通信的優(yōu)勢在于:
組件之間沒有強的依賴,組件之間被解耦。
組件之間可以多帶帶開發(fā)、多帶帶測試。數(shù)據(jù)和事件都可以簡單的進行偽造進行測試(mocking)。
總結(jié):component之間有嵌套和組合的關系,構(gòu)成components tree;component之間通過事件進行信息、數(shù)據(jù)的交換。
servicescomponent的渲染和顯示依賴于數(shù)據(jù)(model)。例如上面的評論,就會有一個評論列表的model。
comments: [ {user:.., content:.., createTime: ..}, {user:.., content:.., createTime: ..}, {user:.., content:.., createTime: ..} ]
每個評論的component會對應一個comment(comments數(shù)組中的對象)進行渲染,渲染完以后就會正確地顯示在頁面上。
因為可能在其他component中也會需要用到這些數(shù)據(jù),所以comment component不會自己直接保存這些comment model。這些model都會保存在service當中,而component會從service拿取數(shù)據(jù)。components和services之間是多對多的關系:一個component可能會從不同的services中拿取數(shù)據(jù),而一個service可能為多個components提供數(shù)據(jù)。
services除了用于緩存數(shù)據(jù)以外,還提供一系列對數(shù)據(jù)的一些操作接口??梢蕴峁┙ocomponents進行操作。這樣的好處在于保持了數(shù)據(jù)的一直性,假如你使用的是MVVM框架進行component的開發(fā),對數(shù)據(jù)的操作還可以直接對多個視圖產(chǎn)生數(shù)據(jù)綁定,當services中的數(shù)據(jù)變化了,多個components的視圖也會相應地得到更新。
總結(jié):services是對前端數(shù)據(jù)(也就是model)的緩存和操作。
databus而services中緩存的數(shù)據(jù)是從哪里來的呢?當然也許想到的第一個方案是在services中直接發(fā)送Ajax請求去服務器中拉去數(shù)據(jù)。而這里建議不直接這樣做,而是把各種和后端的API進行交互的接口封裝到一個叫databus的模塊當中,這里的databus相當于是“對后端數(shù)據(jù)進行原子操作的集合”。
如上面的comment service需要從后端進行拉取數(shù)據(jù),它會這樣做:
var databus = require("databus") var comments = null databus.getAllComments(function(cmts) { // 調(diào)用databus方法進行數(shù)據(jù)拉取 comments = cmts })
而databus中則封裝了一層Ajax
databus.getAllCommetns = function(callback) { utils.ajax({ url: "/comments", method: "GET", success: callback }) }
這樣做是因為,不同的services之間可能會用到同樣的接口對后端進行操作,把操作封裝起來可以提高接口的復用性。注意,如果databus中的某些操作不涉及到servcies的數(shù)據(jù),這操作也可以被components所調(diào)用(例如退出、登錄等)。
總結(jié):databus封裝了提供給services和component和后端API進行交互的接口。
common/utils這兩個模塊都可以被其他組件所依賴。
common,故名思議,組件之間的共用數(shù)據(jù)和一些程序參數(shù)可以緩存在這里。
utils,封裝了一些可復用的函數(shù),例如ajax等。
eventbus所有組件(特別是components之間)的通過事件機制進行數(shù)據(jù)、消息通信的接口。可以簡單地使用EventEmitter這個庫來實現(xiàn)。
后端架構(gòu)傳統(tǒng)的網(wǎng)頁頁面一般都是由后端進行頁面的渲染,而在我們的架構(gòu)當中,后端只渲染一個頁面,其后,后端只是相當于一個Web Service,前端使用Ajax調(diào)用其接口進行數(shù)據(jù)的調(diào)取和操作,使用數(shù)據(jù)進行頁面的渲染。
這樣的好處就是,后端不僅僅能處理Web端的頁面的請求,而且處理提供移動端、桌面端的請求或者作為第三方開放接口來使用。大大提高后端處理請求的靈活性。
后端對比起前端的架構(gòu)來說會簡單很多,但是這只是其中一種模式,對于不同復雜程度的應用可能會做相應的調(diào)整。后端大概分為三層:
CGI:設置不同的路由規(guī)則,接受前端來的請求,處理數(shù)據(jù),返回結(jié)果。
business:這一層封裝了對數(shù)據(jù)庫的一些操作,business可以被CGI所調(diào)用。
database:數(shù)據(jù)庫,進行數(shù)據(jù)的持久化。
例如上面的comments的例子,CGI可以接收到前端發(fā)送的請求:
var commentsBusiness = require("./businesses/comments") app.get("/comments", function(req, res) { // 此處調(diào)用comments的business數(shù)據(jù)庫操作 commentsBusiness.getAllComments(function(comments) { // 返回數(shù)據(jù)結(jié)果 res.json(comments) }) })
后端的API可以采用更規(guī)范的RESTful API的方式,而RESTful不在本文的討論范圍內(nèi)。有興趣的可以參考Best Practices for Designing a Pragmatic RESTful API。
前后端的架構(gòu)都基本清晰了,我們來看看文章開頭的圖:
看著圖來,我們總結(jié)一下整個前后端的交互流程:
前端向服務端請求第一個頁面,后端渲染返回。
前端加載各個component,components從services拿數(shù)據(jù),services通過databus發(fā)送Ajax請求向后端取數(shù)據(jù)。
后端的CGI接收到前端databus發(fā)送過來的請求,處理數(shù)據(jù),調(diào)用business操作數(shù)據(jù)庫,返回結(jié)果。
前端接收到后端返回的結(jié)果,把數(shù)據(jù)緩存到service,component拿到數(shù)據(jù)進行前端組件的渲染、顯示。
工作流一個好的工作流可以讓開發(fā)事半功倍。上面的這種單頁面應用也有其相應的一種開發(fā)工作流,當然這種工作流也適合非單頁面應用:
進行產(chǎn)品功能、原型設計。
后端數(shù)據(jù)庫設計。
根據(jù)產(chǎn)品確定前后端的API(or RESTful API),以文檔方式紀錄。
前后端就可以針對API文檔同時進行開發(fā)。
前后端最后進行連接測試。
前后端分離開發(fā)。建議都可以采用TDD(測試驅(qū)動開發(fā))的方式來多帶帶測試、多帶帶開發(fā)(關于Web APP測試這一塊可以多帶帶進行討論研究),提高產(chǎn)品的可靠性、穩(wěn)定性。
(完)
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87577.html
摘要:單頁面應用的出現(xiàn)依然存在著爭議性,我們該如何看待他的兩面性呢接下來小生給大家總結(jié)一下他的優(yōu)缺點。單頁面應用的優(yōu)勢無刷新體驗沒有了令人詬病的頁面頻繁刷新,同時節(jié)約瀏覽器資源,路由響應比較及時,提升了用戶的體驗。 前端猿一天不學習就沒飯吃了,后端猿三天不學習仍舊有白米飯擺于桌前。IT行業(yè)的快速發(fā)展一直在推動著前端技術棧在不斷地更新?lián)Q代,前端的發(fā)展成了互聯(lián)網(wǎng)時代的一個縮影。而單頁面應用的發(fā)展...
摘要:單頁面應用的出現(xiàn)依然存在著爭議性,我們該如何看待他的兩面性呢接下來小生給大家總結(jié)一下他的優(yōu)缺點。單頁面應用的優(yōu)勢無刷新體驗沒有了令人詬病的頁面頻繁刷新,同時節(jié)約瀏覽器資源,路由響應比較及時,提升了用戶的體驗。 前端猿一天不學習就沒飯吃了,后端猿三天不學習仍舊有白米飯擺于桌前。IT行業(yè)的快速發(fā)展一直在推動著前端技術棧在不斷地更新?lián)Q代,前端的發(fā)展成了互聯(lián)網(wǎng)時代的一個縮影。而單頁面應用的發(fā)展...
摘要:的全稱是統(tǒng)一資源定位符英文,可以這么說,是一種標準,而網(wǎng)址則是符合標準的一種實現(xiàn)而已。渲染器,將組件渲染到頁面上。 0x000 概述 從這一章開始就進入路由章節(jié)了,并不直接從如何使用react-route來講,而是從路由的概念和實現(xiàn)來講,達到知道路由的本質(zhì),而不是只知道如何使用react-route庫的目的,畢竟react-route只是一個庫,是路由的一個實現(xiàn)而已,而不是路由本身。 ...
閱讀 2237·2021-09-24 10:31
閱讀 3887·2021-09-22 15:16
閱讀 3408·2021-09-22 10:02
閱讀 1023·2021-09-22 10:02
閱讀 1837·2021-09-08 09:36
閱讀 1982·2019-08-30 14:18
閱讀 616·2019-08-30 10:51
閱讀 1876·2019-08-29 11:08