基於Vue.js的企業級前端代碼架構設計設想

    在前端架構設計這塊也已經工作了一段時間,也翻遍了很多書籍,但是就目前來說筆者還是沒有看過真正把前端架構講好的書,加上現在前端技術的發展誕生了許多新的框架,如:vue、react、angular,這也越來越淡化了前端工程師們對架構設計的積極性,本着按這些技術本身的框架寫就好,不用管其他,怎麼方便怎麼來的思維,最後帶來的後果就是代碼只有自己看的懂,一處修改處處bug,不利於多人協作,代碼邏輯不清等各種常見問題,鑑於這些原因筆者還是想着大膽的提出一些自己的想法,當然也僅限於筆者自己的一些拙見,歡迎大家一起加入前端架構設計的探討,打造更加美好的前端生態。

vue.js是目前最流行的一套用於構建用戶界面的漸進式框架。官網的介紹如下:

Vue (讀音 /vjuː/,類似於 view) 是一套用於構建用戶界面的漸進式框架。與其它大型框架不同的是,Vue 被設計爲可以自底向上逐層應用。Vue 的核心庫只關注視圖層,不僅易於上手,還便於與第三方庫或既有項目整合。

這裏明確說了一個問題,vue的核心庫只關注視圖層,那麼我們在寫一個前端系統的時候是否只有視圖層,我想不是的,接觸過三層架構的人可能知道,它應該還有業務邏輯層和數據訪問層,也就是說vue並沒有爲廣大前端工程師去考慮如何設計業務邏輯層和數據訪問層,這些就是我們在設計前端架構的時候需要考慮的事情了。

爲了在頁面上顯示一個“hello world”,我們可以直接寫的html的元素裏面,如:

<div>hello world</div>

還可以在html頁面中用JavaScript代碼來實現,如:

<div id="msg"></div>
<script>document.getElementById('msg').innerHTML="hello world"</script>

還有一個辦法是什麼呢,我們可以創建一個hello.js,然後在html頁面中引入這個hello.js,如:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
	</head>
	<body>
		<div id="msg"></div>
		<script type="text/javascript" src="hello.js" ></script>
	</body>
</html>

//hello.js
document.getElementById('msg').innerHTML='hello world';

    這應該就是目前比較常見的幾種實現方式了,這三種方式基本可以代表系統不同階段的設計問題了,如果我們的系統還非常非常簡單,我們可能會選擇第一種,雖然它的靈活性是最低的,但是在系統還只是一個demo的時候,它也是最快的,這一點我相信大家應該是認可的,可是現實開發中,我們的系統肯定不會簡單到這種地步,我們需要保證一定的靈活性,所以第三種應該是最常見的方式,至少它做到了將頁面與js代碼分離,這在開發過程中會更方便我們實現某些需求,這就是一種架構變化的體現。

隨着我們系統一直變大,然後人們發現這種方式也不是最好的,雖然解決了一部分問題,但是代碼還是會混亂,還是不好維護,然後我們就企圖一直優化一直優化,然後話題回到我們要講的vue,如果用vue來實現上面說的功能,它可以這麼來實現:

<div id="app">
  {{ message }}
</div>
new Vue({
  el: '#app',
  data: {
    message: 'hello world'
  }
})

是一種類似模板的方式實現的,但是不同的是vue可以實現雙向數據綁定,頁面與數據之間的維護由vue來實現,我們不再需要使用getElementById來獲取元素,再修改元素,這就大大減輕了我們在html元素和數據之間的維護成本,再加上vue提供的一系列其他操作,我們在開發前端的過程中就像多了一把神器一樣。

但是上面的方式並沒有解決一個問題,就是當我們的系統比較大的情況下,html元素可能會一層嵌套一層,而且量也非常大,當我們看到那麼多的html代碼的時候,那將是一件非常頭疼的事,而且頁面上肯定會有很多冗餘的html代碼,我們可能更希望的是修改了一處相同,其他相同的地方也能跟着變化,而不是打開系統的每個頁面去查找相同的地方,然後修改,這種涉及到的變動太大了,維護成本那是相當的高,所以vue提供了這樣一個功能:組件,再來一段官方解釋:

組件 (Component) 是 Vue.js 最強大的功能之一。組件可以擴展 HTML 元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue.js 的編譯器爲它添加特殊功能。在有些情況下,組件也可以表現爲用 is 特性進行了擴展的原生 HTML 元素。

這樣就比較方便了,我們可以將很多頁面重複的地方進行統一劃分到一個組件中,也可以將一個頁面中的元素劃分到幾個組件中,只要你覺得有這個必要。

