Vuex
基本使用
vue add vuex
状态和状态变更
state保存数据状态,mutations用于修改状态,store.js :
export default new Vuex.Store({
state: { count:0 },
mutations: {
increment(state) {
state.count += 1;
}
}
})
使用状态
<template>
<div>
<div>{{$store.state.count}}</div>
<button @click="add">加一</button>
</div>
</template>
<script>
export default {
methods:{
add(){
this.$store.commit('increment')
}
}
}
</script>
派生状态 - getters
从state派生出新状态,类似计算属性:
export default new Vuex.Store({
getters: {
left(state) {
// 计算剩余数量
return 10 - state.count;
}
}
})
使用getters
<span>还剩{{$store.getters.left}}个</span>
动作 - actions
复杂业务逻辑,类似于controller:
export default new Vuex.Store({
actions: {
increment({ getters, commit }) {
// 添加业务逻辑
if (getters.left > 0) {
commit("increment");
return true;
}
return false;
},
asyncIncrement({ dispatch }) {
// 异步逻辑返回Promise
return new Promise(resolve => {
setTimeout(() => {
// 复用其他action
resolve(dispatch("increment"));
}, 1000);
});
},
}
})
使用actions:
<template>
<div id="app">
<button @click="asyncAdd">异步加一</button>
</div>
</template>
<script>
export default {
methods: {
add() {
// 即使action执行同步代码返回的结果依然是promise
this.$store.dispatch("increment").then(result => {
if (!result) { alert("失败"); }
});
},
asyncAdd() {
this.$store.dispatch("asyncIncrement").then(result => {
if (!result) { alert("失败"); }
});
}
};
</script>
模块化
按模块化的方式编写代码,store.js:
const count = {
namespaced: true,
// ...
};
export default new Vuex.Store({
modules: {a: count}
});
使用变化:
//使用时加上前缀:模块名/
<template>
<div id="app">
<div>{{$store.state.a.count}}</div>
<p>{{$store.getters['a/score']}}</p>
<button @click="add">加一</button>
<button @click="addAsync">异步加一</button>
</div>
</template>
<script>
export default {
methods: {
add() {
this.$store.commit("a/increment");
},
addAsync() {
this.$store.dispatch("a/incrementAsync");
}
}
};
</script>
源码分析
实现一个Store的类,里面需要包含dispatche方法,commit方法,然后本身是一个插件,需要实现install方法,并混入到vue的生命周期中。
开始写源码
class Store(){
//options是配置对象store.js,里面包含了state,actions,mutations,getters...
constructor(options={}){
//把传递进来的state作响应话处理
this.state = new Vue({
data:options.state
})
//保存mutations
this.mutations = options.mutations || {}
//保存actions
this.actions = options.actions || {}
// 保存getters
this.getters = {}
// 拦截getters
Object.keys(options.getters).forEach(item=>{
this.registerGetter(this,options.getters[item],item)
})
}
//实现dispatche方法
dispatche(type,arg){
//type是actions中的函数名,执行action时,注入getter,commit,state,dispatche...
this.actions[type]({
getter:options.getter,
commit:this.commit,
state:this.state,
dispatche:this.dispatche,
},arg)
}
//实现commit方法,这里用箭头函数,action中会调用commit
commit=(type,arg)=>{
//type是mutations中的函数名,执行mutation时,注入state
this.mutations[this.type](this.state,arg)
}
// 定义一个拦截getters的方法
const registGetter = functions(store,fn,name) =>{
Object.defineProperty(store.getters,name,{
get:function(){
return fn(store.state) //注入state
}
})
}
}
//实现插件机制
function install(Vue){
Vue.mixin({
beforeCreate(){
if(this.$options.store){
//挂载全局对象$store
Vue.prototype.$store = this.$options.store
}
}
})
}
export default {Store,install}
Redux
流程控制
store保存状态,dispatch(action)派发一个更新操作,reducer纯函数负责状态更新,getState获取状态。
什么是reducer
reducer 就是⼀个纯函数,接收旧的 state 和 action,返回新的state。
(previousState, action) => newState
什么是reduce? 组件聚合
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
定义三个函数
function f1() {
console.log("f1");
}
function f2() {
console.log("f2");
}
function f3() {
console.log("f3");
}
要想输出f1,f2,f3,可以使用聚合函数
//方法一
f1();f2();f3()
//方法二
f3(f2(f1()));
//方法三,聚合函数
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => {
a(b(...args))
})
}
compose(
f1,
f2,
f3,
)();
redux基本使用
1. 需要⼀个store来存储数据(createStore)
2. store⾥的reducer初始化state并定义state修改规则
3. 通过dispatch⼀个action来提交对数据的修改
4. action提交到reducer函数⾥,根据传⼊的action的type,返回新的state
5. subscribe 变更订阅
6. getState 获取状态值
这里尤其要注意reduc中订阅数据变更的逻辑:
import store from './store/ReduxStore'
const render = ()=>{
ReactDom.render(
<App/>,
document.querySelector('#root')
)
}
render()
store.subscribe(render)
每次都重新调⽤render和getState太low了,想⽤更react的⽅式来写,需要react-redux⽀持
提供了两个api:
1. Provider 为后代组件提供store
2. connect 为组件提供数据和变更⽅法
react-redux基本使用
全局提供store,index.js
import React from 'react'
import ReactDom from 'react-dom'
import App from './App'
import store from './store/ReactReduxStore'
import { Provider } from 'react-redux'
ReactDom.render(
<Provider store={store}>
<App/>
</Provider>,
document.querySelector('#root')
)
获取状态数据
import React, { Component } from "react";
import { connect } from "react-redux";
class ReactReduxPage extends Component {
render() {
const { num, add, minus, asyAdd } = this.props;
return (
<div>
<h1>ReactReduxPage</h1>
<p>{num}</p>
<button onClick={add}>add</button>
<button onClick={minus}>minus</button>
{/* <button onClick={asyAdd}>asyAdd</button>*/}
</div>
);
}
}
const mapStateToProps = state => {
return {
num: state,
};
};
const mapDispatchToProps = {
add: () => {
return { type: "add" };
},
minus: () => {
return { type: "minus" };
},
// 必须使用中间件才能运作
// asyAdd: () => {
// setTimeout(() => {
// return { type: "add" };
// }, 1000);
// },
};
export default connect(
mapStateToProps, //状态映射 mapStateToProps
mapDispatchToProps, //派发事件映射
)(ReactReduxPage)
异步
Redux只是个纯粹的状态管理器,默认只⽀持同步,实现异步任务⽐如延迟,⽹络请求,需
中间件的⽀持,⽐如我们试⽤最简单的redux-thunk和redux-logger
import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import thunk from "redux-thunk";
import counterReducer from './counterReducer'
const store = createStore(counterReducer,applyMiddleware(logger, thunk));
然后就可以进行异步操作了
asyAdd: () => dispatch => {
setTimeout(() => {
// 异步结束后,⼿动执⾏dispatch
dispatch({ type: "add" });
}, 1000);
},
redux源码分析
redux中需要实现一个createStore方法,参数(reducer,applyMiddleware),所以还需要实现applyMiddleware方法,内部维护者一个state,同时需要返回getState,dispatch,subcribe。
redux源码实现
export function createStore(reducer,enhancer){
//enhancer=applyMiddleware(...)
if(enhancer){
//如果由中间件,就使用中间件处理
return enhancer(createStore)
}
//维护的状态
let currentState = undefined
//监听者
let currentListeners = []
//获取状态的函数
getState(){
return currentState
}
//派发事件额函数
dispatch(action){
//修改state
curentState = reducer(curentState,action)
//通知监听者更新
currentListeners.forEach(func=>func())
//返回action供中间件使用,如打印日志
return action
}
//订阅的函数
subcribe(listener){
currentListeners.push(listener)
}
//初始化currentState,走reducer中的默认流程,type匹配不上即可
dispatch({type:'init-state-xxx'})
//返回一个对象,包含上面的方法
return {getState,dispatch,subscribe}
}
//中间件实现
export function applyMiddleware(...middlewares){
//enhance(createStore)
return createStore => (...args) =>{
const store = createStore(...args)
//获取dispatch,目的是为了加工返回一个新的dispatch
let dispatch = store.dispatch
// 传递给中间件的参数,中间件就可以获取状态,改变状态了
let params = {
getState:store.getState,
dispatch:dispatch
}
//链式调用中间件
const middlewareChain = middlewares.map(md=>md(params))
//聚合函数加工dispatch
dispatch = compose(...middlewareChain)(store.dispatch)
//返回的是被加工处理了的dispatch
return {...store,dispatch}
}
}
reduc-thunk,redux-logger
function logger({ getState }) {
//compose(...middlewareChain)(store.dispatch)
return dispatch => action => {
action.type && console.log(action.type + "执行啦!");
return dispatch(action);
};
}
function thunk({ getState}) {
//compose(...middlewareChain)(store.dispatch)
return dispatch => action => {
//asyAdd: () => (dispatch,getState) => {
//setTimeout(() => {
//dispatch({ type: "add" });
//}, 1000);
//},
//上面这种情况就是action就是function
if (typeof action === "function") {
return action(dispatch, getState);
} else {
return dispatch(action);
}
};
}
react-redux源码分析
1.实现⼀个⾼阶函数connect,可以根据传⼊状态映射规则函数和派发器映射规则函数映射需
的属性(mapStateToProps,mapDispatchToProps),可以处理变更检测和刷新任务。
2.实现⼀个Provider组件可以传递store。
react-redux源码实现
//hooks实现Provider,connect
import React, {useState,useEffect,useContext} from 'react'
//创建context
const Context = React.createContext()
//导出Provide组件
export function Provider({store,children}){
return <Context.Provider value={store}>{children}</Context.Provider>
}
//导出高阶组件connect,接受mapStateToProps,mapDispatchToProps,和一个组件作为参数,返回一个组件
export const connect = (
mapStateToProps:state=>state, //mapStateToProps的默认值
mapDispatchToProps: {}
) => Cmp => props =>{
//从Provider的value中获取store
const store = userContext(Context)
//定义一个方法获取最新的moreProps,初始化和数据变更时都要用
getMoreProps(){
const stateProps = mapStateToProps(store.getState()),
//{add:()=>{type:'add'}}=> add:dispatch(type:'add')
const dispatchProps = bindActionCreators({
mapDispatchToProps,
store.dispatch
})
return {...stateProps,...dispatchProps }
}
//对新加入的props做响应式处理
useEffect(()=>{
store.subscribe(()=>{
//获取最新的数据并setState
setMorePros(getMoreProps())
})
},[])
//将要map的所有新属性放在moreProps中
const [moreProps,setMorePros] = useState(getMoreProps())
//返回一个添加了新属性的组件
return <Cmp {...props} {...moreProps} />
}
// 定义一个工具函数转换mapDispatchToProps
// {add:()=>{type:'add'}}=> add:()=>dispatch(type:'add')
function bindActionCreators(actionCreators,dispatch){
var obj = {}
for(var key in actionCreators){
obj[key] = dispatch(actionCreators[key])
}
return obj
}