react項目配置及redux使用流程(詳細記錄)
以TodoList爲例,項目地址:https://github.com/mandyshen9...
react項目創建及配置
首先創建react項目:
creact-react-app reactdemo
修改默認配置:
對 create-react-app
的默認配置進行自定義,這裏我們使用 react-app-rewired(一個對 create-react-app 進行自定義配置的社區解決方案)。
$ yarn add react-app-rewired customize-cra
修改package.json:
/* package.json */
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test",
+ "test": "react-app-rewired test",
}
然後在項目根目錄創建一個 config-overrides.js
用於修改默認配置。
module.exports = function override(config, env){
// do staff with the webpack config...
return config
}
配置按需加載:
babel-plugin-import 是一個用於按需加載組件代碼和樣式的 babel 插件(原理),現在我們嘗試安裝它並修改 config-overrides.js
文件。
yarn add babel-plugin-import
修改config-overrides.js
文件:
const { override, fixBabelImports } = require('customize-cra')
module.exports = override(
fixBabelImports('import',{
libraryName: 'antd', // 或其他第三方組件庫名稱
libiaryDirectory: 'es', // 組件位置
style: 'css',
})
)
配置less
配置less
: 我們可以引入 customize-cra
中提供的 less 相關的函數 addLessLoader 來幫助加載 less 樣式,同時修改 config-overrides.js
文件如下。
yarn add less less-loader
const { override, fixBabelImports, addLessLoader } = require('customize-cra')
module.exports = override(
fixBabelImports('import',{
libraryName: 'antd', // 或其他第三方組件庫名稱
libiaryDirectory: 'es', // 組件位置
style: true,
}),
addLessLoader({
javascriptEnabled: true,
})
)
<hr/>
<hr/>
redux的使用
安裝redux:
yarn add redux
從圖片中可以看出,Redux工作流程中有四個部分,最重要的就是store這個部分,因爲它把所有的數據都放到了store中進行管理。在編寫代碼的時候,因爲重要,所以要優先編寫store。
(1)創建src/store/index.js
,就是整個項目的store文件。
/**
* index.js 文件就是整個項目的store文件
*/
import { createStore } from 'redux' // 引入 createStore方法
import reducer from './reducer' // 引入reducer
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // 使瀏覽器中redux-devtool插件生效
) // 創建數據存儲倉庫
export default store // 將倉庫暴露出去
(2)創建src/store/reducer.js
,有管理能力的模塊。
store只是一個倉庫,它並沒有管理能力,它會把接收到的action自動轉發給reducer。
/**
* reducer暴露出去的就是一個方法函數,有兩個參數:state 和 action。
* state: 是整個項目中需要管理的數據信息。
*/
/**
* 一定要注意: reducer裏只能接收state,不能改變state。
* 不要認爲把業務邏輯寫在了reducer中,那改變state值的一定是reducer。
* 其實不然,reudcer只是返回了更改的數據,操作的是newState,但是並沒有更改store中的state數據,store拿到了reducer的數據,自己對自己進行了更新。
*/
const defaultState = {} // 默認數據
export default (state = defaultState, action) => {
if (action.type === CHANGE_INPUT) {
let newState = JSON.parse(JSON.stringify(state)) // 深度拷貝state
newState.inputValue = action.value
return newState
}
if (action.type === ADD_ITEM) {
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue) //push新的內容到列表中去
newState.inputValue = ''
return newState
}
// 其他類似操作流程...
return state
}
(3)組件獲取state中的數據。
import store from '../store/index' // 在組件中引入store
class TodoList extends Component {
constructor(props) {
super(props)
this.state = store.getState() // 從store中獲取state數據
store.subscribe(this.storeChange) // 訂閱Redux的狀態
}
/**
* 當訂閱的redux狀態變化時,使用setState()方法,將新的數據存入state中。
*/
storeChange = () => {
this.setState(store.getState())
}
}
(4) 創建src/store/actionTypes.js
如果需要action的地方我們就自己命名一個type,會出現兩個基本問題:
- 這些types如果不統一管理,不利於大型項目的複用,甚至會產生冗餘代碼。
- 因爲action裏的type,一定要和reducer裏的type一一對應,所以這部分代碼或字母寫錯後,瀏覽器裏並沒有明確的報錯,這給調試帶來了極大的困難。
所以新建立一個actionTypes.js文件,然後把type集中放到文件中進行管理。
export const ADD_ITEM = 'addItem'
export const // ...
// ...
(5) 創建src/store/actionCreators.js
把所有的redux action放到一個文件裏進行管理。
import { CHANGE_INPUT, ADD_ITEM, DELETE_ITEM, GET_LIST } from './actionTypes'
export const changeInputAction = (value) => ({
type: CHANGE_INPUT,
value
})
export const addItemAction = () => ({
type: ADD_ITEM
})
export const deleteItemAction = (index) => ({
type: DELETE_ITEM,
index
})
export const getListAction = (data) => ({
type: GET_LIST,
data
})
// ...
下面通過button
的點擊事件來熟悉redux流程。
組件:
import React, { Component } from 'redux'
import { addItemAction } from '.././store/actionCreators'
class List extends Component {
constructor(props){
super(props)
this.state = store.getState()
store.subscribe(this.storeChange)
}
storeChange = () => {
this.setState(store.getState())
}
clickBtn = () => {
action = addItemAction() // 返回一個對象{type: ADD_ITEM}
store.dispatch(action) // 通過dispatch()方法將action傳遞給store
}
render(){
return(
<div>
<button onClick={this.clickBtn}>增加</button>
<div/>
)
}
}
store/index.js,整個項目的store文件:
// store/index.js
import { createStore } from 'redux' // 引入 createStore方法
import reducer from './reducer' // 引入reducer
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
) // 創建數據存儲倉庫
export default store // 將倉庫暴露出去
store/reducer.js:
// store/reducer.js
import { ADD_ITEM } from './actionTypes'
const defaultState = {
inputValue: 'Write Something',
list: []
} // 默認數據
export default (state = defaultState, action) => {
if (action.type === ADD_ITEM) {
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue) //push新的內容到列表中去
newState.inputValue = ''
return newState
}
return state
}
store/actionTypes.js:
// store/actionTypes.js
export const ADD_ITEM = 'addItem'
store/actionCreators.js:
// store/actionCreators.js
import { ADD_ITEM } from './actionTypes'
export const addItemAction = () => ({
type: ADD_ITEM
})