Ant Design Pro 學(xué)習(xí)筆記

學(xué)習(xí)筆記

原文地址:antDesignPro使用心得,快速開發(fā)必備。
https://www.52pojie.cn/thread-696928-1-1.html
(出處: 吾愛破解論壇)

摘要介紹

Ant Design Pro 是一個基于Ant Design搭建起來的模板項目。它提供了兩個主要布局:BasicLayoutUserLayout,在布局基礎(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代碼審查配置


Qgit是什么?

Agit是版本控制工具,類似的工具還有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 工具,提供 serverbuildtest 三個命令,分別用于本地調(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.jsonroadhog這兩者上,其他的都可以忽略掉。

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 analyzenpm 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)的配置項。這兩項的配置可以參考jestlint。這兩項與寫業(yè)務(wù)邏輯代碼沒有直接關(guān)聯(lián)。

roadhog摘要介紹

roadhog 是一個 cli 工具,提供 serverbuildtest 三個命令,分別用于本地調(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)境變量是 developmentbuild 的環(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

配置 webpackexternals 屬性。

theme

配置主題,實際上是配 lessmodifyVars。支持 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è)定了envreact兩個轉(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 propackage.json相關(guān)的兩個插件在這里:transform-decorators-legacytransform-class-properties。而在.roadhogrc配置文件中也額外配置了babel的插件:transform-runtimedva-hmrbabel-plugin-import。其中唯一需要多說一點的是按需加載插件babel-plugin-import,它可以針對antd插件中的部分元素樣式進行按需加載。

其實不用更多了解相關(guān)內(nèi)容,只需知道如此配置之后,1.開發(fā)者可以使用最新的ES6書寫代碼;2.放心使用react相關(guān)插件;3.在進行run startrun 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)用入口

這個頁面核心就是importconst這兩個命令,至于其是否是ES6的暫且不管。

import

語法介紹見這里。在index.js這個文件中核心就是加載并執(zhí)行了一系列module,并引入了dvamodels兩個變量。

有關(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}對象,返回值是一個頁面,其由LocaleProviderRouterSwitchRouteRedirect標(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)該怎么寫,這對于團隊而言,會更可控。另外,除了 reactreact-dompeerDependencies 以外,dva 封裝了所有其他依賴。dva 實現(xiàn)上盡量不創(chuàng)建新語法,而是用依賴庫本身的語法,比如 router 的定義還是用 react-routerJSX 語法的方式(dynamic config 是性能的考慮層面,之后會支持)。 引用自這里,github的中文介紹在這里

這又引出了一系列新的概念:reduxreactreact-routerreact-dom等。暫且先不管這些新的概念是什么,只需要知道dva是一套框架(framework),盡量不引入新的語法,只是明確了每個部件該怎么寫。

redux

由于dva是一層皮,其內(nèi)涵是reduxreact等。那就了解下redux是什么?redux的中文文檔從這里找到。用幾句話概括就是:ReduxJavaScript 狀態(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是通用布局;modelsdva modelroutes是業(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 defaultES6相關(guān)語法,前面已有介紹,指默認輸出。connectreact-redux中的一個api,是用來連接 React 組件與 Redux store的,其用法詳情見這里,只需知道其返回的仍是原React組件(當(dāng)前事例中是BasicLayout),并與已有的狀態(tài)信息state相關(guān)聯(lián)。

再展開說render。1.在render中,用到了一系列來自antd的組件如:LayoutMenu等,其相關(guān)的介紹不展開,可以到antd網(wǎng)站參考其相關(guān)教程。2.當(dāng)然也需要用到dva封裝好的有關(guān)路由的一系列組件,如:LinkRouteRedirectSwitch。3.同樣也用到了ant design pro中創(chuàng)建的組件,如:HeaderSearchNoticeIconGlobalFooter。如果深入進去了解每個組件的實現(xiàn)原理,會不可避免陷入泥潭,對此只需閱讀antddva的相關(guān)api,明確該組件是什么、怎么做就好,接下來就是實踐中掌握。

BasicLayout布局的render中,核心布局就是左邊欄(Sider,參考antdLayout.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)antddva中已經(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)RedirectRoute的詳細介紹在這里這里。先來看看getRouteData函數(shù)干了什么。

getRouteData函數(shù)來自于../utils/utils文件,通過對utils.js查看,可以看出該函數(shù)引用了../common/nav文件,也就是nav.js,結(jié)合nav.js文件進行梳理。綜合三個位置可以分析得到如下過程:

  1. nav.js中通過import...from...引入了layoutsroutes中的布局頁面和業(yè)務(wù)頁面(常用模板)。由此可知 如需添加新的頁面可以參照這個格式引入
  2. nav.js中定義了data,以json的格式定義了菜單目錄結(jié)構(gòu),并將引入的頁面(1.)以變量的形式引入進來。這個目錄內(nèi)在含義可以后續(xù)深入了解,需要注意的是:在添加新頁面時應(yīng)參照這個json格式添加
  3. 通過export default關(guān)鍵字把data輸出出來。
  4. utils.js中,import navData from '../common/nav';引入了剛剛定義的data并重命名為navData。
  5. utils.js中的函數(shù)getRouteData中,用到了數(shù)組對象中的函數(shù)some官方中譯文檔解讀參考)、filter官方中譯文檔解讀參考)以及lodashcloneDeep
  6. 依照字面意思來理解,getRouteData做了兩件事:1.對輸入?yún)?shù)path,進行檢查,如果檢查不通過返回null(檢查含義主要是確保path是一個有效layout,同時layoutchildren不為空);2.對檢查通過的path,獲取該布局下的所有數(shù)據(jù),并將數(shù)據(jù)轉(zhuǎn)換成數(shù)組,數(shù)組的每個元素是被稱為item,該item具有path屬性、exact屬性、component屬性、children屬性。
  7. 解釋下item的四個屬性:path屬性會賦值給外層調(diào)用中Routepath屬性;component屬性會賦值給外層調(diào)用中component屬性;children屬性是用來控制如何進行遞歸的;exact屬性會賦值給外層調(diào)用中exact屬性。
  8. getRouteData就獲得了具有pathcomponent等屬性的元素數(shù)組。
  9. BasicLayout中調(diào)用了getRouteData函數(shù)并對返回數(shù)組逐項拆解,組成Route
  10. 通過以上過程就將nav中的json格式配置轉(zhuǎn)換成了頁面中的跳轉(zhuǎn)路由。當(dāng)頁面中的地址欄中出現(xiàn)/一級目錄/二級目錄時,就可以通過Route找到對應(yīng)的component并進行加載。

