1. 程式人生 > >關於React的Container&Presentational Component模型結構分析

關於React的Container&Presentational Component模型結構分析

數量 傳遞 index 行數 決定 one pbo widget 一個人


  • react.js
  • javascript
3

之前翻譯了兩篇關於Container&Presentational Component模型的文章,一篇是基礎的Container和Component的定義,另外一篇是進階版,因為翻譯的太爛,感覺有很多錯誤,所以只放原文鏈接。

在這裏我想討論一下我自己對這個模型的一些想法。

註:便於書寫,下面統一把Container&Presentational Components模型翻譯為容器&展示組件模型

註:下面圖片中的components文件夾指的是都是Presentational Components文件夾。


基於容器&展示組件模型的目錄結構

Round 1:

剛接觸React和這個模型的時候,我認為項目的結構應該是這樣子的:
技術分享圖片

  • Containers下面一個jsx文件就代表一個頁面,負責和後臺交互,負責和Redux進行connect,負責傳遞數據給component。在Router裏面放入對應頁面的Container

  • Components下面每個jsx文件就代表頁面裏面所有的渲染的內容,負責渲染,和把從container拿到的數據放到頁面上

  • 頂多把一些基礎的component分離出來,便於以後進行復用

可是才用兩天,就知道這麽搞有多麽坑了。容器組件模型的目的就是復用性,可讀性,可維護性

,然而雖然我們很成功的把後臺交互和頁面展示分離開了,但是看到這麽多代碼放在一起,我沒有感覺到任何復用性,可讀性,可維護性,那麽多代碼,而且都混合了業務邏輯,你讓我怎麽復用,理解,維護?!
技術分享圖片

Round 2:

痛定思痛,決定改一下,針對之前的問題,面向Component做出修改。基本的想法是這樣子的:盡量拆分component,避免把所有的東西都放到一個文件裏面; 拆出可復用的組件,便於組件的復用;拆分邏輯復雜的模塊,增加模塊的可讀性和可維護性;所以關鍵字就是“拆、拆、拆”,拆出大好前程,拆出一片藍天...

所以結構成了這樣...
技術分享圖片

整個代碼結構復雜很多,不過主要的改變就是把基礎組件分離出來(Sidebar, Form之類),每一個頁面也精細化。我們可以更清晰的看出每個文件負責的功能,同時像Sidebar, Form這些組件都可以被多個不同的父組件調用。

當然,這不是結束,雖然上面的方法解決了我們可讀性,可復用性,可維護性,但是也只針對Component的組件,在container中,依然會有很多的代碼堆積在哪裏

而且還有一個很嚴重的問題,先看一個代碼邏輯結構圖:

技術分享圖片

我們現在的數據是通過Container來進行管理的,所以如果Images需要圖片數據,那麽就需要通過Container->Top->Slide->Images這樣進行數據的傳遞,然而這些圖片數據跟中間的組件沒有任何關系,但是他們還必須把數據傳遞給下一級,就像公交車上,從後門遞公交卡到前門刷一樣,中間的人的心理OS其實是:

技術分享圖片

當然代碼是沒有情感的,不會覺得厭煩,但是由於中間每一層都需要傳遞數據給下層,一旦某些數據發生改變,就造成了中間層級的重新render,浪費了瀏覽器性能的同時,增大了調試的難度,而且接收數據的組件還要考慮“中間那些牲口們有沒有動我的數據”?!

Round 3:

所以,為什麽一定要讓頂級的container作為唯一的數據來源呢?

讀了這篇文章就知道,Container是可以包含多個Container和Presentational Component的,所以我們可以適當的提升一些組件成為container。如果老板一個人直接管理很多員工,絕對會亂七八糟的,這個情況下,leader這個角色就應運而生,我們修改一下文件的結構:
技術分享圖片

現在,代碼的邏輯結構就變成這樣子:
技術分享圖片

作為老板的index.jsx,現在主要負責:

  • 頁面的基礎配置,比如頁面的title,比如頁面整體內容結構的配置

  • 頁面全局的數據的獲取和修改

