什么问题?
组件的树形结构决定了数据的流向,致使的数据传递黑洞javascript
怎么解决?
全部组件都经过中介者传递共享数据
方案:
中介者:html
(function createStore() { var store; return function() { if(!store) { store = new Regular; } return store; } })()
组件A修改数据前端
define(['./store.js'], function(createStore) { var A = Regular.extend({ name: "组件A", data: { title: '标题' }, getData: function() { this.data.title = createStore().data.title; }, setData: function() { store.data.title = '新标题' //通知全部其余组件 store.$emit('change', { title: '新标题' }) } }); return A; });
其余组件能够监听,也能够主动拿:java
define(['./store.js'], function(store) { var B = Regular.extend({ name: "组件B", init: function() { createStore().$on('change', function(newTitle){ this.data.title = newTitle }) } }); return B; });
两个问题:
1 store.data能够直接被访问和修改->data和store分开&经过接口拿
2 只须要订阅和派发(派发的时候会把数据存起来并通知其余订阅者)git
(function createStore() { var store; return function() { if(!store) { var store = new Regular; var state = {}; store.getState = function(){ return state; }; store.subscribe = function(listener) { store.$on('change', listener); } store.dispatch = function(action) { if(action.type == 'changeTitle') { state.title = action.data.title; } store.$emit('change', state); } } return store; } })() define(['./store.js'], function(createStore) { var A = Regular.extend({ name: "组件A", data: { title: '标题' }, getData: function() { this.data.title = createStore().data.title; }, setData: function() { store.dispatch({ type: 'changeTitle', data: {title: '新标题'} }) }); return A; }); define(['./store.js'], function(store) { var B = Regular.extend({ name: "组件B", init: function() { createStore().subscribe(mapState) }, mapState: function(state) { this.data.title = state.title } }); return B; });
这个就是基本的redux雏形。后面的其实都是一些改进。
改进1: 数据处理耦合在store当中->抽出reducer并能传入初始statees6
(function createStore(reducer, initState) { var store; return function() { if(!store) { var store = new Regular; var state = initState; store.getState = function(){ return state; }; store.subscribe = function(listener) { store.$on('change', listener); } store.dispatch = function(action) { state = reducer(state, action); store.$emit('change', state); } } return store; } })()
reducer长这样:github
function reducer1(state, action) { switch(action.type) { case 'CHANGE_TITLE': //es6写法 //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator //return { //...state, //title: action.data.title //} return Object.assign({}, state, {title: action.data.title}); break; } }
reducer说是一个纯函数,本质上就是它不改变输入的参数,最后返回了一个新对象(规约)。也解决了可变数据结构的问题。
改进2:每一个组件都须要依赖store,而且须要调用store的subscribe和dispatch方法->建立顶层容器而且扩展它的组件的能力redux
const App = Regular.extend({ name: 'App', template: ` <StoreProvider> <A /> <B /> </StoreProvider> ` }); var StoreProvider = Regular.extend({ template: '{#include this.$body}', config: function(){ this.store = createStore(); }, modifyBodyComponent: function( component ){ component.dispatch = this.store.dispatch.bind(store) //把订阅这个工做给作了 this.store.subscribe(function () { var state = this.store.getState(); component.mapState(state); }.bind(this)); component.getState = this.store.getState.bind(store) } });
而后子组件内就能够这样调用了:数组
define([], function() { var B = Regular.extend({ name: "组件B", init: function() { this.subscribe(mapState) }, mapState: function(state) { this.data.title = state.title } }); return B; });
改进3: 这里的store无法传reducer和初始状态,由于你是里面调用的->createStore在外面作,而后把store传给顶层容器数据结构
const App = Regular.extend({ name: 'App', template: ` <StoreProvider store={store}> <A /> <B /> </StoreProvider> ` config(data) { data.store = createStore(reducers, { title: "标题" }) } }); var StoreProvider = Regular.extend({ template: '{#include this.$body}', config: function(data){ this.store = data.store; }, modifyBodyComponent: function( component ){ component.dispatch = this.store.dispatch.bind(store) //把订阅这个工做给作了 this.store.subscribe(function () { var state = this.store.getState(); component.mapState(state); }.bind(this)); //这个方法其实不用了。由于数据经过connect得到了,而初始数据经过一开始就传到store进去了 component.getState = this.store.getState.bind(store) } });
改进3: 而且这样每一个组件都有一个mapState方法,并且作得事都比较相似就是把state的数据过来-> 抽出一个connect函数
function connect(config, B) { B.implement({ mapState: function(state) {//this指向B const mappedData = config.mapState.call(this, state); mappedData && Object.assign(this.data, mappedData); } }); } connect({ //es6简写 //mapState(state) { //... //} mapState: function(state) { return { title: state.title } }, A);
以上,就是redux核心的基本实现原理。
中间件是干吗用的?
https://guoyongfeng.github.io/book/15/04-redux-logger%E7%9A%84%E5%BA%94%E7%94%A8%E5%92%8C%E7%90%86%E8%A7%A3.html
好比上面的logger中间件,就是想在store的dipatch方法里面作点其余事,好比打印下个性化的日志。
怎么实现?
正常想法:代理dispatch方法
function applyMiddleware() { let store = createStore(reducer1, initState); store.dispatch = function(action) { console.log("....") store.dispatch(action); console.log("....") } return store; }
后面呢也是改进。。
改进1:不但愿改变原先store&dispatch内打印日志部分但愿能作其余事情->传递一个middleware回调进去
function logger(dispatch, action) { console.log("....") dispatch(action); console.log("....") } function applyMiddleware(middleware) { let store = createStore(reducer1, initState); let dispatch = function(action) { middleware(store.dispatch, action) } return Object.assign({}, store, {dispatch: dispatch}); } var store = applyMiddleware(logger)
改进2: 如何处理多个中间件(每一个中间件作的事不同)并且store的dispatch应该只被执行一次
var store = applyMiddlewares(logger, someMiddleware) function applyMiddlewares(logger, someMiddleware) { let store = createStore(reducer1, initState); let dispatch = function(action) { //但愿一层层代理地执行中间件,最左边的先执行 someMiddleware(logger(store.dispatch, action), action) } return Object.assign({}, store, {dispatch: dispatch}); } //因此logger必须return一个函数,给其余middleware执行 function logger(dispatch) { return function(action) { console.log("....") dispatch(action); console.log("....") } }
改进3: 仔细看这里面的实现发现就是数组的reduceRight方法
function applyMiddlewares(middlewares) { let store = createStore(reducer1, initState); let dispatch = middlewares.reduceRight(function(dispatch, middeware) { return middeware(dispatch); }, store.dispatch) return Object.assign({}, store, {dispatch: dispatch}); }
改进4: 中间件里面的next函数是干吗用的?->其实就是传进去的dispatch方法
function logger(next) { return function(action) { console.log("....") next(action); console.log("....") } }
以上,就是redux中间件实现的基本原理。
Action Creator是什么?
是对dispatch函数参数(也就是action)的一种抽象,便于Action的复用
好比咱们这么写:
this.$dispatch({ action: 'CHANGE_TITLE', data: { title: '新标题' } })
可能其余组件也须要写类似的代码,你须要复制代码。其实咱们能够抽出一个creator。
//这部分能够被复用 const CHANGE_TITLE = 'CHANGE_TITLE'; function changeTitle(newTitle) { return { type: CHANGE_TITLE , data: { title: newTitle } } } this.$dispatch(changeTitle('新标题'))
最后的一张图总结:
做者知乎/公众号:前端疯