這樣一來,layoutsroutesnav.jsutils.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的值進行了初始化。綜合來看梳理成如下過程:

  1. BasicLayout的構(gòu)造方法中,通過getNavData方法獲取到nav.js中定義好的data
  2. data本身是數(shù)組,通過reduce官方中譯文檔解讀參考)方法和concat(官方中譯文檔)方法將數(shù)組規(guī)約成一個新的數(shù)組。新的數(shù)組是原數(shù)組中每個元素的children數(shù)組再拼裝在一起組成的。換句話說,nav中定義的data,第一層級是布局(layout),布局的children是頁面集合(第二層級),頁面集合的children是業(yè)務(wù)頁面(第三層級)。此處的轉(zhuǎn)換就是將layout去掉,拼裝成頁面集合(第二層級)的數(shù)組。
  3. getNavMenuItems函數(shù)中,對menus進行操作。對每一項(也就是頁面集合)轉(zhuǎn)換成一個Menu.Item。這是可以理解的。
  4. getNavMenuItems函數(shù)從上到下核心做了5件事:1.判斷有沒有可顯示的name,如果沒有將返回null;2.給item制定path,也就是跳轉(zhuǎn)路徑,采用父子路徑拼裝的思路完成;3.如果存在children且存在name不為空,構(gòu)造子菜單SubMenu;4.明確可顯示的圖標(biāo);5.拼裝成Menu.Item
  5. 在拼裝Item過程中,涉及到了跳轉(zhuǎn)路徑path的構(gòu)造、可顯示的nameicon

結(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)LinkIcon等框架提供的組件,需要自行查閱相關(guān)API,HeaderSearchNoticeIconGlobalFooter的用法在后續(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',
  });
}