作為leader的Top, Content,現在主要負責:

  • 和index.jsx進行溝通,獲取基礎配置和數據

  • 負責整合需要的container和component

  • 獲取和處理自己對應模塊的數據,並傳遞給下一層級

作為presentational component的組件,就負責獲取數據並進行渲染

這麽做的好處是,分離了原來頂層container的繁重的任務,使代碼更加清晰。同時減少了從數據源到葉子結點的層級,減少了中間層級的數量和不必要的重復渲染。

當然,或許你會覺得之前舉的那個栗子,只有index.js下面有一層container,或許中間節點還是太多。其實container裏面可以包含container,根據需要,可以創建很多container在不同的層級上。

Round 4

View-Container-Presentational Component模型?這個名字是我自己編的,其實是對上面說的結構的一個分離。我也看到過有人說Page-Module-Component模型,反正大概思路都是一樣的。

其實和上面的差不多,但是作為一個大老板來說,肯定不能和一堆下級員工混在一起,位置看起來有點混亂不說,"客人"(比如Router)來了,還不容易認。所以,我覺得應該給老板一個包間,讓老板們在自己的包間中,聽候客人的調遣。所以做出一點改動:
技術分享圖片

Okay,這就是我的最終方案,相比於最早的結構,這個結構更清晰,每個模塊負責的功能也更明確,代碼可讀性、可復用性和可維護性更高。

技術分享圖片

最後自問自答環節

  • Container和Presentational Component的區別?

Container通常會負責和服務端的溝通,還有一些業務邏輯的處理。他們通常只負責獲取數據,處理數據,處理狀態,但一般不知道如何去展示頁面。

Presentational Component通常不知道數據如何獲取,也不知道這些數據是做什麽用的,更不知道怎麽去操作這些數據,他們一般只負責頁面的渲染,把領導給的數據放到對應的位置。

當然一切都不是絕對的,容器組件模型只是一個指導思想,並不是一個硬性的規定,你可以按照自己的需要來進行改變。而且我在上面給了兩個一般,也是說明這些不是絕對的。Container當然可以負責頁面的展示,老板雖然大部分負責方向和管理,但誰規定老板就不能寫代碼的?!同樣,Component也可以負責獲取數據,舉個栗子,一個地圖的component,或者一個天氣預報的component,他們可以從固定的地方獲取數據,並把數據渲染出來。


  • Container可以包含Presentational Component?Presentational Component是否可以包含Container?

Container可以包含Container和Component。

但是Component一般不包含Container,雖然這篇文章的作者最後改口說,Component也可以包含Container,但是個人覺得應該保證component的純凈性,如果包含Container,那麽就不再純凈,或許在復用的時候,會出現偏差的情況。

當然像我之前所說,一切都不是硬性規定,或許也只是因為我接觸的少所以沒有想到Presentational component需要包含container的情況,一切都根據自己的需要進行調整。


  • 如何知道什麽時候要用container,什麽時候要用Presentational component?

一般Presentational component應該是純凈(Pure)的,也就是說父級傳給他的數據不變,那麽渲染出來的結果也不應該發生任何變化。所以當一個組件需要業務邏輯處理,業務數據獲取,那麽可以考慮使用container。如果不需要這些東西,那麽考慮使用Presentational component。當然,像之前所說的地圖,天氣預報,按照邏輯他們也屬於component,但是他們也獲取數據,處理數據。

當不知道該使用container還是Presentational component的時候,那麽或許你在這個時候並不需要去決定這個問題。這種情況下,可以直接使用container來寫,當你的container變得越來越復雜,代碼量越來越多,邏輯越來越不清晰的時候,你就可以考慮分離處更多的container和Presentational component來。


  • 如果這篇文章指導的方向有錯誤,裏面有很多的問題,該怎麽辦?

歡迎指出和討論,一切問題都會認真回答,虛心接受。

如果我也答不出來,那我會當作沒看到...
技術分享圖片

關於React的Container&Presentational Component模型結構分析