🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 第一版 ``` let appState = { title:{color:'red',text:'标题'} ,content:{color:'green',text:'内容'} }; // change(appState); function renderTitle(title){ // appState = null; let element = document.querySelector('#title'); element.innerHTML = title.text; element.style.color = title.color; } function renderContent(content){ let element = document.querySelector('#content'); element.innerHTML = content.text; element.style.color = content.color; } function renderApp(state){ renderTitle(state.title); renderContent(state.content); } renderApp(appState); ``` 问题: 此时状态是一个全局变量,任何地方都可以改 例如第八行 就会造成毁灭性结果 ## 第二版 改进: 增加修改的门槛 ``` const UPDATE_TITLE_COLOR = 'UPDATE_TITLE_COLOR'; const UPDATE_TITLE_TEXT = 'UPDATE_TITLE_TEXT'; const UPDATE_CONTENT_COLOR = 'UPDATE_CONTENT_COLOR'; const UPDATE_CONTENT_TEXT = 'UPDATE_CONTENT_TEXT'; // 派发分发的意思 // action动作 动作 描述一下你想干什么,动作是一个普通的js对象,只有一个属性是必须的 type属性 其它属性随意 function dispatch(action){ //{type:UPDATE_TITLE_COLOR,color:'purple'} switch(action.type){ case UPDATE_TITLE_COLOR: appState.title.color = action.color; break; case UPDATE_TITLE_TEXT: appState.title.text = action.text; break; case UPDATE_CONTENT_COLOR: appState.content.color = action.color; break; case UPDATE_CONTENT_TEXT: appState.content.text = action.text; break; default: throw new Error('你发给我的指令我不认识或无法处理!'); } } setTimeout(function(){ dispatch({type:UPDATE_TITLE_COLOR,color:'orange'}); dispatch({type:UPDATE_CONTENT_TEXT,text:'新内容'}); renderApp(appState) },2000); ``` 问题: 此时我们仍然可以直接修改appState ## 第三版 改进: 定义一个方法,创建一个仓库,这个仓库其实一个对象,只不过可以引用闭包变量 ``` function createStore(){ let state = { title:{color:'red',text:'标题'} ,content:{color:'green',text:'内容'} }; function dispatch(action){ //{type:UPDATE_TITLE_COLOR,color:'purple'} switch(action.type){ case UPDATE_TITLE_COLOR: state.title.color = action.color; break; case UPDATE_TITLE_TEXT: state.title.text = action.text; break; case UPDATE_CONTENT_COLOR: state.content.color = action.color; break; case UPDATE_CONTENT_TEXT: state.content.text = action.text; break; default: throw new Error('你发给我的指令我不认识或无法处理!'); } } function getState(){ return JSON.parse(JSON.stringify(state)); //第二层保护 } return { getState,dispatch } } let store = createStore(); renderApp(store.getState()); setTimeout(function(){ store.dispatch({type:UPDATE_TITLE_COLOR,color:'orange'}); store.dispatch({type:UPDATE_CONTENT_TEXT,text:'新内容'}); renderApp(store.getState()); },2000); ``` 问题: 此时状态和动作都是写死的,createStore无法复用 ## 第四版 改进: 将dispatch做成了一个对外接口,让用户可以自定义动作 并将状态的初始化绑定在了reducer上的第一个参数上 ``` function createStore(reducer){ let state; let listeners = []; function dispatch(action){ //{type:UPDATE_TITLE_COLOR,color:'purple'} state = reducer(state,action); //会返回一个新状态 } function getState(){ return JSON.parse(JSON.stringify(state)); //第二层保护 } dispatch({}); //获取初始状态 return { getState,dispatch,subscribe } } let initState = { title:{color:'red',text:'标题'} ,content:{color:'green',text:'内容'} }; reduce处理的意思 处理器 根据老的状态和拿到的动作 返回新的状态 let reducer = function(state = initState,action){ switch(action.type){ case UPDATE_TITLE_COLOR: return {...state,title:{...state.title,color:action.color}}; //return 后自动break case UPDATE_TITLE_TEXT: return {...state,title:{...state.title,text:action.text}}; case UPDATE_CONTENT_COLOR: return {...state,content:{...state.content,color:action.color}}; case UPDATE_CONTENT_TEXT: return {...state,content:{...state.content,color:action.text}}; default: return state; //返回老状态 } }; let store = createStore(reducer); renderApp(store.getState()); setTimeout(function(){ store.dispatch({type:UPDATE_TITLE_COLOR,color:'orange'}); store.dispatch({type:UPDATE_CONTENT_TEXT,text:'新内容'}); renderApp(store.getState()); },2000); ``` 问题: 仍然需要我们手动调用render刷新 ## 第五版 改进: 用发布订阅来实现自动render刷新 ``` function createStore(reducer,preloadedState){ let state = preloadedState; let listeners = []; function dispatch(action){ //{type:UPDATE_TITLE_COLOR,color:'purple'} // 接收到新的动作后u通过老状态 和新动作 计算出新状态 state = reducer(state,action); //会返回一个新状态 // 然后通知所有的监听函数执行 listeners.forEach(cb => { console.log(1); cb(); }); } function getState(){ return JSON.parse(JSON.stringify(state)); //第二层保护 } dispatch({}); //获取初始状态 //在redux源码中派发的是名为@@redux/init的东东 // 订阅,供外界订阅本仓库中状态的变化,如果状态变化了会执行订阅的逻辑 function subscribe(listener){ listeners.push(listener); //会返回一个取消订阅函数 return function(){ listeners = listeners.filter(item=>item!=listener); } } return { getState,dispatch,subscribe } } let initState = { title:{color:'red',text:'标题'} ,content:{color:'green',text:'内容'} }; // reduce处理的意思 处理器 根据老的状态和拿到的动作 返回新的状态 let reducer = function(state = initState,action){ switch(action.type){ case UPDATE_TITLE_COLOR: return {...state,title:{...state.title,color:action.color}}; //return 后自动break case UPDATE_TITLE_TEXT: return {...state,title:{...state.title,text:action.text}}; case UPDATE_CONTENT_COLOR: return {...state,content:{...state.content,color:action.color}}; case UPDATE_CONTENT_TEXT: return {...state,content:{...state.content,text:action.text}}; default: return state; //返回老状态 } }; let store = createStore(reducer); //可以传入初始值 那么此时initStore将会失效 function render(){ renderTitle(store.getState().title); renderContent(store.getState().content); } render(); store.subscribe(render); let unsubscribe = store.subscribe(render); setTimeout(function(){ store.dispatch({type:UPDATE_TITLE_COLOR,color:'orange'}); unsubscribe(); store.dispatch({type:UPDATE_CONTENT_TEXT,text:'新内容'}); },2000); //为了方便action的书写和避免写错,我们一般会写一个函数来生成对应的action 方便检查错误 有报错 function updateTitleColor(color){ return {type:UPDATE_TITLE_COLOR,color} } ```