redux principle
// 定义一个dispatch的方法,接收到动作之后,自动调用
const dispatch = (action) => {
changeState(action)
renderCount(countState)
}
创建createStore方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Redux principle 02</title>
</head>
<body>
<h1>redux principle</h1>
<div class="counter">
<span class="btn" onclick="store.dispatch({type: 'COUNT_DECREMENT', number: 10})">-</span>
<span class="count" id="count"></span>
<span class="btn" id="add" onclick="store.dispatch({type: 'COUNT_INCREMENT', number: 10})">+</span>
</div>
<script>
// 定义一个方法,用于集中管理state和dispatch
const createStore = (state, changeState) => {
// getState用于获取状态
const getState = () => state
// 定义一个监听器,用于管理一些方法
const listeners = []
const subscribe = (listener) => listeners.push(listener)
// 定义一个dispatch方法,让每次有action传入的时候返回render执行之后的结果
const dispatch = (action) => {
// 调用changeState来处理数据
changeState(state, action)
// 让监听器里的所以方法运行
listeners.forEach(listener => listener())
}
return {
getState,
dispatch,
subscribe
}
}
// 定义一个计数器的状态
const countState = {
count: 10
}
// 定一个方法叫changeState,用于处理state的数据,每次都返回一个新的状态
const changeState = (state, action) => {
switch(action.type) {
// 处理减
case 'COUNT_DECREMENT':
state.count -= action.number
break;
// 处理加
case 'COUNT_INCREMENT':
state.count += action.number
break;
default:
break;
}
}
// 创建一个store
const store = createStore(countState, changeState)
// 定义一个方法用于渲染计数器的dom
const renderCount = () => {
const countDom = document.querySelector('#count')
countDom.innerHTML = store.getState().count
}
// 初次渲染数据
renderCount()
// 监听,只要有dispatch,这个方法就会自动运行
store.subscribe(renderCount)
</script>
</body>
</html>
让changeState方法变为一个纯函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Redux principle 03</title>
</head>
<body>
<h1>redux principle</h1>
<div class="counter">
<span class="btn" onclick="store.dispatch({type: 'COUNT_DECREMENT', number: 10})">-</span>
<span class="count" id="count"></span>
<span class="btn" id="add" onclick="store.dispatch({type: 'COUNT_INCREMENT', number: 10})">+</span>
</div>
<script>
// 定义一个方法,用于集中管理state和dispatch
const createStore = (state, changeState) => {
// getState用于获取状态
const getState = () => state
// 定义一个监听器,用于管理一些方法
const listeners = []
const subscribe = (listener) => listeners.push(listener)
// 定义一个dispatch方法,让每次有action传入的时候返回render执行之后的结果
const dispatch = (action) => {
// 调用changeState来处理数据
state = changeState(state, action)
// 让监听器里的所有方法运行
listeners.forEach(listener => listener())
}
return {
getState,
dispatch,
subscribe
}
}
// 定义一个计数器的状态
const countState = {
count: 10
}
// 定一个方法叫changeState,用于处理state的数据,每次都返回一个新的状态
const changeState = (state, action) => {
switch(action.type) {
// 处理减
case 'COUNT_DECREMENT':
return {
...state,
count: state.count - action.number
}
// 处理加
case 'COUNT_INCREMENT':
return {
...state,
count: state.count + action.number
}
default:
return state
}
}
// 创建一个store
const store = createStore(countState, changeState)
// 定义一个方法用于渲染计数器的dom
const renderCount = () => {
const countDom = document.querySelector('#count')
countDom.innerHTML = store.getState().count
}
// 初次渲染数据
renderCount()
// 监听,只要有dispatch,这个方法就会自动运行
store.subscribe(renderCount)
</script>
</body>
</html>
合并state和changeState(最终版)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Redux principle 04</title>
</head>
<body>
<h1>redux principle</h1>
<div class="counter">
<span class="btn" onclick="store.dispatch({type: 'COUNT_DECREMENT', number: 10})">-</span>
<span class="count" id="count"></span>
<span class="btn" id="add" onclick="store.dispatch({type: 'COUNT_INCREMENT', number: 10})">+</span>
</div>
<script>
// 定义一个方法,用于集中管理state和dispatch, changeState改名了,专业的叫法是reducer
const createStore = (reducer) => {
// 定义一个初始的state
let state = null
// getState用于获取状态
const getState = () => state
// 定义一个监听器,用于管理一些方法
const listeners = []
const subscribe = (listener) => listeners.push(listener)
// 定义一个dispatch方法,让每次有action传入的时候返回reducer执行之后的结果
const dispatch = (action) => {
// 调用reducer来处理数据
state = reducer(state, action)
// 让监听器里的所有方法运行
listeners.forEach(listener => listener())
}
// 初始化state
dispatch({})
return {
getState,
dispatch,
subscribe
}
}
// 定义一个计数器的状态
const countState = {
count: 10
}
// 定一个方法叫changeState,用于处理state的数据,每次都返回一个新的状态
const changeState = (state, action) => {
// 如果state是null, 就返回countState
if (!state) return countState
switch(action.type) {
// 处理减
case 'COUNT_DECREMENT':
return {
...state,
count: state.count - action.number
}
// 处理加
case 'COUNT_INCREMENT':
return {
...state,
count: state.count + action.number
}
default:
return state
}
}
// 创建一个store
const store = createStore(changeState)
// 定义一个方法用于渲染计数器的dom
const renderCount = () => {
const countDom = document.querySelector('#count')
countDom.innerHTML = store.getState().count
}
// 初次渲染数据
renderCount()
// 监听,只要有dispatch,renderCount就会自动运行
store.subscribe(renderCount)
</script>
</body>
</html>
使用Redux框架
Redux的流程:
1.store通过reducer创建了初始状态
2.view通过store.getState()获取到了store中保存的state挂载在了自己的状态上
3.用户产生了操作,调用了actions 的方法
4.actions的方法被调用,创建了带有标示性信息的action
5.actions将action通过调用store.dispatch方法发送到了reducer中
6.reducer接收到action并根据标识信息判断之后返回了新的state
7.store的state被reducer更改为新state的时候,store.subscribe方法里的回调函数会执行,此时就可以通知view去重新获取state
Reducer必须是一个纯函数:
Reducer 函数最重要的特征是,它是一个纯函数。也就是说,只要是同样的输入,必定得到同样的输出。Reducer不是只有Redux里才有,之前学的数组方法reduce
, 它的第一个参数就是一个reducer
纯函数是函数式编程的概念,必须遵守以下一些约束。
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
由于 Reducer 是纯函数,就可以保证同样的State,必定得到同样的 View。但也正因为这一点,Reducer 函数里面不能改变 State,必须返回一个全新的对象,请参考下面的写法。
// State 是一个对象
function reducer(state = defaultState, action) {
return Object.assign({}, state, { thingToChange });
// 或者
return { ...state, ...newState };
}
// State 是一个数组
function reducer(state = defaultState, action) {
return [...state, newItem];
}
最好把 State 对象设成只读。要得到新的 State,唯一办法就是生成一个新对象。这样的好处是,任何时候,与某个 View 对应的 State 总是一个不变(immutable)的对象。
我们可以通过在createStore中传入第二个参数来设置默认的state,但是这种形式只适合于只有一个reducer的时候。
划分reducer:
因为一个应用中只能有一个大的state,这样的话reducer中的代码将会特别特别的多,那么就可以使用combineReducers方法将已经分开的reducer合并到一起
注意:
1、分离reducer的时候,每一个reducer维护的状态都应该不同
2、通过store.getState获取到的数据也是会按照reducers去划分的
3、划分多个reducer的时候,默认状态只能创建在reducer中,因为划分reducer的目的,4、就是为了让每一个reducer都去独立管理一部分状态
最开始一般基于计数器的例子讲解redux的基本使用即可。
关于action/reducer/store的更多概念,请查看官网
Redux异步
通常情况下,action只是一个对象,不能包含异步操作,这导致了很多创建action的逻辑只能写在组件中,代码量较多也不便于复用,同时对该部分代码测试的时候也比较困难,组件的业务逻辑也不清晰,使用中间件了之后,可以通过actionCreator异步编写action,这样代码就会拆分到actionCreator中,可维护性大大提高,可以方便于测试、复用,同时actionCreator还集成了异步操作中不同的action派发机制,减少编码过程中的代码量
常见的异步库:
- Redux-thunk(就讲这个)
- Redux-saga
- Redux-effects
- Redux-side-effects
- Redux-loop
- Redux-observable
- …
基于Promise的异步库:
- Redux-promise
- Redux-promises
- Redux-simple-promise
- Redux-promise-middleware
- …
容器组件(Smart/Container Components)和展示组件(Dumb/Presentational Components)
使用react-redux
可以先结合context
来手动连接react和redux。
react-redux提供两个核心的api:
- Provider: 提供store
- connect: 用于连接容器组件和展示组件
- Provider
根据单一store原则 ,一般只会出现在整个应用程序的最顶层。 - connect
语法格式为
connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)(component)
一般来说只会用到前面两个,它的作用是:
- 把
store.getState()
的状态转化为展示组件的props
- 把
actionCreators
转化为展示组件props
上的方法
特别强调:
官网上的第二个参数为mapDispatchToProps, 实际上就是actionCreators
只要上层中有Provider
组件并且提供了store
, 那么,子孙级别的任何组件,要想使用store
里的状态,都可以通过connect
方法进行连接。如果只是想连接actionCreators
,可以第一个参数传递为null
React Router
React Router现在的版本是5, 于2019年3月21日搞笑的发布,搞笑的官网链接
, 本来是要发布4.4的版本的,结果成了5。从4开始,使用方式相对于之前版本的思想有所不同。之前版本的思想是传统的思想:路由应该统一在一处渲染, Router 4之后是这样的思想:一切皆组件
React Router包含了四个包:
主要使用react-router-dom
使用方式
正常情况下,直接按照官网
的demo就理解 路由的使用方式,有几个点需要特别的强调:
- Route组件的exact属性
exact
属性标识是否为严格匹配, 为true
是表示严格匹配,为false
时为正常匹配。
- Route组件的render属性而不是component属性
怎么在渲染组件的时候,对组件传递属性呢?使用component
的方式是不能直接在组件上添加属性的。所以,React Router的Route
组件提供了另一种渲染组件的方式 render
, 这个常用于页面组件级别的权限管理。
- 路由的参数传递与获取
- Switch组件
总是渲染第一个匹配到的组件
- 处理404与默认页
- withRoute高阶组件的使用
- 管理一个项目路由的方法
- code spliting
- HashRouter和BrowserRouter的区别,前端路由和后端路由的区别。这个在Vue里应该有讲过了。
React Router基本原理
一、网安学习成长路线图
网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、网安视频合集
观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
三、精品网安学习书籍
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、网络安全源码合集+工具包
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
五、网络安全面试题
最后就是大家最关心的网络安全面试题板块
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!