日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

一种SPA(单页面应用)架构

發(fā)布時(shí)間:2025/3/16 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一种SPA(单页面应用)架构 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

(如果對(duì)SPA概念不清楚的同學(xué)可以先自行了解相關(guān)概念)

? ? 平時(shí)喜歡做點(diǎn)小頁面來玩玩,并且一直采用單頁面應(yīng)用(Single Page Application)的方式來進(jìn)行開發(fā)。這種開發(fā)方式是在之前一年做的一個(gè)創(chuàng)業(yè)項(xiàng)目的經(jīng)驗(yàn)和思考,一直想寫篇博客來總結(jié)一下。

? ? 個(gè)人認(rèn)為單頁面應(yīng)用的優(yōu)勢(shì)相當(dāng)明顯:

  • 前后端職責(zé)分離,架構(gòu)清晰:前端進(jìn)行交互邏輯,后端負(fù)責(zé)數(shù)據(jù)處理。
  • 前后端單獨(dú)開發(fā)、單獨(dú)測(cè)試。
  • 良好的交互體驗(yàn),前端進(jìn)行的是局部渲染。避免了不必要的跳轉(zhuǎn)和重復(fù)渲染。
  • ? ? 當(dāng)然,SPA也有它自身的缺點(diǎn),例如不利于搜索引擎優(yōu)化等等,這些問題也有其相應(yīng)的解決方案。

    ? ? 下面要介紹的這種方式可以說是一種模式或者工作流,和前端使用什么框架無關(guān),也和后端使用什么語言、數(shù)據(jù)庫無關(guān)。不能說是The Best Practice,我相信經(jīng)過更多人的討論和思考會(huì)有A Better Practice。:)

    概覽

    ? ? 下圖展示了這種模式的整個(gè)前后端及各自的主要組成:

    ? ? 看起來有點(diǎn)復(fù)雜,接下來會(huì)仔細(xì)地對(duì)上面每一個(gè)部分進(jìn)行解釋。看完本文,就應(yīng)該能理解上圖中的各部件之間的交互流程。

    前端架構(gòu)

    ? ? 把上圖的前端部分單獨(dú)抽出來進(jìn)行研究:

    ? ? 前端中大致分為四種類型的模塊:

  • components:前端UI組件
  • services:前端數(shù)據(jù)緩存和操作層
  • databus:封裝一系列Ajax操作,和后端進(jìn)行數(shù)據(jù)交互的部件
  • common/utils:以上組件的共用部件,可復(fù)用的函數(shù)、數(shù)據(jù)等
  • components

    ? ? component指的是頁面上的一個(gè)可復(fù)用UI交互單元,例如一個(gè)博客的評(píng)論功能:

    ? ? 我們可以把博客評(píng)論做為一個(gè)組件,這個(gè)組件有自己的結(jié)構(gòu)(html),外觀(css),交互邏輯(js),所以我們可以單獨(dú)做一個(gè)叫comment的 ? ? ? ?component,由以下文件組成:

  • comment.html
  • comment.css
  • comment.js
  • (每個(gè)component可以想象成一個(gè)工程,甚至可以有自己的README、測(cè)試等)

    components tree

    ? ? 一個(gè)component可以依賴另外一個(gè)component,這時(shí)候它們是父子關(guān)系;component之間也可以互相組合,它們就是兄弟關(guān)系。最后的結(jié)果就類似DOM tree,component可以組成components tree。

    例如,現(xiàn)在要給這個(gè)博客添加兩個(gè)功能:

  • 顯示評(píng)論回復(fù)。
  • 鼠標(biāo)放到評(píng)論或者回復(fù)的用戶頭像上可以顯示用戶名片。
  • ? ? 我們構(gòu)建兩個(gè)組件,reply和user-info-card。因?yàn)槊總€(gè)comment都要有自己的回復(fù)列表,所以comment組件是依賴于reply組件的,comment和reply組件是嵌套關(guān)系。

    ? ? 而user-info-card可以出現(xiàn)在comment或者reply當(dāng)中,并且為了以后讓user-info-card復(fù)用性更強(qiáng),它應(yīng)該不屬于任何一個(gè)組件,它和其他組件是組合關(guān)系。所以我們就得到一個(gè)簡(jiǎn)單的componenets tree:

    components之間的通信

    ? ? 怎么可以做到鼠標(biāo)放到評(píng)論和回復(fù)的用戶頭像上顯示名片呢?這其實(shí)牽涉到組件之間是如何進(jìn)行通信的問題。

    ? ? 最佳的方式就是使用事件機(jī)制,所有組件之間可以通過一個(gè)叫eventbus通用組件進(jìn)行信息的交互。所以,要做到上述功能:

  • user-info-card可以在eventbus監(jiān)聽一個(gè)user-info-card:show的事件。
  • 而當(dāng)鼠標(biāo)放到comment和reply組件的頭像上的時(shí)候,組件可以使用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之間用事件進(jìn)行通信的優(yōu)勢(shì)在于:

  • 組件之間沒有強(qiáng)的依賴,組件之間被解耦。
  • 組件之間可以單獨(dú)開發(fā)、單獨(dú)測(cè)試。數(shù)據(jù)和事件都可以簡(jiǎn)單的進(jìn)行偽造進(jìn)行測(cè)試(mocking)。
  • ? ? 總結(jié):component之間有嵌套和組合的關(guān)系,構(gòu)成components tree;component之間通過事件進(jìn)行信息、數(shù)據(jù)的交換。

    services

    ? ? component的渲染和顯示依賴于數(shù)據(jù)(model)。例如上面的評(píng)論,就會(huì)有一個(gè)評(píng)論列表的model。

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

    ? ? 每個(gè)評(píng)論的component會(huì)對(duì)應(yīng)一個(gè)comment(comments數(shù)組中的對(duì)象)進(jìn)行渲染,渲染完以后就會(huì)正確地顯示在頁面上。

    ? ? 因?yàn)榭赡茉谄渌鹀omponent中也會(huì)需要用到這些數(shù)據(jù),所以comment component不會(huì)自己直接保存這些comment model。這些model都會(huì)保存在service當(dāng)中,而component會(huì)從service拿取數(shù)據(jù)。components和services之間是多對(duì)多的關(guān)系:一個(gè)component可能會(huì)從不同的services中拿取數(shù)據(jù),而一個(gè)service可能為多個(gè)components提供數(shù)據(jù)。

    ? ? services除了用于緩存數(shù)據(jù)以外,還提供一系列對(duì)數(shù)據(jù)的一些操作接口。可以提供給components進(jìn)行操作。這樣的好處在于保持了數(shù)據(jù)的一直性,假如你使用的是MVVM框架進(jìn)行component的開發(fā),對(duì)數(shù)據(jù)的操作還可以直接對(duì)多個(gè)視圖產(chǎn)生數(shù)據(jù)綁定,當(dāng)services中的數(shù)據(jù)變化了,多個(gè)components的視圖也會(huì)相應(yīng)地得到更新。

    ? ? 總結(jié):services是對(duì)前端數(shù)據(jù)(也就是model)的緩存和操作。

    databus

    ? ? 而services中緩存的數(shù)據(jù)是從哪里來的呢?當(dāng)然也許想到的第一個(gè)方案是在services中直接發(fā)送Ajax請(qǐng)求去服務(wù)器中拉去數(shù)據(jù)。而這里建議不直接這樣做,而是把各種和后端的API進(jìn)行交互的接口封裝到一個(gè)叫databus的模塊當(dāng)中,這里的databus相當(dāng)于是“對(duì)后端數(shù)據(jù)進(jìn)行原子操作的集合”。

    ? ? 如上面的comment service需要從后端進(jìn)行拉取數(shù)據(jù),它會(huì)這樣做:

    var databus = require("databus") var comments = null databus.getAllComments(function(cmts) { // 調(diào)用databus方法進(jìn)行數(shù)據(jù)拉取comments = cmts })

    ? ? 而databus中則封裝了一層Ajax

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

    ? ? 這樣做是因?yàn)?#xff0c;不同的services之間可能會(huì)用到同樣的接口對(duì)后端進(jìn)行操作,把操作封裝起來可以提高接口的復(fù)用性。注意,如果databus中的某些操作不涉及到servcies的數(shù)據(jù),這操作也可以被components所調(diào)用(例如退出、登錄等)。

    ? ? 總結(jié):databus封裝了提供給services和component和后端API進(jìn)行交互的接口。

    common/utils

    ? ? 這兩個(gè)模塊都可以被其他組件所依賴。

    ? ? common,故名思議,組件之間的共用數(shù)據(jù)和一些程序參數(shù)可以緩存在這里。

    ? ? utils,封裝了一些可復(fù)用的函數(shù),例如ajax等。

    eventbus

    ? ? 所有組件(特別是components之間)的通過事件機(jī)制進(jìn)行數(shù)據(jù)、消息通信的接口。可以簡(jiǎn)單地使用EventEmitter這個(gè)庫來實(shí)現(xiàn)。

    后端架構(gòu)

    ? ? 傳統(tǒng)的網(wǎng)頁頁面一般都是由后端進(jìn)行頁面的渲染,而在我們的架構(gòu)當(dāng)中,后端只渲染一個(gè)頁面,其后,后端只是相當(dāng)于一個(gè)Web Service,前端使用Ajax調(diào)用其接口進(jìn)行數(shù)據(jù)的調(diào)取和操作,使用數(shù)據(jù)進(jìn)行頁面的渲染。

    ? ? 這樣的好處就是,后端不僅僅能處理Web端的頁面的請(qǐng)求,而且處理提供移動(dòng)端、桌面端的請(qǐng)求或者作為第三方開放接口來使用。大大提高后端處理請(qǐng)求的靈活性。

    ? ? 后端對(duì)比起前端的架構(gòu)來說會(huì)簡(jiǎn)單很多,但是這只是其中一種模式,對(duì)于不同復(fù)雜程度的應(yīng)用可能會(huì)做相應(yīng)的調(diào)整。后端大概分為三層:

  • CGI:設(shè)置不同的路由規(guī)則,接受前端來的請(qǐng)求,處理數(shù)據(jù),返回結(jié)果。
  • business:這一層封裝了對(duì)數(shù)據(jù)庫的一些操作,business可以被CGI所調(diào)用。
  • database:數(shù)據(jù)庫,進(jìn)行數(shù)據(jù)的持久化。
  • ? ? 例如上面的comments的例子,CGI可以接收到前端發(fā)送的請(qǐng)求:

    var commentsBusiness = require("./businesses/comments") app.get("/comments", function(req, res) {// 此處調(diào)用comments的business數(shù)據(jù)庫操作commentsBusiness.getAllComments(function(comments) {// 返回?cái)?shù)據(jù)結(jié)果res.json(comments)}) })

    ? ? 后端的API可以采用更規(guī)范的RESTful API的方式,而RESTful不在本文的討論范圍內(nèi)。有興趣的可以參考Best Practices for Designing a Pragmatic RESTful API。

    ? ? 前后端的架構(gòu)都基本清晰了,我們來看看文章開頭的圖:

    ? ? 看著圖來,我們總結(jié)一下整個(gè)前后端的交互流程:

  • 前端向服務(wù)端請(qǐng)求第一個(gè)頁面,后端渲染返回。
  • 前端加載各個(gè)component,components從services拿數(shù)據(jù),services通過databus發(fā)送Ajax請(qǐng)求向后端取數(shù)據(jù)。
  • 后端的CGI接收到前端databus發(fā)送過來的請(qǐng)求,處理數(shù)據(jù),調(diào)用business操作數(shù)據(jù)庫,返回結(jié)果。
  • 前端接收到后端返回的結(jié)果,把數(shù)據(jù)緩存到service,component拿到數(shù)據(jù)進(jìn)行前端組件的渲染、顯示。
  • 工作流

    ? ? 一個(gè)好的工作流可以讓開發(fā)事半功倍。上面的這種單頁面應(yīng)用也有其相應(yīng)的一種開發(fā)工作流,當(dāng)然這種工作流也適合非單頁面應(yīng)用:

  • 進(jìn)行產(chǎn)品功能、原型設(shè)計(jì)。
  • 后端數(shù)據(jù)庫設(shè)計(jì)。
  • 根據(jù)產(chǎn)品確定前后端的API(or RESTful API),以文檔方式紀(jì)錄。
  • 前后端就可以針對(duì)API文檔同時(shí)進(jìn)行開發(fā)。
  • 前后端最后進(jìn)行連接測(cè)試。
  • ? ? 前后端分離開發(fā)。建議都可以采用TDD(測(cè)試驅(qū)動(dòng)開發(fā))的方式來單獨(dú)測(cè)試、單獨(dú)開發(fā)(關(guān)于Web APP測(cè)試這一塊可以單獨(dú)進(jìn)行討論研究),提高產(chǎn)品的可靠性、穩(wěn)定性。

    (完)

    總結(jié)

    以上是生活随笔為你收集整理的一种SPA(单页面应用)架构的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。