當前位置:
首頁 > 科技 > 聊聊vue組件開發的「邊界把握」和「狀態驅動」

聊聊vue組件開發的「邊界把握」和「狀態驅動」

前言

今日早讀文章由前端早讀課專欄作者@呂大豹分享。

正文從這開始~

vue有著完整的組件化開發機制,但是官網只給了開發的方式,對於開發規範以及組件化開發的最佳實踐,還需要我們來摸索。本文就平時開發中的經驗來談談「把握邊界」和「狀態驅動」這兩個話題。

邊界把握

邊界把握其實很好理解。在模塊化編程中,我們通常要定義好一個模塊的功能邊界,做什麼,不做什麼,從外部接收什麼,向外部提供什麼。在vue的組件化系統之下,這些問題又更具體一些,需要我們細細把握。

劃分業務邏輯

這個原則適用於任何模塊化開發,一個組件要負責哪些業務,在開始寫之初就應該非常明確,否則邊界就容易模糊了。舉個例子,頁面上有個彈出層,裡面會顯示用戶名。那麼在彈出層組件中,需要有username這樣一個數據嗎?

很顯然是不需要的。彈出層的任務就是:彈出、關閉、顯示內容。至於是什麼內容,組件並不需要關心。所以我們頂多會定義一個通用的content欄位,或者乾脆用slot。

組件簡單了尚且容易把握,當業務較複雜的時候就需要好好斟酌了,這是個基本思維。

父子通信的注意點

這個話題想必大家不陌生,你甚至可以朗朗上口的背出來:父通過props傳遞數據給子,子通過emit發送消息給父。這有什麼好說的呢?

props容易忽略的問題在於,當父組件傳遞一個對象給子組件時,這個傳遞就不再是「單向」的。因為子組件拿到的是一個引用,當子組件修改了該對象上的屬性值,父組件的數據也會相應變化。數據流就變成了雙向的,子組件是不應該直接修改父組件的數據的。所以我們要在props中只傳遞簡單值。對象、數組這樣的引用類型要避免傳遞。

為了保證props傳遞的數據類型,推薦在定義props的時候寫明類型和默認值:

props:{

name:{

type:string,

default:

}

}

關於子組件emit消息,我之前也談到過一個原則,子組件需要對外通知的是「我發生了什麼」,而不是「你去幹什麼」。這只是語義上的一個差別,往小里說只是一個命名的事。但從邏輯上來講,缺是一個邊界把握不清楚的行為。

這也是很容易想通的,如果讓子組件決定父組件的行為,那麼他們在邏輯上便耦合了。舉個例子:點擊彈出層上的確定按鈕,父組件去請求商品列表。那麼子組件發出的消息應該叫"confirm"或"ok",而不是叫"request-product"。

避免全局操作

我們在平時的編程中,通常會用一些BOM的方法如history,或者是使用document上的方法,這類訪問全局對象的行為,我也視之為「越界」行為。畢竟已經跨出了組件之外了。

一旦一個組件有操作全局對象的行為,那它就可以被認為有潛在威脅。所以通常應該注意以下方面:

用this.$el.querySelector代替document.querySelector,不要去查詢組件外的DOM

用到的BOM介面,統一封裝成模塊,在組件中引入使用

本地存儲也進行一次包裝,例如,把localStorage相關操作統一封到一個storage.js模塊中

子組件盡量避免監聽window的事件,可讓最外層組件監聽,然後傳遞數據

vuex的狀態管理

如果你使用了vuex,那麼store中的數據管理也是需要留意的。vue完美集成了vuex這樣一個全局狀態管理工具,可以在任何組件中通過this.store訪問/提交狀態。

既然是全局狀態,我們擔心的又來了,組件內操作全局的東西,豈不是一次越界行為?而且各種commit散落在各個組件中,將來找起來豈不是很麻煩?

我的做法是這樣的,單獨定義一個模塊,姑且叫做storeMonitor吧,所有修改全局狀態的方法全部定義在這裡面,組件藉助這個storeMonitor去修改store中的數據,相當於是一個門面模式。這樣的好處是,組件間接地去修改全局狀態,相當於建立了一個隔離層。另一方面,所有的commit操作都集中在這個文件中,一目了然。

狀態驅動

何為狀態驅動

狀態驅動也可以說是數據驅動,只不過數據是具體存在的(比如一個js對象),「狀態」是抽象出來的一種描述。狀態驅動就是指代碼邏輯集中在數據操作, 而不是DOM操作以及樣式操作。

舉個例子,一個表單提交按鈕,不可點擊的時候要灰色背景,可點擊的時候要藍色背景。那麼我們通過一個js變數disabled來控制,大致代碼如下:

提交

這不就是mvvm雙向綁定的終極奧義嘛,說了半天廢話。

其實上面的代碼是有問題的。如果你隱隱覺得bg-gray、bg-blue這倆名字有點彆扭,甚至那個disabled也看著不順眼,那麼你有可能要理解我想說什麼了。

問題在哪裡呢?想想這段代碼表達了什麼語義。「按鈕不可用的時候給灰色背景,可用的時候給藍色背景」,這,明明還是DOM世界的說法嘛。只是包上了雙向綁定的皮而已,根本不是狀態驅動。

而狀態驅動的精髓,是要保留業務邏輯,消滅和DOM、樣式有關的一切思維。而我們真正的業務邏輯可能是什麼呢?「校驗通過的時候讓按鈕可用,不通過的時候失效」。所以,正確的代碼應該這麼寫:

提交

什麼?別騙我!你只是改了命名而已。

我沒騙你,「命名即思維「,這是我一貫堅持的準則,胡亂給變數命名的人必然有一顆亂成麻團的腦袋。等你明白了捨生取義的道理,自然會回來和我一起念:「命名即思維」。