好了,vue能給我們解決的視圖層的問題好像基本也就差不多了,貌似我們已經可以憑藉着vue帶給我們的便利馳聘前端沙場了。那麼問題來了,我們在開發一個項目的時候,是否每一個數據都像hello world這麼簡單,我想沒有人會告訴我真的就這麼簡單吧,應該說前端的數據絕大部分都是發送請求到後臺,然後由後臺返回。這中間會涉及到一些問題,前端需要發送請求到後臺,後臺返回的數據卻不一定是前端想要的格式,尤其是現在主推前後端分離模式,這種情況肯定就更常見了,而且有些系統肯定還會遇到這樣的情況,數據不是從一個後臺接口系統過來的,可能會用上其他的三方系統提供的接口,比如:百度提供的某些API,這個時候更是無法限定後臺數據返回格式。當然了,你也可以跟技術負責人說:我就要後臺給我返回我要的格式,需要請求三方系統的接口的時候,讓後臺先請求一遍,轉換成我想要的格式再返回給我,這不失爲一個辦法,但是呢,我想說的是,如果我是你們公司的技術負責人,不好意思,這個要求我沒辦法滿足你,我們來算一筆賬:

假設公司現有後臺系統能處理的QPS爲500(請求數/秒),即平均每個請求耗時爲:
1000/500=2ms,
現在需要將一個前端可以處理的任務放到後臺系統進行處理,假設處理這個任務不管前端還是後臺都是耗時2ms,
那麼理想狀態下後臺系統的QPS將會變成:
1000/4=250(請求數/秒)
這個結果直接導致後臺系統處理數據的能力下降一半,如果後臺系統必須要滿足QPS爲500,
那麼後臺系統就需要在現有基礎上至少增加一倍的配置來應對該變化,後臺系統的維護成本也將相應的變高,
而如果把這個任務放在前端處理,前端請求耗時將會是在原有耗時基礎上增加2ms左右,不會出現疊加效果,對整個系統的影響可以忽略不計。

看了這個數據之後,相信我們應該可以統一一個觀點,那就是前端肯定不會簡單到只寫頁面,肯定會涉及到前端有自己需要處理的業務數據。

    筆者曾經在做項目的過程中接觸過一個前輩們寫的js代碼,裏面多數都是涉及到需要前端處理的業務數據之類的操作,其中就有一個js文件中的代碼行數達到了數萬行,而且這樣的文件還不止一個,然後帶來的問題就是,因爲文件代碼量過大,有些不規範的寫法無法查找出來,導致文件無法被壓縮,而且代碼複雜度相當高,修改代碼的時候很容易就出現一處修改處處bug的情況,導致項目嚴重延期。前期寫的時候大家都可以隨心所欲的寫,寫的很快,後面就變成了系統各種問題頻發,嚴重拖延系統開發進度,無法應對需求變更。

    談到這裏,我們的重點應該可以出現了,就是該如何去設計這個架構,讓前端代碼能看起來更加清晰易維護,便於多人協作開發,降低系統複雜度。前面我們說了,vue能幫我們實現的更多偏向於視圖層,如果我們將業務代碼與視圖層代碼全部寫到一個vue文件中,那麼這個文件將可能變得非常龐大,同樣導致系統的複雜度變高,變得難以維護,業務代碼之間的複用性也會變低,導致冗餘代碼過多。

    有人可能就會問了,廢話了一大堆,能不能來點實在的,你說說怎麼解決吧。在這裏,筆者就上面的一些問題,在這裏提出一套自己設想的架構方案,希望能解決目前大部分項目中遇到的這些問題。

    首先還是利用分層的思想,實現代碼的邏輯分層,然後結合vue和ES6的優勢,將代碼模塊化,使得整個代碼結構清晰明瞭,易維護。首先筆者將各個分層結構展示給大家:


該結構整體是由vue-cli進行創建,vue-cli生成的代碼在這裏就不做過多的解釋,我們重點放在src文件夾中。

assets:

    靜態資源存放目錄。主要就是存放系統需要用到的一些靜態資源文件,如果有需要的情況下可以在裏面建立子文件夾,便於區分靜態資源屬於哪個頁面或者哪個模塊。

components:

    組件存放目錄。主要存放各個模塊需要用到的vue組件,組件的劃分應該有:公共組件、模塊組件,命名規則可以如下:

    +components
        -common

        -module(模塊名稱,如:login)

    組件的命名規則應該讓人能很容易判斷出來該文件的類型屬於組件,如:HelloWorldComponent.vue。

consts:

    靜態變量存放目錄。主要存放系統需要利用到的各種靜態變量,如:後臺請求的路徑。在其他語言開發中,有一點是非常忌諱的,那就是魔法值,魔法值會使代碼的可讀性降低,增加維護成本;而且一旦變量不只在一個地方用到,在多處或者多個文件,一旦涉及到該變量的變更就會導致多處修改,又增加了維護成本,容易導致新的Bug出現。

