如何避免 Vue 的漏洞破壞單向資料流

語言: CN / TW / HK

highlight: vs2015

小知識,大挑戰!本文正在參與“程式設計師必備小知識”創作活動。

導讀

一直有人說,“React 適合中大型專案,Vue 比較適合中小型專案”,但是一直也沒人說清楚為什麼,所以筆者之前對這種言論是直接忽略的。最近在研究如何提升專案的可維護性,發現了 Vue 設計的一個漏洞會破壞單向資料流。如果以此為論據說 Vue 不適合大專案,勉強能夠附會上。

澄清一點,筆者沒有任何褒貶傾向,甚至感情上更傾向 Vue 多一點,所以大家客觀看待這個問題就好,題目也只是想多博一些眼球而已。

正文

為什麼要單向資料流

This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it.\ 這就是為什麼稱 state 為區域性的或是封裝的的原因。除了擁有並設定了它的元件,其他元件都無法訪問。

請正確理解“訪問”這個詞,實際上換成“修改”更容易理解一些。畢竟 state 可以作為 props 傳給子元件,子元件也可以使用它,但是不可以修改它。

這裡又有一個疑問了,明明很多場景子元件都是可以修改父元件資料的,最典型的就是 input 類的表單元件。我們具體看下程式碼: html <Input :value="value" @update-value="v => { this.value = v; }" /> 請注意,Input 有能力修改父元件的 value,是因為父元件給它傳了一個 updateValue 的 emit,它能做的只是“使用”這個方法而已,如果父元件不給它傳類似的方法,它是無法對 value 做任何修改操作的。

這就保證了在閱讀程式碼時你會對子元件的行為有所預期,更具體點說就是,如果我只給子元件傳了 value 而沒有傳 updateValue 的話,子元件就不可能修改 value,debug 就可以忽略該子元件。這就是所謂的“確定性”。

為了加深印象,我們再看一個例子: html <Parent> <ChildA :value="value" /> <ChildB :value="value" /> <ChildC :value="value" /> <ChildD :value="value" @update-value="handleUpdateValue" /> </Parent> 多個子元件都使用了 value,如果有單向資料流的限制,我們可以確定,只有 ChildD 有能力修改 value。如果沒有單向資料流的限制,我們要如何定位是哪個元件修改了 value 呢?恐怕只能進入每個子元件的程式碼中去查看了,如果是個比較複雜的專案,這種情況會“套娃般”的出現,這對於專案維護來說是災難性的。

Vue 的漏洞

Vue 在文件中強調了單向資料流的重要性,但是卻留了一個漏洞,並且沒有給出解決辦法。

提示\ 注意在 JavaScript 中物件和陣列是通過引用傳入的,所以對於一個數組或物件型別的 prop 來說,在子元件中改變變更這個物件或陣列本身將會影響到父元件的狀態。

來看一個例子: ```jsx // Parent

// Child

``` 如上例所示,可以“強行”打破單向資料流,這就是 Vue 中存在的漏洞。

如何避免漏洞

目前沒有絕對硬性的辦法來避免這個漏洞,只能通過工具儘量規避。比如: - 設定 eslint 規則,並要求全部開發者啟用; - 進一步的,在 CI/CD 中進行 lint 檢測,不通過不能合併程式碼; - CR 的時候注意

不管採取何種方式,都需要一定的成本,好在靠工具可以算是“一次投入,終身受益”的做法。即便如此,還是不能夠徹底解決這個問題,除非 Vue 的更新機制做出改變,但就目前來看,這基本上是不可能的。

面對上例,正確的做法應該是顯式的傳入修改 dataemit,並且 clone 後進行修改,保證 immutable,如下: ```jsx // Parent 改為:

```

結語

一些人可能認為有點小題大做,覺得偶爾破壞一下單向資料流,帶來了操作的便利,又有什麼不行的呢?

的確,一切脫離實際情況的討論,都是耍流氓。如果你的專案並不大,也就區區幾個頁面,然後大費周章的又要搞 CI/CD,又要普及規範標準,ROI 實在是太低了。但是對於中大型專案,保持絕對的單向資料流,是非常必要的。就像上文提到的,在複雜的專案中,一個小問題可能“套娃”一樣的出現,然後帶來災難性的後果。

最後以一句名言來結尾:

“千丈之堤,以螻蟻之穴潰;百尺之室,以突隙之煙焚。”——《韓非子·喻老》