一、React+Redux 項目結構
二、Redux 演變過程
- 混亂結構 -> 組件化結構
- 插件引用 -> 模塊化工程
- MVC -> MVVM
- 零散狀態管理 -> 全局狀態管理
- 雙向數據流 -> 單向數據流
前言
? ? ? ?Web 前端近十年的技術革新和巨變可謂是讓人膛目結舌。其外在表現是很多團隊和公司提出了一個又一個新興技術和解決方案,各家爭鳴,百花齊放。而其內在,則是技術專家們在實踐中,對原有問題的不斷改進,是互聯網蓬勃發展過程中的必然經歷。
? ? ? ?接下來的時間,我們主要將探討,一些技術結構形成演變的過程,及其出現的必然性。
1. 混亂結構 -> 組件化結構
? ? ? ?在 組件化結構
問世之前,Web 項目的開發模式通常是簡單粗暴的將問題一鍋燉,最顯著的表現就是 View
部分結構的胡亂拼湊。我們姑且稱其為混亂時代。
? ? ? ?示例中很明顯的可以看到,有數個頁面都需要填充一個新聞列表。在混亂時代,我們會很粗暴的將重復代碼
Ctrl + C
,在需要的另一個頁面中 Ctrl + V
。這樣的復制粘貼,在初步構建頁面應用時顯得非常“快捷、方便”,但是一旦我們發現這個新聞列表無法完全適用于其他頁面而需要調整改動,或者所有頁面的新聞列表都需要變更時(通常幾乎所有項目都會需要這樣的調整和維護更新),工作量就變得無法想像。假設你有 20 個頁面都擁有這樣的新聞列表,那么就需要變更 20 個頁面。? ? ? ?在混亂時代,這樣的應用維護問題也是被人們所詬病的。因此出現了三種解決方案:frame框架、動態網頁和動態渲染插件。
1.1 frame框架
? ? ? ?frame框架主要包含兩種使用方式:frameset結構 和 iframe內嵌結構。由于缺陷太多因此 HTML5
已不再支持使用,因此我們僅進行簡單介紹。
? ? ? ?frameset是一個框架集,主要用于規劃組成當前主頁面的子頁面構成,實際會鑲嵌多個子窗口在主頁面部分,比如我們有這樣的一個 index.html
頁面文件:
<html>
<frameset rows="25%,*">
<frame src="/example/html/frame_a.html">
<frameset cols="30%,*">
<frame src="/example/html/frame_b.html">
<frame src="/example/html/frame_c.html">
</frameset>
</frameset>
</html>
它的呈現結果是:
即便是現在,也許我們還能在一些古老的項目中看到以frameset分層的頁面結構??。
? ? ? ?而iframe,則是一個內聯(行內)框架,其作用與 frameset 相似,也是將其他的頁面窗口引入至一個主窗口中:
<html>
<body>
<iframe src="/example/html/frame_a.html"></iframe>
<p>這里是主頁面窗口!</p>
<iframe src="/example/html/frame_b.html"></iframe>
</body>
</html>
它的呈現效果是:
二者最顯著的區別就是,frameset 作為 frame 的父結構,規范 frame 的呈現方式,frame 不可脫離 frameset 獨立使用。而 iframe,則可以以獨立的標簽方式嵌入到任何地方。其相同點是,都用于嵌入其他頁面窗口。
? ? ? ?利用 frame 框架,我們可以將上述問題這樣設計,新聞列表寫在一個獨立的頁面,由需要的頁面進行內嵌引用:
解決了我們的維護問題,可以一處開發,各處鑲嵌。但使用 frame 過程中,卻會引發其他的問題。主要的缺陷有:
- 樣式/腳本需要額外鏈入,增加請求頻次;
- 搜索引擎難以抓取內容,獲取搜索排名困難;
- 鏈接導航困難,腳本中需要觸發父級頁面(或更高層級)的頁面跳轉;
- ...
1.2 動態網頁
? ? ? ?動態網頁通常是指一些網頁模版,通過編譯之后會生成靜態的 html
頁面。常見的如 php 、jsp、asp、cgi 等。
? ? ? ?動態網頁主要的作用是通過模板實現數據動態化,通常由服務器端進行構建。同時,動態網頁技術往往提供了頁面嵌套方案,拿 jsp
舉例:
<!-- index.jsp -->
<!-- jsp指令:靜態包含 -->
<%@ include file="a.jsp"%>
<!-- jsp動作:動態包含 -->
<jsp:include page="b.jsp"/>
@ include
指令用于將希望包含的頁面在編譯之后,與主頁面生成一個頁面,它發生在 Servlet
生成靜態頁面的過程中,index.jsp
和 a.jsp
將合并成一個 Servlet
然后產生靜態頁面。而 <jsp:include />
動作標簽的文件嵌入,則發生在請求過程中,index.jsp
和 b.jsp
將分別代表兩個不同的 Servlet
然后進行頁面嵌套。后者往往用于嵌入改動較多的動態頁面內容,可以進行參數傳遞;而前者則往往用于直接嵌入靜態文件,無法直接傳遞參數。但無論使用哪種方案,都可以解決我們的新聞列表可維護性的問題:
動態網頁技術很大程度上解決了項目維護性的問題,通過訪問者或訪問方式的不同,生成不同的呈現視圖。動態網頁技術的發展和應用歷史悠久,在客戶需求逐漸從需求供給到體驗至上的今天,動態網頁的缺陷也愈發明顯。主要表現在:
- 訪問速度低于靜態網頁,請求需要通過服務器端才能產生視圖內容;
- 對搜索引擎不友好,搜索引擎抓取數據困難;
- 視圖表現耦合度高,第三方編程語言侵入性高;
- ......
1.3 UI渲染方案
? ? ? ?時間的車輪依舊在向前滾動,技術的改進也依舊在邁步。許多團隊認為,視圖呈現應當與數據計算剝離,Web前后端分工應當更加明確。我們在專注于處理一件事時,才會表現的更加高效精準,技術升級也可以分頭進行的更加深入徹底。
? ? ? ?隨著 Ajax
異步加載技術的成熟應用,Web視圖層逐步開始脫離動態網頁技術,所有計算好的數據都當作是數據模型(Model),服務器端的工作僅僅作為提供數據模型(Model)而不用考慮視圖表現,Web前端的工作則是負責處理這些數據模型(Model),將其渲染成相應視圖。在這個過程中,Web前端涌現了大量的UI渲染插件和解決方案,以便于從另一個層面解決視圖可復用性問題。
? ? ? ?在這個階段,主要的解決思路是這樣的:由UI插件定義視圖表現所需的 html
標簽結構 和 css
樣式結構,通過插件對外暴露的調用方式,以參數的形式接收數據(通常數據格式必須符合UI插件所制定的數據規范),數據通常是一個 js
對象(通常由ajax獲取的數據進行 JSON
反序列化)。代表產品有 jQuery-ui、Bootstrap、ECharts、Layui等等,零散的視圖渲染插件更是數不勝數。應用過程大致變成了這個樣子:
1.4 組件化應用
? ? ? ?組件化應用
的構思源于UI渲染插件,是基于該方案的擴展實踐,二者的關系是點和面的關系。如果我們將小規模的渲染插件當作是通過數據構建某一個視圖功能,那么組件化框架的作用便是將全站的所有內容統一當作是零散的視圖功能,通過設計組裝進行全站拼接。優勢在于,全站整體視圖模型都可以復用,并且不限于一個項目。代表產品有React、Vue、Angular。
? ? ? ?組件化應用
通常用于在一個單頁面中拼裝復雜的頁面結構。以 React
為例,我們會逐層級的將子組件于網頁模版 index.html
進行合并:
<!-- index.html 主頁模版 -->
<body>
<div id="root"></div>
</body>
頂層組件引用:
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root'));
組件的設計是一個由上自下的樹級結構,越向下深入,可拼接的子組件可能越豐富:
// App.js
import React, { PureComponent } from 'react';
import './App.css';
import Topbar from './components/Topbar';
import MainPanel from './components/MainPanel';
class App extends PureComponent{
render(){
return (
<div className="App">
<header className="App-header">
<Topbar items={this.props.items} />
</header>
<MainPanel />
</div>
);
}
}
組件化UI結構如圖所示:
組件化的UI工程具有如下優勢:
- 高內聚性:每一個組件都具備完整的局部功能實現。
- 低耦合度:組件之間是互相獨立的,通過狀態或參數進行交互。在團隊協作的過程,也不會產生過多的沖突和影響。
- 高可復用性:在理想狀態下,每一個組件相當于是一個純函數,渲染結果單一,可在應用多處進行復用。