此時就要引出前面提到的dvaredux相關(guān)的概念了。上文中提到了app.model()這個函數(shù)及其參考。接下來把整體邏輯串起來:

  1. 通過app.model()方法,對model進行注冊,這個model概念來自于dva
  2. model中的effects部分,包裝自redux-saga
  3. this.props.dispatch方法源自redux,見這里
  4. model注冊時,其實就是注冊了一系列的監(jiān)聽器,等待著被觸發(fā)。
  5. dispatch時,會接受一個 Action 對象作為參數(shù),將它發(fā)送出去。關(guān)于redux的數(shù)據(jù)流程參見這里
  6. 'user/fetchCurrent'為例,依照dva的邏輯會找到model中的user命名空間,并在其下找到fetchCurrent方法。因此,dispatch之后會觸發(fā)models/user.js中的fetchCurrent方法。
  7. 又通過import加載了services/user.js中的queryCurrent異步調(diào)用方法。
  8. services/user.js中,importutils/request.js,并調(diào)用了其中的request請求。并傳遞請求路徑為'/api/currentUser'
  9. 最終在utils/request.js中找到了request函數(shù),其核心調(diào)用了dvafetch函數(shù)進行url請求。
  10. dva中的fetch包裝自isomorphic-fetch,說明見這里
  11. 至此將發(fā)起fetch請求,向服務(wù)端請求相應(yīng)的數(shù)據(jù)。以queryCurrent為例,將利用url:'/api/currentUser'
  12. .roadhogrc.mock.js中配置了代理,同時可以找到/api/currentUser請求返回的數(shù)據(jù)。后續(xù)不再贅述。

通過上述分析,可見關(guān)鍵點在1.model的撰寫和注冊;2.dispatch的正確調(diào)用;3.后臺業(yè)務(wù)請求的封裝。這涉及到了modelsservices目錄內(nèi)容及utils/request.js(由于這個文件主要完成的是底層封裝,基本可以不去關(guān)注。以添加新的后臺請求為例,需要完成:1.在services目錄下定義好請求的url以及函數(shù)名稱;2.將該后臺請求添加到對應(yīng)的model中。以添加新的數(shù)據(jù)加載為例,需要完成:1.在models目錄下中定義新的model,注意namespacestateeffectsreducerssubscriptions的合理使用;2.如果涉及到后臺請求數(shù)據(jù)需要引入services;3.在合理的事件處發(fā)起dispatch。需要注意的是dispatch的目錄要與modelnamespace相一致。

components目錄

這個目錄其實是抽象出來的一系列的業(yè)務(wù)通用組件。詳細內(nèi)容不再展開。包括BasicLayout中已經(jīng)提到的HeaderSearchNoticeIconGlobalFooter組件,實際上都是自行封裝的結(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ù):componentDidMountcomponentWillUnmountrender。這涉及到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ù)currentTabKeyactiveKey進行賦值;
    3.在真正渲染時通過activeKey設(shè)置TabsactiveKey屬性并傳值給CustomTab組件;
    4.在Tabs組件被點擊時通過onChange方法回調(diào)handleTabChange函數(shù);
    5.在回調(diào)函數(shù)中通過this.setStatestate中的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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,693評論 25 708
  • ant Design給人的感覺就是:很強大、很龐大(雜亂)。 它號稱已經(jīng)應(yīng)用于多個生產(chǎn)項目了。或者看這里。或者看看...
    松鼠楊閱讀 16,297評論 4 21
  • 前段時間和朋友聊天,聊到女孩子不應(yīng)該讓自己的男朋友陪自己去逛地攤,即使你有把地攤貨穿出大牌子的能力,你有能力買牌...
    怕曬仍向陽閱讀 4,381評論 0 2
  • 最近在看軍師聯(lián)盟。里面兩個人的斗法讓我印象深刻。那就是楊修和司馬懿的較量。楊修當(dāng)時無論是才華還是地位都在司馬懿之上...
    mylkevin閱讀 280評論 0 0
  • 分享198 時間每到此時此刻就需要用度秒如年計算,面對著一個個蠢蠢欲動的學(xué)生,這又是斗智斗勇的過程,怎樣讓他...
    梓桐潔兒閱讀 429評論 0 2