21.Flux和Redux

1.react-virtualized

是一个react中的虚拟化的库,使用它来做列表、Grid可以提高性能,减少dom操作。

  • Grid:用于优化构建任意网状的结构,传入一个二维的数组,渲染出类似棋盘的结构。

  • List:List是基于Grid来实现的,但是是一个维的列表,而不是网状。

  • Collection:类似于瀑布流的形式,同样可以水平和垂直方向滚动。

2.Flux

Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC 架构是同一类东西,但是更加简单和清晰

Flux把应用分为4个部分:

  1. View : 视图

  2. Action :动作

  3. Dispatcher :派发器,用来接收Action

  4. Store : 数据层

Flux最大特点:数据单向流动

3.Redux

我们把Flux看做一个框架的理念的话,Redux是Flux的一种实现

1
2
3
4
Redux在此基础上强调三个基本原则:
1.唯一数据源 :唯一数据源指的是应用的状态数据应该只存储在唯一的一个Store上。
2.保持状态只读 : 保持状态只读,就是说不能去直接修改状态,要修改Store的状态,必须要通过派发一个action对象完成。
3.数据改变只能通过纯函数完成 :这里所说的纯函数就是把Reducer,Reducer描述了state状态如何修改。Redux这个名字的前三个字母Red代表的就是Reducer,其实Redux名字的含义就是Reducer+Flux。

BP

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
47
//1.安装redux   yarn add redux


//2.引入createStore,用来创建一个Store对象
import { createStore } from 'redux'

//3.创建actions。action是改变state的唯一途径,返回一个普通的javascript对象,
//它描述了一个行为且是改变state的唯一途径。
//这个行为我们需要通过dispatch函数来触发action,从而改变对应的state。
//action必须带有type指明具体的行为名称,且能附带上额外的信息。
function addFn() {
return { type:'ADD' }
}
function minusFn() {
return { type:'MINUS' }
}


/*
4.创建reducer
这是一个 reducer,形式为 (state, action) => state 的纯函数。
该reducer可以接收action,描述如何通过action把state转变成下一个state。
state的形式取决于你,可以是基本类型、数组、对象、甚至是Immutable.js 生成的数据结构。
惟一的要点是当state变化时需要返回全新的对象,而不是修改传入的参数。
*/
function counter(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
case 'MINUS':
return state - 1
default:
return state
}
}


//5.创建一个store。store对象的API 有 { subscribe, dispatch, getState }。
let store = createStore(counter)

//6.订阅更新,监听state的变化
store.subscribe(() => console.log(store.getState(),"xxxx"))

//7.通过dispatch来触发action修改state
store.dispatch(addFn())
store.dispatch(addFn())
store.dispatch(minusFn())

3.1 异步处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
异步处理:
1. yarn add redux-thunk

2. const store=createStore(couter ,applyMiddleware(thunk))
import { createStore,applyMiddleware } from 'redux'

3. 声明异步action
function addAsynFn() {
return dispatch=>{
setTimeout(()=>{
dispatch(addFn())
},2000)
}
}

react-redux是在react中使用redux的一个插件,使用它可以方便快捷的在react中用redux。

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
//react-redux的使用步骤
//1.安装 yarn add react-redux

//2.react-redux 给我们提供了两个接口:一个Provider,一个是connect
//2.1 Provider组件的作用:在子组件中快速获取到store对象
//2.2 connect组件的作用:连接React组件与Redux store。连接操作不会改变原来的组件类,反而返回一个新的已与Redux store连接的组件类。

//3.在index.js中引入Provider组件,并且作为项目根组件包裹App组件,然后传入store
ReactDOM.render(<Provider store={store}><App/></Provider>, document.getElementById('root'));

//4.在需要视同redux的组件中引入connect组件,然后使用connect将当前组件和redux的store关联
import { connect } from 'react-redux'
import { addFn, minusFn, addAsynFn } from './actions/index'
//会将store中state的数据映射到组件的props中
const mapStatetoProps = (state) => {
return { num: state }
}
//会将actions映射到组件的props中
const mapDispatchToProps = { addFn, minusFn, addAsynFn }
//使用connect返回一个已经把App和store联系起来的一个组件
App = connect(mapStatetoProps, mapDispatchToProps)(App)
export default App;


