學(xué)習(xí)筆記
原文地址:antDesignPro使用心得,快速開發(fā)必備。
https://www.52pojie.cn/thread-696928-1-1.html
(出處: 吾愛破解論壇)
摘要介紹
Ant Design Pro
是一個基于Ant Design
搭建起來的模板項目。它提供了兩個主要布局:BasicLayout
、UserLayout
,在布局基礎(chǔ)上制作了20多個基礎(chǔ)頁面,詳情見模板
介紹段落。
Ant Design Pro
主體代碼使用ES2015+語法規(guī)則,因此在閱讀源碼時遇到不理解的語法,可前往相關(guān)主頁進行查詢。在學(xué)習(xí)筆記中,除非遇到關(guān)鍵的語法點,否則不對語法進行額外說明。
在接下來,將按照以下幾個方面介紹項目的相關(guān)代碼。
......
目錄結(jié)構(gòu)
├── mock # 本地模擬數(shù)據(jù)
├── node_modules # 依賴庫
├── public
│ ├── favicon.ico # Favicon
│ └── index.html # HTML 入口模板
├── src
│ ├── common # 應(yīng)用公用配置,如導(dǎo)航信息
│ ├── components # 業(yè)務(wù)通用組件
│ ├── e2e # 集成測試用例
│ ├── layouts # 通用布局
│ ├── models # dva model
│ ├── routes # 業(yè)務(wù)頁面入口和常用模板
│ ├── services # 后臺接口服務(wù)
│ ├── utils # 工具庫
│ ├── g2.js # 可視化圖形配置
│ ├── polyfill.js # 兼容性墊片
│ ├── theme.js # 主題配置
│ ├── index.js # 應(yīng)用入口
│ ├── index.less # 全局樣式
│ └── router.js # 路由入口
├── tests # 測試工具
├── .editorconfig # 編輯器配置
├── .eslintrc # js代碼檢測工具
├── .ga # 未知
├── .gitignore # git版本配置
├── .roadhogrc # roadhog配置
├── .roadhogrc.mock.js # roadhog的模擬配置
├── .stylelintrc # css代碼審查配置
├── .travis.yml # travis持續(xù)構(gòu)建工具配置
├── package.json # web前端項目配置文件
├── README.md
└──
項目中的幾項配置文件
Q:為什么要用
編輯器配置
?A:當(dāng)多人共同開發(fā)一個項目的時候,往往會出現(xiàn)大家用不同編輯器的情況。就前端開發(fā)者來說,有人喜歡 Sublime,有人喜歡 Webstorm , 也有人喜歡 Atom,還有人喜歡 Vim,HBuilder 等等。各種不同編程語言的開發(fā)者喜歡各種不同的編輯器。問題來了,如何讓使用不同編輯器的開發(fā)者在共同開發(fā)一個項目時“無痛”地遵循編碼規(guī)范(編碼風(fēng)格)?
Q:怎么用
編輯器配置
?A:在項目根創(chuàng)建一個名為 .editorconfig 的文件。安裝與編輯器對應(yīng)的 EditorConfig 插件。詳情參考編輯器配置
Q:
代碼檢查工具
是什么?A:js的代碼檢查工具是ESLint,css的代碼檢查工具是StyleLint,相應(yīng)的配置項保存在
.eslintrc
和.stylelintrc
文件中。一般來說,都不需要進行修訂,深入學(xué)習(xí)可參考js代碼檢測工具、css代碼審查配置
Q:
git
是什么?A:
git
是版本控制工具,類似的工具還有svn
。有關(guān).gitignore
的配置參考git版本配置
Q:什么是
持續(xù)集成系統(tǒng)
?A:對個人而言,就是讓你的代碼在提交到遠程——這里是GitHub——后,立即自動編譯,并且在失敗后可以自動給你發(fā)郵件的東西。當(dāng)然,除了編譯,還能做自動化測試、自動部署等。對團隊或企業(yè)而言,這意味著更多的東西,是敏捷開發(fā)的一種踐行。
travis
是一種持續(xù)集成工具,企業(yè)中還有用jenkins
的。有關(guān)travis
的更多介紹見travis持續(xù)構(gòu)建工具配置
Q:什么是
roadhog
?A:這是一個 cli 工具,提供
server
、build
和test
三個命令,分別用于本地調(diào)試和構(gòu)建。更多介紹見roadhog配置
Q:web前端項目的配置是package?
A:每個web前端項目的根目錄下面,一般都有一個
package.json
文件,定義了這個項目所需要的各種模塊,以及項目的配置信息(比如名稱、版本、許可證等元數(shù)據(jù))。更多介紹見web前端項目配置文件
Q:最關(guān)鍵,初學(xué)者應(yīng)著重關(guān)心哪些?
A:初學(xué)者應(yīng)將精力放在
package.json
、roadhog
這兩者上,其他的都可以忽略掉。
package.json摘要介紹
npm install
命令根據(jù)這個package.json
配置文件,自動下載所需的模塊,也就是配置項目所需的運行和開發(fā)環(huán)境。package.json
文件就是一個JSON對象,該對象的每一個成員就是當(dāng)前項目的一項設(shè)置。
scripts
指定了運行腳本命令的npm命令行縮寫,比如start指定了運行npm run start
時,所要執(zhí)行的命令。
進行前端開發(fā)時可能經(jīng)常用到的幾個命令是:npm run start
(進行開發(fā)調(diào)試,縮寫成npm start
)、npm run build
(進行構(gòu)建打包)。當(dāng)然也可以嘗試package.json
中給出的其他命令,如:npm run analyze
、npm run test
等,甚至也可以自己在package.json
中添加需要的命令。
dependencies
字段指定了項目運行所依賴的模塊,devDependencies
指定項目開發(fā)所需要的模塊。它們都指向一個對象。該對象的各個成員,分別由模塊名和對應(yīng)的版本要求組成,表示依賴的模塊及其版本范圍。更多信息參見web前端項目配置文件
babel
配置項,其實是有關(guān)babel
的配置內(nèi)容,寫在package.json
中實際上是一種省略,也可以寫在.babelrc
文件中。
lint-staged
是與代碼檢查有關(guān)的配置項,jest
是與單元測試有關(guān)的配置項。這兩項的配置可以參考jest、lint。這兩項與寫業(yè)務(wù)邏輯代碼沒有直接關(guān)聯(lián)。
roadhog摘要介紹
roadhog
是一個 cli 工具,提供 server
、 build
和 test
三個命令,分別用于本地調(diào)試和構(gòu)建,并且提供了特別易用的 mock
功能。命令行體驗和 create-react-app
一致,配置略有不同,比如默認開啟 css modules
,然后還提供了 JSON
格式的配置方式。
重點介紹roadhog
有關(guān)的幾個配置項,主要是在ant design pro
的代碼中用到了這些配置項。
entry
指定 webpack
入口文件,支持 glob
格式。
如果你的項目是多頁類型,會希望把 src/pages
的文件作為入口。可以這樣配:
"entry": "src/pages/\*.js"
env
針對特定的環(huán)境進行配置。server
的環(huán)境變量是 development
,build
的環(huán)境變量是 production
。
比如:
"extraBabelPlugins": ["transform-runtime"],
"env": {
"development": {
"extraBabelPlugins": ["dva-hmr"]
}
}
這樣,開發(fā)環(huán)境下的 extraBabelPlugins
是 ["transform-runtime", "dva-hmr"]
,而生產(chǎn)環(huán)境下是 ["transform-runtime"]
。
"env": {
"development": {
"extraBabelPlugins": [
"dva-hmr",
"transform-runtime",
"transform-decorators-legacy",
"transform-class-properties",
["import", { "libraryName": "antd", "style": true }]
]
},
"production": {
"extraBabelPlugins": [
"transform-runtime",
"transform-decorators-legacy",
"transform-class-properties",
["import", { "libraryName": "antd", "style": true }]
]
}
}
在這段代碼中,開發(fā)環(huán)境和生產(chǎn)環(huán)境分別配置,其中開發(fā)環(huán)境使用了dva-hmr
插件,
externals
配置 webpack
的 externals
屬性。
theme
配置主題,實際上是配 less
的 modifyVars
。支持 Object
和文件路徑兩種方式的配置。
比如:
"theme": {
"@primary-color": "#1DA57A"
}
或者,
"theme": "./node_modules/abc/theme-config.js"
這里有 如何配置 antd theme 的例子 。
babel
上述多處提到了babel
,因此有必要針對babel
進行了解。用官方的用語:Babel 是一個 JavaScript 編譯器。今天就來用下一代 JavaScript 語法寫代碼吧!
。換句話說:Babel
是一個廣泛使用的轉(zhuǎn)碼器,可以將ES6
代碼轉(zhuǎn)為ES5
代碼,從而在現(xiàn)有環(huán)境執(zhí)行。
既然如此,接下來重點說說package.json
、.roadhogrc
中有關(guān)babel
的配置內(nèi)容:
presets
字段用來設(shè)定轉(zhuǎn)碼規(guī)則,如package.json
中
"babel": {
"presets": [
"env",
"react"
],
...
}
就是設(shè)定了env
、react
兩個轉(zhuǎn)碼規(guī)則。其中env
指的是babel-preset-env,這是一個新的 preset
,可以根據(jù)配置的目標(biāo)運行環(huán)境(environment
)自動啟用需要的 babel
插件。react
指的是babel-preset-react,主要是針對所有react
插件的轉(zhuǎn)碼規(guī)則。
plugins
配置項不用多說,從名稱就能看出來,就是babel
的一系列插件,與ant design pro
的package.json
相關(guān)的兩個插件在這里:transform-decorators-legacy、transform-class-properties。而在.roadhogrc
配置文件中也額外配置了babel
的插件:transform-runtime、dva-hmr、babel-plugin-import。其中唯一需要多說一點的是按需加載插件babel-plugin-import
,它可以針對antd插件中的部分元素樣式進行按需加載。
其實不用更多了解相關(guān)內(nèi)容,只需知道如此配置之后,1.開發(fā)者可以使用最新的ES6
書寫代碼;2.放心使用react相關(guān)插件;3.在進行run start
或run build
時,代碼能夠自動轉(zhuǎn)換成ES5
代碼,從而在瀏覽器中執(zhí)行。
項目中的幾個目錄
其中mock
是本地模擬數(shù)據(jù)目錄、node_modules
是依賴庫目錄、public
是入口目錄、src
是源碼目錄、tests
是測試工具目錄。在進行npm run build
之后還會生成(默認情況下)dist
目錄,這是生成的生產(chǎn)環(huán)境下的運行代碼目錄。其他目錄不必多說,只需要核心了解src
的目錄組成即可。
├── src
│ ├── common # 應(yīng)用公用配置,如導(dǎo)航信息
│ ├── components # 業(yè)務(wù)通用組件
│ ├── e2e # 集成測試用例
│ ├── layouts # 通用布局
│ ├── models # dva model
│ ├── routes # 業(yè)務(wù)頁面入口和常用模板
│ ├── services # 后臺接口服務(wù)
│ ├── utils # 工具庫
│ ├── g2.js # 可視化圖形配置
│ ├── polyfill.js # 兼容性墊片
│ ├── theme.js # 主題配置
│ ├── index.js # 應(yīng)用入口
│ ├── index.less # 全局樣式
│ └── router.js # 路由入口
index.js 應(yīng)用入口
這個頁面核心就是import
、const
這兩個命令,至于其是否是ES6
的暫且不管。
import
語法介紹見這里。在index.js
這個文件中核心就是加載并執(zhí)行了一系列module
,并引入了dva
、models
兩個變量。
有關(guān)ES6的語法推薦查詢?nèi)钜环宸g的ECMAScript 6 入門,后續(xù)不再贅述。
const
語法介紹見這里。這里就是聲明了一個變量app
。
其他
至于index
頁面中的const app = dva({})
、models.forEach((m)=>{app.model(m);});
、app.router(require('./router'));
、app.start('#root');
這幾句代碼暫時不理會,待梳理調(diào)用流程時再進行解析。
簡單說一下,由于這里是入口,所有這些內(nèi)容都是用來進行全局設(shè)置的,至于其如何實現(xiàn)?如何調(diào)用?這些是細節(jié)內(nèi)容,在概略瀏覽代碼時,可以不關(guān)注這些點。
router.js 路由入口
這個頁面核心就是定義路由策略。并引出了export
命令。
export
語法介紹見這里。這里是將定義好的函數(shù)RouterConfig
默認輸出出去。
定義路由策略
通過function
關(guān)鍵字定義了RouterConfig
函數(shù),其輸入?yún)?shù)是{history}
對象,返回值是一個頁面,其由LocaleProvider
、Router
、Switch
、Route
、Redirect
標(biāo)簽(component)組成的。其中每個component從哪里來,怎么操作暫時先不去關(guān)注這些細節(jié),在模仿實現(xiàn)的時候可以有樣學(xué)樣。
核心認識到通過/user
路徑可以訪問到UserLayout
,通過/
可以訪問到BasicLayout
,默認情況下(Redirect
),會跳轉(zhuǎn)到/
。具體代碼就是如下這段:
<Switch>
<Route path="/user" component={UserLayout} />
<Route path="/" component={BasicLayout} />
<Redirect to="/" />
</Switch>
其他文件
theme.js
主要是主題配置、index.less
主要是進行了全局樣式設(shè)置、polyfill.js
是兼容性墊片、g2.js
是可視化圖形的配置。一般情況下,如果不需要修改全局樣式、主題修改、對g2進行配置,不進行額外的瀏覽器兼容性考慮,這些文件可以不進行深入了解。
其實對這些文件的加載過程是其使用的腳手架完成的,暫且不深入了解腳手架及webpack
相關(guān)的實現(xiàn)機制。
dva
先來看看dva
的介紹:dva
是基于現(xiàn)有應(yīng)用架構(gòu) (redux
+ react-router
+ redux-saga
等)的一層輕量封裝,沒有引入任何新概念,全部代碼不到 100 行。dva
是 framework,不是 library,類似 emberjs,會很明確地告訴你每個部件應(yīng)該怎么寫,這對于團隊而言,會更可控。另外,除了 react
和 react-dom
是 peerDependencies
以外,dva
封裝了所有其他依賴。dva
實現(xiàn)上盡量不創(chuàng)建新語法,而是用依賴庫本身的語法,比如 router
的定義還是用 react-router
的 JSX
語法的方式(dynamic config 是性能的考慮層面,之后會支持)。 引用自這里,github的中文介紹在這里
這又引出了一系列新的概念:redux
、react
、react-router
、react-dom
等。暫且先不管這些新的概念是什么,只需要知道dva
是一套框架(framework),盡量不引入新的語法,只是明確了每個部件該怎么寫。
redux
由于dva
是一層皮,其內(nèi)涵是redux
、react
等。那就了解下redux
是什么?redux
的中文文檔從這里找到。用幾句話概括就是:Redux
是 JavaScript
狀態(tài)容器,提供可預(yù)測化的狀態(tài)管理。可以讓你構(gòu)建一致化的應(yīng)用,運行于不同的環(huán)境(客戶端、服務(wù)器、原生應(yīng)用),并且易于測試。不僅于此,它還提供 超爽的開發(fā)體驗,比如有一個時間旅行調(diào)試器可以編輯后實時預(yù)覽。
從閱讀其介紹就可以獲知:Redux
試圖讓 state
的變化變得可預(yù)測,它就是管理state
的解決方案。至于如何使用redux
、它具有哪些核心概念、如何操作redux
中的函數(shù)、使用它有哪些注意事項。可以通過學(xué)習(xí)redux
獲知。這應(yīng)該是2天工作量的就可以完成的學(xué)習(xí)任務(wù)。
由于后續(xù)進行開發(fā)時會反復(fù)使用到redux
相關(guān)的概念,這一章節(jié)必須進行擴展學(xué)習(xí),可以安排后續(xù)的學(xué)習(xí)任務(wù)。但為不影響主線,暫且放下,進行整體思路梳理。
react及其他
React
是一個采用聲明式,高效而且靈活的用來構(gòu)建用戶界面的框架。因此,有必要對react
的基礎(chǔ)邏輯進行理解,此處需要安排2天的學(xué)習(xí)任務(wù),了解react
的基礎(chǔ)知識。中文的教程在這里
React Router
是完整的 React
路由解決方案。React Router
保持 UI 與 URL 同步。它擁有簡單的 API 與強大的功能例如代碼緩沖加載、動態(tài)路由匹配、以及建立正確的位置過渡處理。中文教程在這里
React Dom
提供了針對dom的方法,具體參考這里。
各目錄及相互關(guān)系
common
是應(yīng)用公共配置,如導(dǎo)航信息;components
是業(yè)務(wù)通用組件;layouts
是通用布局;models
是dva model
;routes
是業(yè)務(wù)頁面入口和常用模板;services
是后臺接口服務(wù);utils
是工具庫;e2e
是集成測試用例。
從入口開始看起,一步步抽絲剝繭看看各個組件之間是怎樣進行調(diào)用的。
首先在index.js
中加載了models
中的所有model
并進行了注冊。其中app.model()
函數(shù)來自于dva
的用法,而且model
的概念也是dva
中的概念,詳見這里,而所有的model
均來自于./models
目錄。之后又通過app.router()
語句注冊了路由表,詳細介紹見這里。index.js
文件的最后調(diào)用了 app.start('#root');
,這句話的含義是啟動應(yīng)用,詳細介紹見這里。
在啟動應(yīng)用之后,開發(fā)者可以通過瀏覽器向服務(wù)器發(fā)起請求訪問,以獲取頁面和數(shù)據(jù)。不詳細展開路由過程,依照路由字面含義。在訪問/
時,會被路由到BasicLayout
布局頁面(此處參見router.js
)。
布局頁面
布局頁面在開始的位置import
了一系列的組件(component),定義了一些const
(常量),先不去管它。接下來出現(xiàn)了詭異的class
關(guān)鍵字,這有中java
代碼亂入的感覺,沒關(guān)系。這是ES6
的新特性,參見這里,簡單理解起來可以將之看作對象。這個class
繼承自React.PureComponent
,在class
中定義了靜態(tài)變量、構(gòu)造方法,以及一系列的內(nèi)部方法,值得注意的是方法只有被調(diào)用時才會執(zhí)行,因此class
中的一系列方法都可以視為聲明,而不會順序執(zhí)行。直到最后出現(xiàn)了一個render
方法,這個方法會在渲染組件時被調(diào)用(此處請參考react組件相關(guān)),因此我們關(guān)心頁面顯示相關(guān)邏輯就主要是關(guān)注render
這個方法。
在對render
方法展開說之前,先把BasicLayout
最后的export
說完。export default
是ES6
相關(guān)語法,前面已有介紹,指默認輸出。connect
是react-redux
中的一個api,是用來連接 React
組件與 Redux store
的,其用法詳情見這里,只需知道其返回的仍是原React
組件(當(dāng)前事例中是BasicLayout
),并與已有的狀態(tài)信息state
相關(guān)聯(lián)。
再展開說render
。1.在render
中,用到了一系列來自antd
的組件如:Layout
、Menu
等,其相關(guān)的介紹不展開,可以到antd
網(wǎng)站參考其相關(guān)教程。2.當(dāng)然也需要用到dva
封裝好的有關(guān)路由的一系列組件,如:Link
、Route
、Redirect
、Switch
。3.同樣也用到了ant design pro
中創(chuàng)建的組件,如:HeaderSearch
、NoticeIcon
、GlobalFooter
。如果深入進去了解每個組件的實現(xiàn)原理,會不可避免陷入泥潭,對此只需閱讀antd
、dva
的相關(guān)api,明確該組件是什么、怎么做就好,接下來就是實踐中掌握。
BasicLayout
布局的render
中,核心布局就是左邊欄(Sider
,參考antd
的Layout.Sider)、頂部(Header
,參考Layout.Header、內(nèi)容區(qū)(Content
,參考Layout.Content。在內(nèi)容區(qū)除了整體的可更換的頁面以外還有自創(chuàng)建的腳部區(qū)域(GlobalFooter
),其中左邊欄、頂部使用了ant design pro
樣例中自創(chuàng)建的組件,包括:搜索框(HeaderSearch
)、通知提醒框(NoticeIcon
),以及antd
中已經(jīng)封裝好的通用模板:菜單欄(Menu
)、下拉菜單(Dropdown
)、頭像(Avatar
)、加載中(Spin
)、圖標(biāo)(Icon
)、標(biāo)簽(Tag
)、全局提示(message
)。如上所述,有關(guān)antd
、dva
中已經(jīng)封裝好的通用模板,應(yīng)當(dāng)閱讀相關(guān)的api,而不應(yīng)該重新造一遍輪子。
整個布局頁面大體就是這個樣子,在
ant design pro
中還提供了另一個頁面布局就是UserLayout
。如果實際開發(fā)中還需要其他的頁面布局,就需要自己撰寫,大體來說就要研究清楚BasicLayout
頁面中的各方面細節(jié),并能夠再依照需求完成創(chuàng)建。
Redirect 路由
在BasicLayout
頁面布局中,Content
區(qū)是通過
{
getRouteData('BasicLayout').map(item =>
(
<Route
exact={item.exact}
key={item.path}
path={item.path}
component={item.component}
/>
)
)
}
<Redirect to="/dashboard/analysis" />
完成頁面加載。有關(guān)Redirect
、Route
的詳細介紹在這里或這里。先來看看getRouteData
函數(shù)干了什么。
getRouteData
函數(shù)來自于../utils/utils
文件,通過對utils.js
查看,可以看出該函數(shù)引用了../common/nav
文件,也就是nav.js
,結(jié)合nav.js
文件進行梳理。綜合三個位置可以分析得到如下過程:
- 在
nav.js
中通過import...from...
引入了layouts
、routes
中的布局頁面和業(yè)務(wù)頁面(常用模板)。由此可知 如需添加新的頁面可以參照這個格式引入。 - 在
nav.js
中定義了data
,以json
的格式定義了菜單目錄結(jié)構(gòu),并將引入的頁面(1.)以變量的形式引入進來。這個目錄內(nèi)在含義可以后續(xù)深入了解,需要注意的是:在添加新頁面時應(yīng)參照這個json格式添加。 - 通過
export default
關(guān)鍵字把data
輸出出來。 -
utils.js
中,import navData from '../common/nav';
引入了剛剛定義的data并重命名為navData。 -
utils.js
中的函數(shù)getRouteData
中,用到了數(shù)組對象中的函數(shù)some
(官方中譯文檔、解讀參考)、filter
(官方中譯文檔、解讀參考)以及lodash的cloneDeep
。 - 依照字面意思來理解,
getRouteData
做了兩件事:1.對輸入?yún)?shù)path
,進行檢查,如果檢查不通過返回null
(檢查含義主要是確保path
是一個有效layout
,同時layout
的children
不為空);2.對檢查通過的path
,獲取該布局下的所有數(shù)據(jù),并將數(shù)據(jù)轉(zhuǎn)換成數(shù)組,數(shù)組的每個元素是被稱為item
,該item
具有path
屬性、exact
屬性、component
屬性、children
屬性。 - 解釋下
item
的四個屬性:path
屬性會賦值給外層調(diào)用中Route
的path
屬性;component
屬性會賦值給外層調(diào)用中component
屬性;children
屬性是用來控制如何進行遞歸的;exact
屬性會賦值給外層調(diào)用中exact
屬性。 -
getRouteData
就獲得了具有path
、component
等屬性的元素數(shù)組。 - 在
BasicLayout
中調(diào)用了getRouteData
函數(shù)并對返回數(shù)組逐項拆解,組成Route
。 - 通過以上過程就將
nav
中的json
格式配置轉(zhuǎn)換成了頁面中的跳轉(zhuǎn)路由。當(dāng)頁面中的地址欄中出現(xiàn)/一級目錄/二級目錄
時,就可以通過Route
找到對應(yīng)的component
并進行加載。
這樣一來,layouts
、routes
、nav.js
、utils.js
之間進行路由的鏈路就清晰了。由于我們是在實踐中學(xué)習(xí),對一些實現(xiàn)細節(jié)還不必過度深究,只需要知道頁面是怎樣串起來的,以及為什么改變nav.js
中的目錄樣式就能夠操作路由,同時能夠結(jié)合實際的路由需要對路由進行必要整改就可以動手執(zhí)行了。
Menu 導(dǎo)航菜單
導(dǎo)航菜單顧名思義就是左側(cè)欄中的內(nèi)容,在BasicLayout
中就是這段代碼
<Menu
theme="dark"
mode="inline"
{...menuProps}
onOpenChange={this.handleOpenChange}
selectedKeys={this.getCurrentMenuSelectedKeys()}
style={{ margin: '16px 0', width: '100%' }}
>
{this.getNavMenuItems(this.menus)}
</Menu>
核心函數(shù)封裝成了getNavMenuItems
,其操作的對象是menus
屬性,而在構(gòu)造方法中對menus
的值進行了初始化。綜合來看梳理成如下過程:
- 在
BasicLayout
的構(gòu)造方法中,通過getNavData
方法獲取到nav.js
中定義好的data
。 -
data
本身是數(shù)組,通過reduce
(官方中譯文檔、解讀參考)方法和concat(官方中譯文檔)方法將數(shù)組規(guī)約成一個新的數(shù)組。新的數(shù)組是原數(shù)組中每個元素的children
數(shù)組再拼裝在一起組成的。換句話說,nav
中定義的data
,第一層級是布局(layout),布局的children是頁面集合(第二層級),頁面集合的children是業(yè)務(wù)頁面(第三層級)。此處的轉(zhuǎn)換就是將layout去掉,拼裝成頁面集合(第二層級)的數(shù)組。 - 在
getNavMenuItems
函數(shù)中,對menus
進行操作。對每一項(也就是頁面集合)轉(zhuǎn)換成一個Menu.Item
。這是可以理解的。 -
getNavMenuItems
函數(shù)從上到下核心做了5件事:1.判斷有沒有可顯示的name
,如果沒有將返回null
;2.給item
制定path
,也就是跳轉(zhuǎn)路徑,采用父子路徑拼裝的思路完成;3.如果存在children
且存在name
不為空,構(gòu)造子菜單SubMenu
;4.明確可顯示的圖標(biāo);5.拼裝成Menu.Item
- 在拼裝
Item
過程中,涉及到了跳轉(zhuǎn)路徑path
的構(gòu)造、可顯示的name
、icon
。
結(jié)合路由和導(dǎo)航就可以理解整個頁面左側(cè)導(dǎo)航欄的動態(tài)顯示、通過導(dǎo)航欄的點擊實現(xiàn)頁面的路由過程。
布局頁面的其他關(guān)注點
在BasicLayout
中還引入了styles
,這是一個less
文件。
Less
是一門 CSS 預(yù)處理語言,它擴展了 CSS 語言,增加了變量、Mixin、函數(shù)等特性,使 CSS 更易維護和擴展。
因此只需要把less當(dāng)成是基本的樣式對待就好。雖然它有不少新的特性,但大多數(shù)情況下,styles都是出現(xiàn)在某個組件的className
位置,以引用的方式加入進來(css的外聯(lián))。
有關(guān)Link、Icon等框架提供的組件,需要自行查閱相關(guān)API,HeaderSearch
、NoticeIcon
、GlobalFooter
的用法在后續(xù)提到components
的用法時再行展開。
動態(tài)獲取數(shù)據(jù)
在BasicLayout
頁面中進行動態(tài)加載數(shù)據(jù)就涉及到React
的組件生命周期,相關(guān)資料也可參考這里,總之在BasicLayout
頁面中用到的就是componentDidMount
函數(shù)進行數(shù)據(jù)動態(tài)加載的。這個函數(shù)核心只是調(diào)用了dispatch
:
componentDidMount() {
this.props.dispatch({
type: 'user/fetchCurrent',
});
}
此時就要引出前面提到的dva
、redux
相關(guān)的概念了。上文中提到了app.model()
這個函數(shù)及其參考。接下來把整體邏輯串起來:
- 通過
app.model()
方法,對model
進行注冊,這個model概念來自于dva
。 -
model
中的effects部分,包裝自redux-saga
。 -
this.props.dispatch
方法源自redux
,見這里。 - 在
model
注冊時,其實就是注冊了一系列的監(jiān)聽器,等待著被觸發(fā)。 - 在
dispatch
時,會接受一個Action
對象作為參數(shù),將它發(fā)送出去。關(guān)于redux的數(shù)據(jù)流程參見這里。 - 以
'user/fetchCurrent'
為例,依照dva
的邏輯會找到model
中的user
命名空間,并在其下找到fetchCurrent
方法。因此,dispatch
之后會觸發(fā)models/user.js
中的fetchCurrent
方法。 - 又通過
import
加載了services/user.js
中的queryCurrent
異步調(diào)用方法。 - 在
services/user.js
中,import
了utils/request.js
,并調(diào)用了其中的request
請求。并傳遞請求路徑為'/api/currentUser'
- 最終在
utils/request.js
中找到了request
函數(shù),其核心調(diào)用了dva
的fetch
函數(shù)進行url請求。 -
dva
中的fetch
包裝自isomorphic-fetch
,說明見這里。 - 至此將發(fā)起
fetch
請求,向服務(wù)端請求相應(yīng)的數(shù)據(jù)。以queryCurrent
為例,將利用url:'/api/currentUser'
。 - 在
.roadhogrc.mock.js
中配置了代理,同時可以找到/api/currentUser
請求返回的數(shù)據(jù)。后續(xù)不再贅述。
通過上述分析,可見關(guān)鍵點在1.model
的撰寫和注冊;2.dispatch
的正確調(diào)用;3.后臺業(yè)務(wù)請求的封裝。這涉及到了models
、services
目錄內(nèi)容及utils/request.js
(由于這個文件主要完成的是底層封裝,基本可以不去關(guān)注。以添加新的后臺請求為例,需要完成:1.在services
目錄下定義好請求的url以及函數(shù)名稱;2.將該后臺請求添加到對應(yīng)的model
中。以添加新的數(shù)據(jù)加載為例,需要完成:1.在models
目錄下中定義新的model
,注意namespace
、state
、effects
、reducers
、subscriptions
的合理使用;2.如果涉及到后臺請求數(shù)據(jù)需要引入services
;3.在合理的事件處發(fā)起dispatch
。需要注意的是dispatch
的目錄要與model
的namespace
相一致。
components目錄
這個目錄其實是抽象出來的一系列的業(yè)務(wù)通用組件。詳細內(nèi)容不再展開。包括BasicLayout
中已經(jīng)提到的HeaderSearch
、NoticeIcon
、GlobalFooter
組件,實際上都是自行封裝的結(jié)果。
e2e目錄
這個目錄是端到端測試的相關(guān)目錄,相關(guān)信息可以從這里了解一些。
由于不在主線學(xué)習(xí)范圍之內(nèi)暫不理會。
關(guān)于redux-auth-wrapper
這個項目的github地址在這里。
通過對這個項目demo的初步體驗,感覺到它是基于redux的state進行設(shè)計的。先來說說state,這是駐留在瀏覽器內(nèi)存中的一個數(shù)據(jù)結(jié)構(gòu),可以講起理解為記錄在瀏覽器(客戶端)中的用戶最新狀態(tài)信息。redux-auth-wrapper的作用位置是route時,就是利用state中的信息驗證用戶是否可以進行跳轉(zhuǎn)。這是對用戶體驗的一重優(yōu)化。
有關(guān)圖標(biāo)
擴展當(dāng)前的圖標(biāo)
核心參考這篇擴展教程。
Q:為什么需要用到擴展圖標(biāo)?
A:阿里框架所使用的圖標(biāo)庫,不足以滿足實際開發(fā)需要。
Q:阿里框架庫中有哪些圖標(biāo)?
A:阿里框架中的所有圖標(biāo)可以在ant design的資源列表中下載到,點擊這里。
Q:為什么在下載的圖標(biāo)庫中有些圖標(biāo)找不到?如:dashboard,它出現(xiàn)在ant design pro的項目中,但是在圖標(biāo)庫中卻沒有。
A:因為ant design pro依賴的是ant-design-3.0.0-beta.5,阿里發(fā)布的圖標(biāo)庫的版本號是2.10.x
Q:如何找到最新的圖標(biāo)庫?
A:可以在ant-design代碼庫的tag中找到3.0.0-beta.5的代碼中找到。其路徑是components\style\core\iconfont.less中能夠找到映射關(guān)系。當(dāng)然阿里使用了一整套的圖標(biāo)(svg、ttf、woff、eot等多種格式,掃盲貼見這里),并用css和js完成適配映射,將圖標(biāo)映射成16進制的unicode,并指定相應(yīng)的class代碼。只要確保工程項目中使用到的圖標(biāo)庫中存在某個圖標(biāo),就可以直接在項目中使用相應(yīng)的unicode代碼或class代碼。如果工程項目中使用到了第三方圖標(biāo),可以對其擴展。
Q:如何依照阿里的icon框架進行圖標(biāo)擴展?
A:參考擴展教程。
Q:還有哪些值得參考的與ant design圖標(biāo)有關(guān)的資料?
A:1.圖標(biāo)設(shè)計規(guī)范;2.本地部署 Iconfont 示例;3.antd圖標(biāo)庫2.10.x;4.內(nèi)網(wǎng)環(huán)境使用離線Icon圖標(biāo)資源
Q:在adp項目中應(yīng)該怎么操作?
A:由于我們的工程打包環(huán)境是可以聯(lián)網(wǎng)的,因此不需要進行離線Icon設(shè)置,也不需要完成Iconfont的本地部署。只需要按照擴展教程進行新組建的擴充即可。在擴充時用iconfont添加新的icon,然后構(gòu)造成相應(yīng)的圖標(biāo)庫,在下載到對應(yīng)位置即可。鑒于已完成基礎(chǔ)性操作,后續(xù)添加新圖標(biāo)的流程:1.在iconfont的圖標(biāo)庫中添加新的圖標(biāo);2.將圖標(biāo)庫下載下來覆蓋項目圖標(biāo)目錄
/public/fonts/iconfont
;3.在項目中使用圖標(biāo)代碼。
React Developer Tools
Q:react的源代碼通過編譯組成了普通html、css代碼。那怎樣在瀏覽器中查看對應(yīng)的源代碼?
A:使用React Developer Tools可以輔助在chrome中查看源代碼,參見這里。
Q:相關(guān)鏈接
A:chrome插件下載地址
有關(guān)樣式的幾個注意事項
Q:如何在ant design的樣式代碼中使用css常用的'-'?如:“font-size”
A:此處核心遇到的是js中的'.'取值與'[]'取值的區(qū)別,參考這里的解答。
Q:ant design中推薦哪種方式編寫樣式?
A:ant design中推薦使用駝峰式的樣式書寫,如:'fontSize'。同時這個框架中大量使用了'.'操作符進行對象操作,使用import語法進行整個的代碼組織。
Q:在引用樣式時傳統(tǒng)的寫法可以使用多個classname,如
class="csdn-toolbar csdn-toolbar-skin-black "
,在ant design中怎樣使用多個樣式?A:使用composes,參考這里的
Class 的組合
章節(jié)。Q:還有哪些注意事項?
A:1.在ant design的框架下推薦XXX.js文件與XXX.css(或XXX.less)文件放在一起,而不是使用分開的css、html、js目錄分別管理不同類型的文件。這是源自react的模塊化管理的思想。2.CSS Modules是ant design中默認使用。簡要介紹見這里。
Q:為什么樣式會變成類似于
class="globalFooter___1cM92"
?A:因為CSS Modules定義了局部作用域的概念,為達到局部作用效果,構(gòu)建工具會對class的name進行哈希操作,使類名變成獨一無二。常用工具就是webpack,而使用的插件是css-loader。
關(guān)于數(shù)據(jù)查詢的方法
Q:如何制定模擬的數(shù)據(jù)請求?
A:ant design pro中使用了mock。可以在.roadhogrc.mock.js中進行數(shù)據(jù)請求配置。比如
'GET /api/testapi'
是新添加的模擬數(shù)據(jù)請求。可以嘗試在瀏覽器中訪問這個url:http://localhost:8000/api/testapi
,確認該接口可以訪問數(shù)據(jù)。Q:在ant design pro中如何發(fā)起數(shù)據(jù)請求?
A:在services目錄中的文件添加一個request請求。如:
export async function queryTestData(){ return request('/api/testapi'); }
就在js中定義了一個request,發(fā)起相應(yīng)的request請求。因此,就可以在需要請求數(shù)據(jù)的位置直接調(diào)用queryTestData
方法。Q:可不可以在頁面點擊事件中直接調(diào)用
queryTestData
方法?ant design pro中為什么沒用這種方法?A:首先,這種調(diào)用方法是基礎(chǔ)的調(diào)用,是可以在頁面布局中引入相應(yīng)的文件直接調(diào)用這個函數(shù)的。ant design pro中遵從了react和redux的設(shè)計理念,承認component的模塊化設(shè)計理念,并加強瀏覽器緩存的管理。
Q:ant design pro設(shè)計框架中在哪里調(diào)用
queryTestData
方法?A:在models中進行調(diào)用。
Q:那models有什么含義?
A:在瀏覽器的緩存中,每個component都有其自身的state(這是redux里的基礎(chǔ)概念),也就是緩存。而models就是緩存進行對象化管理的抽象。因此可以把models中的model看作是不同的業(yè)務(wù)對象。而每個業(yè)務(wù)對象都可以按對象邏輯調(diào)用service方法。如在models/user.js中進行如下調(diào)用:
*fetchTestData(_, { call, put }){
const response = yield call(queryTestData);
yield put({
type: 'returnTestData',
payload:response,
});
}
Q:fetchTestData函數(shù)書寫有什么值得注意的地方?
A:1.這個函數(shù)寫在model中,model的概念來自于dva,見這里,同時其概念解釋見這里。2.要充分理解model的概念,才能更合理書寫和調(diào)用相關(guān)函數(shù)。3.model的概念還整合了redux-saga的設(shè)計理念。4.model中的effects設(shè)計來源于redux-saga的設(shè)計,參考這里。5.effects部分中使用了星號和yield關(guān)鍵字,是源自于ES6中的Generator語法,以及異步調(diào)用,因此只需要認識到星號標(biāo)識了這不是普通函數(shù),是可以暫停執(zhí)行的,加星號以示區(qū)別;yield標(biāo)識此處可以暫停,并在獲得執(zhí)行權(quán)時從此處開始執(zhí)行,調(diào)用方式參考文檔里的next調(diào)用。6.call和put來自于redux-saga(是Redux異步操作的中間件),更全的redux接口見這里。7.call實際上就是創(chuàng)建了一條描述結(jié)果的信息(上例中的含義就是異步(yield)調(diào)用queryTestData,調(diào)用完成后(call)將結(jié)果賦值給response)。8.put實際上是用來創(chuàng)建dispatch Effect的(上例中的含義就是異步(yield)調(diào)用returnTestData,并傳遞參數(shù)response),可從這里、這里以及這里了解更多的信息。
Q:model中如何改變相關(guān)狀態(tài)?
A:model的狀態(tài)存儲在state對象中,在示例中我們添加了
testdata:{},
對象。依照redux的設(shè)計理念,所有對狀態(tài)的修改都應(yīng)該在reducer中完成。因此model中的reducers部分就是定義一系列修改state的函數(shù),如:queryTestData函數(shù)。Q:model中的state和action是什么?
A:其中state是函數(shù)調(diào)用前的狀態(tài),而action就是dispatch發(fā)出的action。
Q:model中的狀態(tài)信息(state)怎么在頁面中使用?
A:通過connect函數(shù)(來自于react-redux),其實connect設(shè)計的初衷就是把容器組件與redux相連接。其更多使用方法見這里。在示例中,就是通過
@connect(state => ({
chart: state.chart,
testdata: state.user.testdata,
}))
方法將redux中的state連接到了這個頁面(容器組件)中。通過這句話testdata: state.user.testdata,
就將全局state中命名空間(namespace)為user下的狀態(tài)(state)屬性testdata對象與容器組件中的testdata屬性(存在于props中)相關(guān)聯(lián)。
- Q:怎樣把容器組件中的屬性與頁面顯示相關(guān)聯(lián)?
- A:通過
const { chart,testdata } = this.props;
這句話能夠把屬性(props)中的對象testdata解析出來。在頁面上通過{testdata.name}
將相應(yīng)的值取出來。
例:Analysis頁面折線圖切換
<Card
loading={loading}
className={styles.offlineCard}
bordered={false}
bodyStyle={{ padding: '0 0 32px 0' }}
style={{ marginTop: 32 }}
>
<Tabs
activeKey={activeKey}
onChange={this.handleTabChange}
>
{
offlineData.map(shop => (
<TabPane
tab={<CustomTab data={shop} currentTabKey={activeKey} />}
key={shop.name}
>
<div style={{ padding: '0 24px' }}>
<TimelineChart
data={offlineChartData}
titleMap={{ y1: '客流量', y2: '支付筆數(shù)' }}
/>
</div>
</TabPane>)
)
}
</Tabs>
</Card>
- Q:怎樣從接口中獲取數(shù)據(jù)?
- A:涉及到數(shù)據(jù)獲取的部分有兩個對象:offlineData、offlineChartData。這兩個對象都是通過如下方法實現(xiàn)的數(shù)據(jù)調(diào)用,詳細過程見'關(guān)于數(shù)據(jù)查詢的方法'部分
componentDidMount() {
this.props.dispatch({
type: 'chart/fetch',
}).then(() => this.setState({ loading: false }));
}
Q:
currentTabKey
怎樣在頁面實現(xiàn)數(shù)值傳遞?A:先明確頁面加載時會調(diào)用的幾個函數(shù):
componentDidMount
、componentWillUnmount
、render
。這涉及到react組件的生命周期概念,參考這里的簡介。現(xiàn)在只需要知道:
componentDidMount()
方法會在組件(所謂組件就是Component
)掛在之后調(diào)用一次;
componentWillUnmount()
方法會在組件被卸載時調(diào)用。;
render()
有四種觸發(fā)場景,詳情不贅述,只需知道頁面中調(diào)用this.setState
方法是會觸發(fā)當(dāng)前組件的更新。
可以看出如下過程:
1.組件初始化時定義了currentTabKey
變量,并賦值為'';
2.在初始化時最后調(diào)用了render
方法,依據(jù)currentTabKey
給activeKey
進行賦值;
3.在真正渲染時通過activeKey
設(shè)置Tabs
的activeKey
屬性并傳值給CustomTab
組件;
4.在Tabs
組件被點擊時通過onChange
方法回調(diào)handleTabChange
函數(shù);
5.在回調(diào)函數(shù)中通過this.setState
將state
中的currentTabKey
值修改;
6.this.setState
觸發(fā)了render
函數(shù)被再次調(diào)用。Q:還有哪些是值得關(guān)注的點?
A:
TabPane
的變量通過const { TabPane } = Tabs;
從Tabs
中析取出來,也就是說TabPane
是定義在Tabs
中的一個子組件;
CustomTab
是一個自定義組件,由于其在Tab
的頁簽位置,同時使用了ReactNode
來做頁簽,因此需要自行控制選中狀態(tài),由此theme={(currentKey !== data.name) && 'light'}
和color={(currentKey !== data.name) && '#BDE4FF'}
就是在控制相應(yīng)的樣式。
相關(guān)鏈接整理
很有用的單獨說一遍
dva:github項目
React中文文檔
React 入門實例教程(阮一峰)
Redux 入門教程(一):基本用法(阮一峰)
Redux 中文文檔
Redux-saga 中文文檔
Redux 三重境
ECMAScript 6 入門
React Router 中文文檔
React Router 使用教程(阮一峰)
Ant Design 指引
全
dva:github項目
dva:React+Redux最佳實踐
dva:中文Readme
dva:中文API
一起學(xué)react (1) 10分鐘 讓你dva從入門到精通
初識 Dva
dva源碼解析(一)
dva 入門:手把手教你寫應(yīng)用
React中文文檔
React:State&生命周期
React:組件 & Props
React 入門實例教程(阮一峰)
React PureComponent源碼解析
ReactJS分析之入口函數(shù)render
談一談創(chuàng)建React Component的幾種方式
React Dom
Redux 入門教程(一):基本用法(阮一峰)
Redux 關(guān)于react-redux中的connect用法介紹及原理解析
Redux 中文文檔
Redux 數(shù)據(jù)流
Redux-saga 中文文檔
Redux-saga 實踐總結(jié)
Redux 三重境
實例講解基于 React+Redux 的前端開發(fā)流程
ECMAScript 6 入門
ES5中新增的Array方法詳細說明
Ecma標(biāo)準(zhǔn)
Lodash 中文文檔
exports 和 module.exports 的區(qū)別
ES6:export default 和 export 區(qū)別
fetch的用法
React 語法之let和const命令
ES6展開運算符
React Router
React Router 中文文檔
React Router 使用教程(阮一峰)
React Router Redirect
path-to-regexp
Path-to-RegExp 使用
Ant Design 指引
Ant Design of React
Ant Design Pro教程
自定義主題開發(fā)中,修改theme.js需要重啟
如何評價 Ant Design 這個項目(一個設(shè)計語言)?
Ant Design源碼分析(一):Icon組件
roadhog
less 快速入門
Less簡介及簡單用法
Babel 入門教程(阮一峰,<s>不知何故他已經(jīng)在自己的博客中刪除了,只能從archive找到</s>)
stylelint初體驗
JS/React 開發(fā)者的 Atom 終極配置
自動化e2e測試 -- WebDriverJS,Jasmine和Protractor
Webpack單元測試,e2e測試
怎么為大中型的vue.js項目編寫e2e測試?
Atom編輯器折騰記_(23)加快React開發(fā)的插件匯總
Fetch API
react&webpack使用css、less && 安裝原則 --- 從根本上解決問題。
less-loader
webpack-中文文檔
React中的DOM操作
編輯器配置
js代碼檢測工具
css代碼審查配置
git版本配置
travis持續(xù)構(gòu)建工具配置
roadhog配置
web前端項目配置文件
jest
lint
如何配置 antd theme 的例子
dva-hmr
babel-plugin-import
babel-preset-env
babel-preset-react
transform-decorators-legacy
transform-class-properties
transform-runtime
antd