在本教程的【上篇】中,已经详细说明了React项目相对基础的部分。在【下篇】中,继续讲解React进阶的部分。还没有阅读【上篇】的同学请先在我的公众号中去阅读哦~
在开始前,先回顾下【上篇】介绍的内容:
1 创建React-APP
2 精简项目
2.1 删除文件
2.2 简化代码
2.3 使用Fragment去掉组件外层标签
3 项目目录结构
3.1 引入全局公用样式
3.2 支持Sass/Less/Stylus
4 路由
4.1 页面构建
4.2 使用react-router-dom
4.3 路由跳转
5 组件引入
5.1 创建header组件
5.2 引入Header组件
5.3 组件传参
6 React Developer Tools浏览器插件
在本次的【下篇】中,继续分享以下内容:
先睹为快
7 Redux及相关插件
7.1 安装redux
7.2 安装react-redux
7.3 安装redux-thunk
7.4 安装浏览器Redux插件
7.5 创建store
7.6 复杂项目store分解
7.7 对接react-redux与store
7.8 启动Redux DevTools
7.9 安装使用immutable
8 Mock.js安装与使用
9 解决本地开发跨域问题
10 其他常用工具
11 附赠章节:集成Ant Design
11.1 安装Ant Design
11.2 实现按需加载
11.3 自定义主题颜色
7 Redux及相关插件
做过vue开发的同学都知道vuex,react对应的工具就是Redux,当然还有一些附属工具,比如react-redux、redux-thunk、immutable。
redux涉及内容篇幅较多,可以单独作为一次分享。本次分享篇幅有限,仅简要介绍下安装部署流程,如有看不懂的地方可先跳过或自行查阅官方文档。
7.1 安装redux
执行:
npm install redux --save
仅安装redux也是可以使用的,但是比较麻烦。redux里更新store里的数据,需要手动订阅(subscribe)更新。可以借助另一个插件(react-redux)提高开发效率。
7.2 安装react-redux
执行:
npm install react-redux --save
react-redux允许通过connect方法,将store中的数据映射到组件的props,省去了store订阅。原state中读取store的属性改用props读取。
由于store(7.5小节)还没讲到,react-redux使用方法在7.6小节介绍。
7.3 安装redux-thunk
执行:
npm install redux-thunk --save
redux-thunk允许在actionCreators里返回函函数。这样可以把业务逻辑(例如接口请求)集中写在actionCreator.js,方便复用的同时,可以使组件的主文件更简洁。
7.4 安装浏览器Redux插件
为了更方便跟踪redux状态,建议安装chrome插件。
先科学上网,在chrome网上应用店里搜索“Redux DevTools”并安装。
安装完成后还不能直接使用,需要在项目代码中进行配置。接下来进行说明。
7.5 创建store
安装以上各种插件后,可以store用来管理状态数据了。
如果项目比较简单,只有一两个页面,可以只创建一个总store管理整体项目。目录结构参考如下:
├─ /src
+ | ├─ /store
+ | | ├─ actionCreators.js
+ | | ├─ contants.js <-- 定义方法的常量
+ | | ├─ index.js
+ | | └─ reducer.js
以下是各文件的代码示例:
src/store/actionCreators.js:
import * as constans from './constants'
export const getData = (data) => ({
type: constans.SET_DATA,
data
})
src/store/contants.js:
export const SET_DATA = 'SET_DATA'
src/store/index.js:
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'
// 这里让项目支持浏览器插件Redux DevTools
const composeEnhancers = typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
const store = createStore(
reducer,
enhancer
)
export default store
以上是store的核心代码,支持了Redux DevTools。同时,利用redux的集成中间件(applyMiddleware)功能将redux-thunk集成进来,最终创建了store。
src/store/reducer.js:
import * as constants from './constants'
// 初始默认的state
const defaultState = {
myData: null
}
export default (state = defaultState, action) => {
// 由于state是引用型,不能直接修改,否则是监测不到state发生变化的。因此需要先复制一份进行修改,然后再返回新的state。
let newState = Object.assign({}, state)
switch(action.type) {
case constants.SET_DATA:
newState.myData = action.data
return newState
default:
return state
}
}
以上代码,我们在store设置了一个myData。如何更好地解决state修改问题,在7.8小节会提到。
7.6 复杂项目store分解
应对更多页面的项目,如果数据都集中放在一个store里,其维护成本非常高。接下来分享下如何将store分解到各个组件中。
一般来说,每个组件有自己的store,再由src下的store作为总集,集成每个组件的store。
以header和login两个组件为例,分别创建组件自己的store。
header的store目录结构如下:
| | ├─ /components
| | | ├─ /header
+ | | | | ├─ /store
+ | | | | | ├─ actionCreators.js
+ | | | | | ├─ contants.js
+ | | | | | ├─ index.js
+ | | | | | └─ reducer.js
组件store下的index.js代码如下:
import reducer from './reducer'
import * as actionCreators from './actionCreators'
import * as constants from './constants'
export { reducer, actionCreators, constants}
其实就是把组件store下的其他文件集中起来作为统一输出口。
组件store下的contants.js代码如下:
const ZONE = 'components/header/'
export const SET_DATA = ZONE + 'SET_DATA'
ZONE是用来避免与其他组件的contants重名。
同样的方式,在login下进行创建store(不再赘述)。
然后修改项目src下的总store,目录结构变动如下:
├─ /src
| ├─ /store
- | | ├─ actionCreators.js <-- 删除
- | | ├─ contants.js <--删除
| | ├─ index.js
| | └─ reducer.js
src/store/index.js重写如下:
import { combineReducers } from 'redux'
import { reducer as loginReducer } from '../pages/login/store'
import { reducer as headerReducer } from '../components/header/store'
const reducer = combineReducers({
login: loginReducer,
header: headerReducer
})
export default reducer
以上代码的作用就是把login和header的store引入,然后通过combineReducers合并在一起,并分别加上唯一的对象key值。
这样的好处非常明显:
- 避免各组件的store数据互相污染
- 组件独立维护自己的store,减少维护成本
非常建议使用这种方式维护store。
7.7 对接react-redux与store
为了方便每个组件都能使用store,而不用一遍一遍的引用store。下面来对接react-redux与store。
修改src/index.js:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
+ import { Provider } from 'react-redux'
+ import store from './store'
import './common/style/frame.styl'
+ const Apps = (
+ <Provider store={store}>
+ <App />
+ </Provider>
+ )
M ReactDOM.render(Apps, document.getElementById('root'))
以上代码就是用react-redux提供的Provider,把store传给了整个App。
在需要使用store的组件中,要使用react-redux提供的connect方法对组件进行包装。
以login为例,修改src/pages/login/index.js:
import React, { Component } from 'react'
import Header from '../../components/header'
+ import { connect } from 'react-redux'
+ import * as actionCreators from './store/actionCreators'
import './login.styl'
class Login extends Component {
render() {
return (
<div className="P-login">
<Header />
<h1>Login page</h1>
+ <p>login: myData = {this.props.myData}</p>
+ <button onClick={()=> {this.props.getData('123456')}}>更改login的myData</button>
<button onClick={this.gotoHome.bind(this)}>跳转Home页</button>
</div>
)
}
gotoHome() {
this.props.history.push('/home')
}
}
+ // 把store中的数据映射到组件的props
+ const mapStateToProps = (state) => ({
+ myData: state.getIn(['login', 'myData']),
+ })
+ // 把store的Dispatch映射到组件的props
+ const mapDispatchToProps = (dispatch) => ({
+ getData(data) {
+ const action = actionCreators.getData(data)
+ dispatch(action)
+ }
+ })
M export default connect(mapStateToProps, mapDispatchToProps)(Login)
最大的变化就是代码最后一行,被connect方法包装了。
然后把store里的state和dispatch都映射到了组件的props。这样可以直接通过props进行访问了,store中数据的变化会直接改变props从而触发组件的视图更新。
点击按钮后,可以看到页面中显示的myData发生了变化。
下面通过Redux DevTools进行可视化跟踪查看。
7.8 启动Redux DevTools
经过7.5小节的设置,7.4小节的Redux DevTools可以正常使用了。点击浏览器右上角的图标,在出现的面板里,可以相信地跟踪查看store里各数据的变化,非常方便。
还可以通过调试工具栏启动Redux DevTools:
具体使用方法这里不赘述了。
7.9 安装使用immutable
在7.5小节,提到了store里不能直接修改state,因为state是引用类型,直接修改可能导致监测不到数据变化。
immutable.js从字面上就可以明白,immutable的意思是“不可改变的”。使用immutable创建的数据是不可改变的,对immutable数据的任何修改都会返回一个新的immutable数据,不会改变原始immutable数据。
immutable.js提供了很多方法,非常方便修改对象或数组类型的引用型数据。
安装immutable和redux-immutable,执行:
npm install immutable redux-immutable --save
然后对代码进行改造:
src/store/reducer.js:
- import { combineReducers } from 'redux'
+ import { combineReducers } from 'redux-immutable'
...(略)
以上代码就是把combineReducers换成redux-immutable里的。
然后修改src/pages/login/store/reducer.js
import * as constants from './constants'
+ import { fromJS } from 'immutable'
M const defaultState = fromJS({
myData: null
M })
+ const getData = (state, action) => {
+ return state.set('myData', action.data)
+ }
export default (state = defaultState, action) => {
switch(action.type) {
case constants.SET_DATA:
M return getData(state, action)
default:
return state
}
}
immutable的介入,就是利用fromJS方法,把原始的JS类型转化为immutable类型。
由于state已经是immutable类型了,可以使用immutable的set方法进行数据修改,并返回一个新的state。代码简洁很多,不需要手动通过Object.assign等方法去复制再处理了。
header组件的代码修改同理不再赘述。
immutable还有很多其他非常使用方法,具体请参阅官方文档:
8 Mock.js安装与使用
在开发过程中,为了方便前端独自调试接口,经常使用Mock.js拦截Ajax请求,并返回预置好的数据。本小节介绍下如何在react项目中使用Mock.js。
执行安装:
npm install mockjs --save
在src下新建mock.js,代码如下:
import Mock from 'mockjs'
const domain = '/api/'
// 模拟getData接口
Mock.mock(domain + 'getData', function () {
let result = {
code: 200,
message: 'OK',
data: 'test'
}
return result
})
然后在src/index.js中引入mock.js:
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { Provider } from 'react-redux'
import store from './store'
+ import './mock'
import './common/style/frame.styl'
...(略)
如此简单。这样,在项目中请求/api/getData
的时候,就会被Mock.js拦截,并返回mock.js中写好的数据。
9 解决本地开发跨域问题
在react开发环境中,默认启动的是3000端口,而后端API服务可能在本机的80端口,这样在ajax请求的时候会出现跨域问题。可以借助http-proxy-middleware工具实现反向代理。
执行安装:
npm install http-proxy-middleware --save-dev
在src下创建setupProxy.js,代码如下:
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
'^/api',
proxy({
target: 'http://localhost',
changeOrigin: true
})
)
}
这代码的意思就是,只要请求地址是以"/api"开头,那就反向代理到http://localhost域名下,跨域问题解决!大家可以根据实际需求进行修改。
※注意:setupProxy.js设置后,一定要重启项目才生效。
10 其他常用工具
- Axios - Ajax请求工具
【安装命令】
npm install axios --save
- better-scroll - 页面原生滚动体验效果工具
【官网】ustbhuangyi.github.io/better-scro…
【安装命令】
npm install better-scroll --save
- react-transition-group - CSS3动画组合工具
【安装命令】
npm install react-transition-group --save
- react-loadable - 动态加载组件工具
【官网】www.npmjs.com/package/rea…
【安装命令】
yarn add react-loadable
11 附赠章节:集成Ant Design
Ant Design是非常好用的前端UI库,很多项目都使用了Ant Design。
【官网】
本章节内容基于上述章节将create-react-app进行eject后集成Ant Design。
具体内容请移步至我的微信公众号阅读^_^
11.1 安装Ant Design
11.2 实现按需加载
11.3 自定义主题颜色
请关注微信公众号「卧梅又闻花」,查阅《超全面详细一条龙教程!从零搭建React项目全家桶(下篇)》
项目GitHub
本次分享涉及的项目代码已全部上传至GitHub,有需要的同学可前往自行下载:
本次React全家桶分享到这里就全部结束啦,欢迎在微信公众号私信与我交流。
欢迎关注我的个人微信公众号,随时获取最新文章。