//5.在组件的页面中就可以使用store了
const num = this.props.num
const addFn = this.props.addFn
const minusFn = this.props.minusFn
const addAsynFn = this.props.addAsynFn

return (
<div className="App">
<h1>你好吗?</h1>
{<p>现在的总数是:{num}</p>}
{<button onClick={addFn}>加1</button>}
{<button onClick={minusFn}>减1</button>}
{<button onClick={addAsynFn}>异步加1</button>}
</div>
);

合并多个reducers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1.使用combineReducers合并多个reducer
import {combineReducers} from 'redux'

import {counter} from './counter';
import {location} from './location';

export default combineReducers({counter,location})

//2.在index.js中引入合并后的reducer,并且使用合并后的reducer创建store
import reducer from './reducers/index'
let store = createStore(reducer,applyMiddleware(thunk))

//3.在页面使用store的时候需要从state中获取对应的数据
const mapStatetoProps = (state) => {
return {num:state.counter,location:state.location}
}

4.redux的不可变数据

在redux中整个应用共用一个state。如何判断state更新呢?Redux采用不可变数据。

我们在判断数据是否相同时,并不需要深入判断数据对象的值是否相同,只需要浅比较即可,也就是判断是否为同一个数据对象地址,因为不可变数据对象在数据变化时均会重新创建一个新的数据对象,数据对象的地址不会相同。这也就是为什么在Reactjs,Redux中才有不可变数据对象。

5.fetch的使用

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
//安装  yarn add whatwg-fetch es6-promise

fetch('/api/home', {
credentials: "include",
/*
fetch不管在同域还是在跨域的情况下,默认都不携带cookie的,所以那些需要权限验证的请求就无法正常获取到数据,这时候需要配置credentials项,有一下三个选项可添:
omit: 默认值,忽略cookie的发送
same-origin: 表示cookie只能同域发送,不能跨域发送
include: 表示既可以同域发送,也可以跨域发送
*/
})
.then(response => {
//有三种方式解析获取到的数据:
//1 json数据 用reponse.json()来解析
//2 xml格式文件 用response.text()来解析
//3 图片文件 用response.blob()来解析
return response.json();
})
.then((data) => {
console.log(data);
})
}

const handleRequestPOST = () => {
//post请求需要提交的数据
fetch('/api/submit', {
method: 'post', //请求方式
credentials: "include",
headers: {
//post请求格式
'Content-Type': 'application/x-www-form-urlencoded'
},
//post请求发送的数据需要是这样的
body: "name=zhangsan&content=宝宝不开心"
})
.then(response => response.json())
.then(data => {
console.log(data);
}).catch(error => console.log('error is', error));

代理的配置:

  1. yarn add http-proxy-middleware

  2. 在src目录下创建setupProxy.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(
proxy("/api/*", {
target: "http://localhost:9999/",
changeOrigin: true
})
);
// app.use(
// proxy("/fans/**", {
// target: "https://easy-mock.com/mock/5c0f31837214cf627b8d43f0/",
// changeOrigin: true
// })
// );
};

5.1 fetch的封装

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
import 'whatwg-fetch'
import 'es6-promise'

//导出get请求
export function fetchget(url) {
var result = fetch(url, {
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*'
}
});
return result.then(response => response.json());
}

// 发送 post 请求
export function fetchpost(url, paramsObj) {
var result = fetch(url, {
method: 'POST',
credentials: 'include',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: obj2params(paramsObj)
});
return result.then(response => response.json());
}

// 将对象拼接成 key1=val1&key2=val2&key3=val3 的字符串形式
function obj2params(obj) {
var result = '';
var item;
for (item in obj) {
result += '&' + item + '=' + encodeURIComponent(obj[item]);
}

if (result) {
result = result.slice(1);
}

return result;
}