乐趣区

react项目配置及redux使用流程详细记录

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})
退出移动版