把頁面上的所有功能都完整的抽象成狀態,那就是狀態驅動了,而這狀態,不是樣式的狀態。那麼,如何擁有正確的狀態驅動思維呢?答案就是:面向對象。

面向對象的思維

不看錶象,看抽象。前端所要有的面向對象思維差不多就是這樣。

表象是啥呢?是輸入框,是彈出層,是列表,是表格,是花里胡哨的各種顏色。

抽象是啥呢?是用戶名,是密碼,是登陸狀態,是各種業務數據。我們把頁面的內容抽象成對象的屬性,把交互抽象成對象的方法。

還是舉個例子吧,看下面這個醜陋的原型圖:

那我們抽象出來的對象應該大致這樣:

{

businessOptions:[],

currentIndex:,

selectedList:[],

select:function(index){//選中操作 }

remove:function(index){//刪除操作 }

}

我們的代碼邏輯應該是切換currentIndex,以及調用select方法來添加選項到selectedList數組。如果你想用active來表示當前激活的tab,或者是用left/right表示左邊/右邊兩欄,那就大大的犯了表象主義錯誤。

在寫小遊戲的時候可能用到的面向對象思維較多,組件化開發中,也應當用這個思維去做整體設計。一個組件就是很具象的實體,所以要將之「物件化」。

css也要「狀態」

css作為樣式的描述語言,其命名方式以及組織方式有多種規則。在狀態驅動的開發思維下,我傾向讓css也具有「描述狀態」的能力。看下面的一段sass 代碼:

.sidebar{

position:absolute;

bottom:;

width:80%;

&.show{

display:block;

}

&.hidden{

display:none;

}

.btn{

display:inline-block;

width:200px;

height:20px;

}

&.open{

left:;

.btn{

background-image:url(left.png);

}

}

&.close{

left:-80%;

.btn{

background-image:url(right.png);

}

}

}

光看css,不看js代碼的情況下,我們已經可以得知界面的展示邏輯了:有一個名為sidebar的側邊欄,它有四種狀態,分別是:show、hidden、open、close。sidebar下有一個按鈕btn,它在sidebar打開的時候是向左的背景圖,在sidebar關閉的時候是向右的背景圖。

這樣一套結構清晰,語義明確的css規則,能夠幫助我們很快理清頁面邏輯,別人在看你的代碼的時候一目了然。上面只是一個簡單的例子,實踐的時候會有複雜的場景,可根據具體功能劃分出各自的作用域(嵌套語法),稍微需要花時間去設計,換來的是清晰的代碼。

不需要動態創建組件

用mvvm框架去寫彈框組件的時候,往往會有這樣一個困惑:在jquery時代,我們通過 $.msg( 內容")這樣的方式調用彈框,此時在頁面上動態創建一個節點,關閉彈框的時候再把節點移除。習慣於此,我們很希望能用同樣的方式來處理彈框。

當然這在vue中也是可以做到的,方式就是動態創建標籤,並且動態new一個組件實例去渲染它,在監聽到close消息時,把這個節點手動刪掉。大體代碼如下:

constMessageConstructor=Vue.extend(alert);

constMessage=(config)=>{

instance=newMessageConstructor({

el:document.createElement( div )

});

document.body.appendChild(instance.$el);

Vue.nextTick(()=>{

instance.show=true;

instance.content=config.content|| ;

instance.type=config.type|| danger ;

instance.$on( close ,function(){

this.show=false;

document.body.removeChild(this.$el);

});

instance.$on( confirm ,config.onConfirm)

});

}

exportdefaultMessage;

這樣的方式確實可以實現,但是其思想卻是和狀態驅動違背的,某個應用在某時某刻彈窗,這可以理解為這個應用的狀態,我們只需用一個變數來標記該狀態即可,犯不著手動創建節點、刪除節點這麼大動干戈。事實上vue作者也推崇這樣來處理彈窗,節點始終掛載在頁面,需要彈的時候給顯示即可。

本篇結束,以上是筆者在實際開發者總結出的最佳實踐,當然這只是一個開發模式,並無對錯。大家可以參考,或引發其他思考。

關於本文

作者:@呂大豹

原文:http://www.cnblogs.com/lvdabao/p/vue-component.html

點擊展開全文

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端早讀課 的精彩文章:

編寫現代 JavaScript 代碼
開箱即用的網站可訪問性提升指南
這是一條送書的頭條
這首歌送給程序員
WebView性能、體驗分析與優化

TAG:前端早讀課 |

您可能感興趣

「錸」真的有助於航空發動機的開發嗎?
漫談Vue組件庫開發
靈感來自Switch!微軟正在開發一款移動設備的手柄
移動端開發的一些小技巧
Oculus開發新型手動追蹤系統
微軟的開發流程是否有問題?從Windows功能更新事故頻發說開去
傳Google正在開發自己的區塊鏈相關技術
Lyft正著手開發自動駕駛系統
迅雷發布了自主研發的TCFS協議,有意轉型區塊鏈開發的必看
Steam暫停「擦邊」遊戲發售 正開發新審核工具
《異形:隔離》開發組正在開發一款戰術FPS遊戲
Uplay,Playu,聊聊著名的旅遊模擬器開發大廠,育碧的發家史
傳蘋果在為iPhone開發非接觸式的手勢控制和曲面屏
Cocos 引擎開始探索區塊鏈遊戲開發,我們是認真的!
《恥辱》系列真的涼了!開發商Arkane已確認暫停開發
如何從零開始用Keras開發一個機器翻譯系統!
遊戲伺服器開發的基本體系與伺服器端開發的一些建議
使用Arduino開發板製作自動空調溫度控制器
如何使用Arduino開發板控制無刷直流電機
使用谷歌的Flutter進行真正的跨平台移動開發