記錄最近做的一個 demo,前端使用 React
,用 React Router
實現前端路由,Koa 2
搭建 API Server, 最后通過 Nginx
做請求轉發。
這是第一篇,主要介紹下前端代碼的構建、React router
使用中遇到問題,以及前端開發完成后部署相關工作。
功能介紹
GitCard 可以通過 GitHub 授權獲取用戶基本信息
- 在首頁模塊,可以查看最近登錄的用戶,並點擊頭像查看該用戶的詳細信息
- 在
/Comment
模塊中可以發表評論,並刪除自己的評論 - 在
/Detail
模塊中可以查看用戶在 Github 上的基本信息(代碼庫,Follower、Following 以及更多開發的信息),你可以在這個基礎上做更多有意思的事情,支持 Follow 和 Unfollow 操作,當然,你可以加上 Star 和 Unstar 操作,異曲同工。🈳️
依賴版本
- 構建工具: create-react-app
- react: 16.0.0
- react-router: 4.2.2
- 網絡請求: axios
- UI: material-ui
項目搭建
前端構建工具使用 React 官方的 create-react-app,快速生成可執行的項目結構。下面是快速上手流程,詳細的內容可以參見 官方文檔。
// 全局安裝 create-react-app
npm install -g create-react-app
//生成項目,項目名 my-app(自定義)
create-react-app my-app
cd my-app
// 安裝依賴
npm install
// 開發環境
npm start
// 打包
npm run build
生成項目后,項目文件結構如下,npm run build
執行后,目錄下會出現 /build/
目錄,存放構建后的文件。
/**
* my-app 目錄結構
*/
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ └── favicon.ico
│ └── index.html
│ └── manifest.json
└── src
└── App.css
└── App.js //項目入口
└── App.test.js
└── index.css
└── index.js
└── logo.svg
└── registerServiceWorker.js
我們在 /src/
目錄下新建 /components/
目錄,用於存放本次項目中的所有自定義組件,最終的目錄如下
.
├── README.md
├── build
├── package.json
├── public
├── src
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── components
│ │ ├── Comments
│ │ │ ├── comment.css
│ │ │ └── index.jsx
│ │ ├── Events
│ │ │ └── index.jsx
│ │ ├── HomePage
│ │ │ └── index.jsx
│ │ ├── UserDetail
│ │ │ └── index.jsx
│ │ └── layouts
│ │ ├── Header.jsx
│ │ └── SideMenu.jsx
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ ├── registerServiceWorker.js
│ └── utils
│ ├── api.jsx
│ ├── fetch.jsx
│ └── keygen.js
└── yarn.lock
前端路由
這次做的項目是單頁應用,通過 React router
實現前端路由,Container
通過路由選擇顯示的模塊內容,在 Header
顯示路由切換入口,頁面結構圖如下。
在 /src/App.js
布局並設置路由,將 Header
和 Container
包在 Router
下,在 Header
的 Menu 組件中設置路由鏈接,並在 Container
中設置路由指向對應組件。
Menu 中 Detail 鏈接在用戶授權登錄后就指向登錄用戶信息,否則跳轉到授權登錄頁面。
// 引入 React router
import {
BrowserRouter as Router,
Link,
Route
} from "react-router-dom"
// 在 layout-content 中設置路由對應的組件
<Router>
<div className="App">
<Header loginInfo={this.state.loginInfo}></Header>
<div className="layout-container">
<div className="layout-content">
<Route exact path="/" component={ HomePage } ></Route>
<Route path="/user/:userid" component={ UserDetail } ></Route>
<Route path="/events" component={Events}></Route>
<Route path="/comments" render={ ()=><Comments loginInfo={this.state.loginInfo} /> }></Route>
</div>
</div>
</div>
</Router>
// 在 Header 中設置 Link
<Menu>
<Link to="/">
<MenuItem primaryText="Home" />
</Link>
<Link to="/comments">
<MenuItem primaryText="Comments"/>
</Link>
{
this.props.loginInfo.login ?
(<Link to={ '/user/' + this.props.loginInfo.login }>
<MenuItem primaryText="Detail" />
</Link>)
: (<MenuItem primaryText="Login" onClick={this.login} />)
}
</Menu>
數據操作
路由下發數據
上面的代碼的路由中,我們通過兩種方式下發數據到組件中,通過路由參數將用戶 ID 下發到 Detail
組件,以及通過 props
講用戶基本信息下發到 Comment
組件中。
// 通過路由參數下發的 Link 以及對應的 Route
<Link to={ '/user/' + this.props.loginInfo.login }><MenuItem primaryText="Detail" /></Link>
<Route path="/user/:userid" component={ UserDetail } ></Route>
// 通過 props 下發
<Route path="/comments" render={ ()=><Comments loginInfo={this.state.loginInfo} /> }></Route>
通過路由參數獲取下發數據,如果在 /user/lijundong
跳轉到 /user/free-free
會出現路由改變,數據不刷新的情況,這里需要用 componentWillReceiveProps(nextProps)
處理。
在 Detail
組件中獲取 ID,
const userId = this.props.match.params.userid;
以及在 Comment
中獲取用戶信息
const loginInfo = this.props.loginInfo;
數據請求
我們在這里選擇用 axios 網絡請求,為了方便管理,將所有的 API 統一放在 /src/utils/api.jsx
中,利於后期維護。
沒有使用 Fetch 是因為 Github API 只支持 XHR 跨域請求,Fetch 跨域請求會導致請求 request type 變成 option
。
數據更新
setState
因為這次的數據並不復雜,所以沒有引入 MobX 或者 Redux 處理,數據更新全程使用 setState
,不過由於 setState
是異步操作,所以,某些情況下,需要用到回調函數,如下
setState(
{ name: "Michael" },
() => console.log(this.state.name)
);
// Michael
componentWillReceiveProps
上面提到過,通過路由參數獲取下發數據,如果在 /user/lijundong
跳轉到 /user/free-free
會出現路由改變,數據不刷新的情況,這是因為 componentDidMount()
只會執行一次,props
更新不會觸發重新獲取數據,這里可通過 componentWillReceiveProps()
解決。
componentWillReceiveProps()
會在每次 props
更新時觸發,通過新參數重新獲取數據列表。
componentWillReceiveProps(nextProps){
const userId = nextProps.match.params.userid;
// 通過新參數獲取數據
Axios.get(url)
}
項目構建部署
項目開發結束,通過 npm run build
進行打包,打包完成后,生成的 /build/
即我們最終上線的版本,因為是前后端分離的項目,前端需要自起 Server,可以通過這兩種方案進行部署
- 使用 create-react-app 自帶的
serve -s build
命令起 Server(我當前使用的方式) - 或者通過第三方工具
http-server
、anywhere
等起 Server
服務開啟后,可以正常訪問就代表前端部分已經完成,暴露的端口供最后 Nginx 配置使用。