第一次真正使用 react 去作一個小項目,可能有使用的不合理的地方,能夠在 issue 中提意見,如下記錄了項目實現過程當中遇到的問題。
模仿如今比較火的,淘寶返利公衆號的功能,包含訂單頁面,已付、待反利、已反、已失效,提現頁面,提現記錄以及登陸頁。其中部分頁面&使用方式參照react-pxq項目,star 數量 6359 頗有參考意義html
1.使用 react 建立頁面 √前端
2.使用 react-router-dom 實現路由 √node
3.使用 props-type 作屬性檢查 √react
4.搭建 mock server 模擬數據請求 √webpack
5.使用 redux 實現狀態管理 √ios
6.使用 Immutablegit
7.項目中添加異步請求 √github
8.redux-thunk 中間件使用 √web
npm start
在開發環境運行chrome
瀏覽器打開http://localhost:3000可訪問
npm run build
將工程打包到 build 目錄下
npm run eject
create-react-app 自己的 webpack 配置文件存在於 node_modules/react-scripts/目錄下面,可是這個目錄是 node_modules/,裏面的源碼都是不建議修改的。create-react-app 提供了 eject 命令,用於釋放這些配置。
並且 create-react-app 並不推薦你們這麼作,由於這個步驟沒法逆轉!
npm run eject 以後,react-scripts 命令就失效了哦。由於在 node_modules/下面,都沒有 react-scripts/的目錄了,它以另外的形式存在於這個項目裏面了。
使用 Ant Design ,相比原生 Html 能快速搭建頁面,更專一於 react 相關技術的學習,其實寫的是 app 端的淘寶返利一個小項目,後來發現應該使用 Ant Design Mobile 來作移動端的,不過沒關係,咱們的注意力不在頁面適配上。
下載 chrome 插件,方便 react 項目代碼調試
react 的類型檢查 PropTypes 自 React v15.5 起已棄用,請使用prop-types
JavaScript 是一門弱類型的語言,容許變量類型作隱式轉換。也正是由於這個特性,JavaScript 中有不少錯誤都是類型錯誤致使的。爲了減小這種錯誤,咱們能夠在 React 中引入類型檢查模塊。
經常使用的檢查類型有一下幾種:
// 屬性能夠聲明爲 JS 原生類型 optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol
React-Redux 將全部組件分紅兩大類:UI 組件(presentational component)和容器組件(container component)。
UI 組件負責 UI 的呈現,容器組件負責管理數據和邏輯
React-Redux 提供 connect 方法,用於從 UI 組件生成容器組件。connect 的意思,就是將這兩種組件連起來。
// CashOut.jsx import { connect } from 'react-redux' class CashOut extends Component {} export default connect( state => ({ cashInfo: state.cashInfo }), { addToCashList, resetUseMoney } )(CashOut)
爲了定義業務邏輯,須要給出下面兩方面的信息。
connect 方法生成容器組件之後,須要讓容器組件拿到 state 對象,才能生成 UI 組件的參數。
React-Redux 提供 Provider 組件,可讓容器組件拿到 state。
import { Provider } from 'react-redux' import store from '@/store/store' const render = Component => { ReactDOM.render( //綁定redux、熱加載 <Provider store={store}> <Component /> </Provider>, document.getElementById('root') ) } render(Route)
使用 mocker-api coss-env
在 webpack-dev-server 的 before 鉤子函數中搭建服務器
運行:npm run start-mock
訪問:localhost:3000/api/getExtractList 查看數據
src 下新建 api 文件
|-src |-|-api |-|-|-api.js // 用來寫接口 |-|-|-server.js // 是對axios的封裝
server.js
axios 中文文檔 查看 axios 配置項
import axios from 'axios' const TIMEOUT = 30000 // 設置超時時間 export default class Server { axios(method, url, params) { return new Promise((resolve, reject) => { if (typeof params !== 'object') params = {} const _option = { method, url, baseURL: 'http://localhost:3002', timeout: TIMEOUT, params: null, data: null, headers: null, withCredentials: true, //是否攜帶cookies發起請求 validateStatus: status => { return status >= 200 && status < 300 }, ...params } axios.request(_option).then( res => { resolve( typeof res.data === 'object' ? res.data : JSON.parse(res.data) ) }, error => { if (error.response) { reject(error.response.data) } else { reject(error) } } ) }) } }
api.js
import Server from './server' class API extends Server { async getExtractList(data = {}) { try { let result = await this.axios('post', '/api/getExtractList', data) return result } catch (err) { throw err } } } export default new API()
一個關鍵問題沒有解決:異步操做怎麼辦?Action 發出之後,Reducer 當即算出 State,這叫作同步;Action 發出之後,過一段時間再執行 Reducer,這就是異步。
怎麼才能 Reducer 在異步操做結束後自動執行呢?這就要用到新的工具:中間件(middleware)。
Redux 的核心概念其實很簡單:將須要修改的 state 都存入到 store 裏,發起一個 action 用來描述發生了什麼,用 reducers 描述 action 如何改變 state tree 。建立 store 的時候須要傳入 reducer,真正能改變 store 中數據的是 store.dispatch API。
import { createStore, combineReducers, applyMiddleware } from 'redux' import * as cashout from './cashout/reducer' import thunk from 'redux-thunk' let store = createStore( combineReducers({ ...cashout }), applyMiddleware(thunk) // 使用中間件 ) export default store
store/action.js
// 獲取提現記錄列表,保存至redux export const getCashList = () => { return async dispatch => { try { const value = await API.getCashList() // 經過dispatch來更新store dispatch({ type: GETCASHLIST, value, initLoading: false }) } catch (err) { console.error(err) } } }
<p className="my-drawer" onClick={this.goTo.bind(this,'/home')} >首頁</p>
goTo (path,e){ this.setState({ visible: false, }); this.props.history.push(path) };
爲了實現需求網上搜到一種,就用在項目中。看到 React 官方文檔上提供的【狀態提高】溫度的例子,也是經過父組件提供函數,以 pros 形式傳遞給子組件,子組件調用 props 來修改父組件的 state。
父組件代碼以下:
class CashOut extends Component { // 在父組件中定義能夠改變state值得函數 transferVisible(visible) { this.setState({ visible }) } // 把transferVisible函數做爲屬性傳遞給Dialog子組件上 如 transferVisible render() { return ( <div> <Dialog title="提現成功" des="您以提現成功,可到提現記錄中查看" visible={this.state.visible} transferVisible={visible => this.transferVisible(visible)} ></Dialog> </div> ) } } export default CashOut
子組件代碼以下:
class Dialog extends React.Component { // 點擊確認和取消時 調用props修改父組件state值 handleCancel = () => { this.props.transferVisible(false) } render() { return ( <div> <Modal title={this.props.title} visible={this.props.visible} okText="確認" cancelText="取消" onOk={this.handleCancel} onCancel={this.handleCancel} > <p>{this.props.des}</p> </Modal> </div> ) } } export default Dialog
在個人訂單中,已付、待反 4 個 Tab 頁面請求都會發送兩次
(1)Request Method: OPTIONS
(2)Request Method: POST
跨域分爲:
1.簡單跨域
2.複雜跨域:複雜跨域會進行預檢
複雜跨域在發送真正的請求前, 會先發送一個方法爲 OPTIONS 的預請求(preflight request), 用於試探服務端是否能接受真正的請求,若是 options 得到的迴應是拒絕性質的,好比 404403500 等 http 狀態,就會中止 post、put 等請求的發出。
有三種方式會致使這種現象:
個人項目中出現這種狀況是由於使用 POST 請求,Content-Type 設置了 application/json
解決方法:
(1)POST 請求,Content-Type 設置 application/x-www-form-urlencoded
(2)使用 qs 對 object 進行轉換