下面給出一個命名規則示例代碼:

/**
 * 以類的方式進行封裝,類名的命名規則爲變量作用類型+Constant,容易一眼看出來該類的用處和作用範圍
 */
class RequestConstant {
	/**
	 * 根據process.env.NODE_ENV的值確定網關地址,可以保證兩種環境下的切換,避免了因爲人爲忘記修改該值,導致部署生產環境時出錯
	 */
	static DOMAIN_URL = process.env.NODE_ENV == 'development' ?
		'我是開發環境下的配置' : '我是生產環境下的配置';
	/**
	 * 請求的路徑
	 */
	static SAY_HELLO_PATH = '/mock/sayHello';
}

export default RequestConstant;
libs:

    庫文件存放目錄。在開發過程中總會遇到需要引入一些其他外部的庫,而這些庫在npm倉庫中卻又不存在,這個目錄的用處就主要用來放置這些庫文件。

mock:

    mock模擬數據接口目錄。用於提供模擬數據,方便測試。

plugins:

    vue插件存放目錄。主要存放一些比較通用的插件,命名規則可以如下:

    插件用途+Plugin,如:ErrorMsgPlugin.js

router:

    vue路由文件存放目錄。一般一個就夠,當然,如果系統夠大,那麼拆分也是有必要的,如果拆分路由文件的話,建議採用模塊名+Router的方式。

services:

    業務邏輯層。這層的主要目的就是爲了將視圖層與業務邏輯層解耦而劃分出來,因爲vue所代表的視圖層已經承擔了不少視圖層的工作,不應該還讓視圖層承擔業務邏輯層的工作,如:

    視圖層需要加載後臺請求的數據,請求之後還需要做判斷是否成功,返回的結果是否滿足視圖層的需要,如果不滿足是否需要再做轉換,這一系列的業務操作如果放在視圖層,那麼視圖層就會變得非常龐大。業務層的文件劃分應該更偏向於模塊,而視圖層文件則是根據項目需求的頁面來定,一個模塊可以只有一個頁面也可以同時擁有幾個頁面。業務層應該具備這種複用性,方便模塊之間調用,如果將業務層代碼直接寫入視圖層,那麼業務層的複用性直接就被降低,還會讓視圖層顯得非常臃腫,代碼維護難度也隨之上升。假設A、B兩個頁面需要同時調用一段相同的方法,而這個代碼不會涉及到任何視圖相關的內容,如果在A、B頁面裏面都寫上這段代碼,那麼一旦涉及代碼修改,帶來的變動就不再是單純的一個頁面,而是A、B兩個,倘若這段代碼使用率極高,那麼帶來的問題將會是巨大的。

    在設計的過程中,我們還應該考慮兩個原則:單一職責原則、最少知識原則。如果視圖層與業務層耦合,那麼視圖層和業務層的職責不在單一,同時也違背了對象之間最少了解的原則。

下面給出一個業務層設計參考代碼:

import RequestUtil from '../utils/RequestUtil'

import RequestConstant from '../consts/RequestConstant'
/**
 * 模塊名+Service的命名規則
 */
class HelloWorldService {

	/**
	 * 初始化實例變量
	 */
	constructor(_this) {
		this.requestUtil = new RequestUtil();
		this._this = _this;
	}
	/**
	 * 業務邏輯層的方法
	 * 視圖層只需要知道調用該方法就行,至於該方法裏面的代碼是如何成功實現返回是不需要視圖層關心的
	 */
	sayHello() {
		console.log(this._this);
		return 'hello';
	}

	/**
	 * 該方法涉及到了異步回調,回調函數只需要關心回調的結果是視圖層想要的
	 * RequestConstant.SAY_HELLO_PATH接口路徑變量其實是業務層需要知道的,視圖層則是不需要關心數據是從哪個接口過來的
	 */
	sayAsyncHello(callBack) {
		this.requestUtil.getRequest(RequestConstant.SAY_HELLO_PATH, {}, function(data) {
			let newObjArr = new Array();
			newObjArr.push(data); //轉換數據格式,滿足視圖層需要
			callBack(newObjArr);
		});
	}

}

export default HelloWorldService;

store:

vuex狀態管理器。如果模塊較多可以按模塊劃分子文件夾,子文件較多的情況下命名規則建議:模塊名+Store

utils:

工具類存放目錄。主要放一些系統需要用到的通用小工具。如:http請求工具類、日期格式化工具類等。

views:

視圖文件存放目錄。如果視圖較多可以劃分模塊子文件夾。


以上內容都是筆者自己在進行項目開發過程中總結的一些經驗,當然了,有可能會存在一些不太合適的地方,歡迎大家一起對前端架構進行探討,創造更加完美的前端架構設計方案。

示例源碼下載地址:http://download.csdn.net/download/u010520626/10265009







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章