redux状态管理,不光react可以用,vue,angular都可以用。
Redux 本身很简单。当使用普通对象来描述应用的 state 时。这个对象就像 “Model”,区别是它并没有 setter(修改器方法)。因此其它的代码不能随意修改它,造成难以复现的 bug。
要想更新 state 中的数据,你需要发起一个 action。Action 就是一个普通 JavaScript对象(注意到没,这儿没有任何魔法?)用来描述发生了什么。强制使用action来描述所有变化带来的好处是可以清晰
地知道应用中到底发生了什么。如果一些东西改变了,就可以知道为什么变。action就像是描述发生了什么的指示器。最终,为了把action和state串起来,开发一些函数,这就是reducer。再次地,没有任何魔
法,reducer 只是一个接收 state 和 action,并返回新的 state 的函数。 对于大的应用来说,不大可能仅仅只写一个这样的函数,所以我们编写很多小函数来分别管理state的一部分,再开发一个reducer
调用这两个 reducer,进而来管理整个应用的 state,差不多就是 Redux 思想的全部。
状态管理
一张图了解redux工作
例如初始一个状态管理,这是我建好的项目里面的三个js,index.js负责渲染,App.js是个组件,index.redux.js负责状态管理
index.js1
2
3
4
5
6
7
8
9
10
11import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import { createStore } from 'redux'
import { center } from './index.redux'
const store=createStore(center)
ReactDom.render(
<App store={store}/>,
document.getElementById('root')
)
App.js1
2
3
4
5
6
7
8
9
10
11
12
13import React,{Component} from 'react'
class App extends Component{
constructor(props){
super(props);
}
render(){
const sTore=this.props.store;
const data=sTore.getState();
return <h1>hellow app,现在有菜鸡${data}只</h1>
}
}
export default App
index.redux.js1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const ADD_CAI='加菜鸡';
const REMOVE_CAI='减菜鸡'
export function center(state=0,action){
switch(action.type){
case ADD_CAI:
return state+1;
case REMOVE_CAI:
return state-1;
default :
return 10
}
}
//action creator专门创建action
export function addCai(){
return {type:ADD_CAI}
}
export function removeCai(){
return {type:REMOVE_CAI}
}
改变状态的话,在App.js里面导入在index.redux.js里头定义的addCai,removeCai方法,然后添加两个按钮用来触发1
2
3<h1>hellow app,现在有菜鸡${data}只</h1>
<button onClick={()=>store.dispatch(addCai())}>添加一只菜鸡</button>
<button onClick={()=>store.dispatch(removeCai())}>菜死一只菜鸡</button>
这时候执行的话,其实状态已经改变了但是h1里面的数值没有改变,原因是状态虽然改变了,但是没有刷新,所以页面显示还是年轻的模样,只需要在index.js里面执行一下,store.subs
cribe(render),意思就是把render重新执行一下,这时候状态改变就可以了1
2
3
4
5
6
7
8
9function render(){
ReactDom.render(
<App store={store}/>,
document.getElementById('root')
)
}
render()
//状态改变之后执行一下render
store.subscribe(render)
当然,也可以把App.js里头import的两个方法转移到index.js,然后通过给子组件传值的方式传给App.js,这样代码看起来可能更和谐。
redux异步
redux默认只处理同步,异步需要中间件redux-thunk1
npm install redux-thunk --save
异步中间件使用,在index.js里头1
2
3
4
5
6
7import { createStore,applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
const store=createStore(
center,
applyMiddleware(thunk)
)
这时候就可以异步了,比如在index.redux.js里面模拟一个异步操作testAsync,然后在App.js里面添加一个button用来触发testAsync。1
<button onClick={()=>store.dispatch(testAsync())}>凌迟处死一只菜鸡</button>
react连接redux状态管理操作
将render函数改成1
2
3
4
5
6
7
8
9import { Provider } from 'react-redux'
ReactDom.render(
(
<Provider store={store} >
<App/>
</Provider>
),
document.getElementById('root'),
)
App.js里面导入connect以及index.redux里头的方法,这时候button里面的onClick事件处理函数直接可以写成{函数名}的形式,因为它这时候所有的数据都是直接从外部传进来的,所有的数据都是由index.react.js控制1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30import { connect } from 'react-redux'
import {addCai,removeCai,testAsync} from './index.redux'
class App extends Component{
constructor(props){
super(props);
}
render(){
const data=this.props.num;
const addCai=this.props.addCai;
const removeCai=this.props.removeCai;
const testAsync=this.props.testAsync;
return (
<div>
<h1>hellow app,现在有菜鸡{data}只</h1>
<button onClick={addCai}>添加一只菜鸡</button>
<button onClick={removeCai}>菜死一只菜鸡</button>
<button onClick={testAsync}>凌迟处死一只菜鸡</button>
</div>
)
}
}
const mapStateToProps=(state)=>{
return { num: state}
}
const actionCreators= {addCai,removeCai,testAsync}
App=connect(mapStateToProps,actionCreators)(App)
export default App
connect的第一个参数,把state给到props,第二个参数也给到props,也就是属性和方法
装饰器
用装饰器之前先装一下这个插件1
npm install babel-plugin-transform-decorators-legacy --save
然后在项目的package.json里面添加1
2
3
4
5{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
]
}
然后就可以做出这样的改变1
2
3
4
5
6
7
8
9
10const mapStateToProps=(state)=>{
return { num: state}
}
const actionCreators= {addCai,removeCai,testAsync}
App=connect(mapStateToProps,actionCreators)(App)
//==》
@connect(
state=>({num:state}),//属性
{addCai,removeCai,testAsync} //方法
)
合并所有reducer
1 | import combineReducers from 'redux' |
react-router4
react-router4是全新的版本,和之前的版本不兼容,浏览器和react-native均兼容和vue一样,React开发单页应用必备,践行路由即组件的概念比如:动态路由,Route,Link,Switch
安装
1 | npm install react-router-dom --save |
react-router-dom作为浏览器的路由
简单的路由例子1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47import React from 'react'
import ReactDom from 'react-dom'
import { createStore,applyMiddleware,compose } from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import App from './App'
import { center } from './index.redux'
import { BrowserRouter,Route,Link } from 'react-router-dom'
const store=createStore(center,compose(
applyMiddleware(thunk),
window.devToolsExtension? window.devToolsExtension():()=>{}
))//异步中间件
function Cai2(){
return <h1>hwllo this is Cai2</h1>
}
function Cai3(){
return <h1>hello this is Cai3</h1>
}
ReactDom.render(
(
<Provider store={store} >
<BrowserRouter>
<div>
<ul>
<li>
<Link to="/">第一只菜鸡</Link>
</li>
<li>
<Link to="cai2">第二只菜鸡</Link>
</li>
<li>
<Link to="cai3">第三只菜鸡</Link>
</li>
</ul>
<Route path="/" component={App}></Route>
<Route path="/cai2" component={Cai2}></Route>
<Route path="/cai3" component={Cai3}></Route>
</div>
</BrowserRouter>
</Provider>
),
document.getElementById('root'),
)
但是有个问题,就是点Cai2的时候,组件App也渲染出来,是因为它path匹配的时候根目录是/,而/cai2,/cai3也是有/
,所以都会显示,它其实是个正则匹配,这时候可以选择加一个exact进行完全匹配,就可以了。
假如要添加一个地址跳转错误的友好404,就像这样1
<Route path="/:localtion" component={Wrong}></Route>
Redirect:默认页面打开加载某个组件或者强制加载某个组件1
<Redirect to="/"></Redirect>
Switch:只渲染命中的第一个组件1
2
3
4
5
6<Switch>
<Route path="/" component={App}></Route>
<Route path="/:localtion" component={Wrong}></Route>
<Route path="/cai2" component={Cai2}></Route>
<Route path="/cai3" component={Cai3}></Route>
</Switch>
React-Router中的HashRouter与BrowserRouter
以下解释的前提是你要懂hash地址,假如有一个 Link 标签,跳转到 /hhardyy1
2BrowserRouter: http://localhost:8080/hhardyy
HashRouter: http://localhost:8080/#/hhardyy
如果有服务器端的动态支持,建议使用 BrowserRouter,否则建议使用 HashRouter。原因在于,如果是单纯的静态文件,假如路径从 / 切换到 /hhardyy 后,此时刷新页面,页面将无法正常访问。
二者的替换方法很简单,在import的时候,将 BrowserRouter 修改为 HashRouter 就可以了,不需要修改其他东西。