Regular的Redux实现整理

什么问题?

组件的树形结构决定了数据的流向,导致的数据传递黑洞

怎么解决?

所有组件都通过中介者传递共享数据

方案:

中介者:

(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;
});

其他组件可以监听,也可以主动拿:

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 只需要订阅和派发(派发的时候会把数据存起来并通知其他订阅者)

(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并能传入初始state

(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长这样:

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方法->创建顶层容器并且扩展它的组件的能力

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('新标题'))

最后的一张图总结:

作者知乎/公众号:前端疯

Redux----Regular的Redux实现整理的更多相关文章

  1. 读redux有感: redux原来是这样操作的。

    2017.9.10日 教师节 : ~当一个事物你没有接触,但是生活中 常常用到他,你就不得不去了解他了. 注:新手可以看一下,毕竟博主也是个菜鸟,没法写高深的东西,不想看博主扯淡的直接看第三节啦~~ ...

  2. react第十八单元(redux中间件redux-thunk,redux工程目录的样板代码,规范目录结构)

    第十八单元(redux中间件redux-thunk,redux工程目录的样板代码,规范目录结构) #课程目标 中间件:中间件增强redux的可扩展性,实现功能复用的目的. redux-thunk异步逻 ...

  3. 学习Redux之分析Redux核心代码分析

    1. React,Flux简单介绍 学习React我们知道,React自带View和Controller库,因此,实现过程中不需要其他任何库,也可以独立开发应用.但是,随着应用程序规模的增大,其需要控 ...

  4. Redux 学习(1) ----- Redux介绍

    Redux 有三个基本的原则: 1,单一状态树,redux 只使用一个javascript 对象来保存整个应用的状态. 状态树样式如下: const state = { count: 0 } 2,状态 ...

  5. redux sample with redux source code

    code sample没有package.json文件,也就没有任何外部依赖,直接使用redux source code. nodejs对es6的import export还不支持,这里使用了stac ...

  6. Redux进阶(Redux背后的Flux)

    简介 Flux是一种搭建WEB客户端的应用架构,更像是一种模式而不是一个框架. 特点 单向数据流 与MVC的比较 1.传统的MVC如下所示(是一个双向数据流模型) 用户触发事件 View通知Contr ...

  7. 迷你redux实现,redux是如何进行实现的?

    export function createStore(reducer){ let currentState={} let currentListeners=[] function getState( ...

  8. redux 个人整理

    序 本人工作之余的闲暇时间还是很充裕的.在完成经理安排的任务后,基本上都是在自学,主要是阅读各种技术文档.浏览博客.运行别人写的一些前端demo并观赏与赞叹.在ScorpionJay 同学的带领下,我 ...

  9. Redux教程1:环境搭建,初写Redux

    如果将React比喻成士兵的话,你的程序还需要一位将军,去管理士兵(的状态),而Redux恰好是一位好将军,简单高效: 相比起React的学习曲线,Redux的稍微平坦一些:本系列教程,将以" ...

随机推荐

  1. Linux显示系统日期

    Linux显示系统日期 youhaidong@youhaidong-ThinkPad-Edge-E545:~$ date 2015年 01月 21日 星期三 20:37:39 CST

  2. 如何让window.open()以post请求方式调用(巧妙解法)

    问题由来: 在公司遇到一个线上bug,如下 var url = 'http://106.75.31.215:8012/onlinePreview?url=' + encodeURIComponent( ...

  3. @postconstruct初始化的操作

    从Java EE 5规范开始,Servlet中增加了两个影响Servlet生命周期的注解(Annotion):@PostConstruct和@PreDestroy.这两个注解被用来修饰一个非静态的vo ...

  4. 【BZOJ2875】随机数生成器(矩阵快速幂)

    [BZOJ2875]随机数生成器(矩阵快速幂) 题面 Description 栋栋最近迷上了随机算法,而随机数是生成随机算法的基础.栋栋准备使用线性同余法(Linear Congruential Me ...

  5. Django的models实现分析

    1      引子 1.1     神奇的Django中的models 我们先来看一段在Django项目中常用的代码: 设置数据库models代码: class Students(models.Mod ...

  6. linux优化项

    Linux优化: 1.建立普通账号,使用普通用户登陆. 2.处理SELINUX. 3.处理防火墙. 4.精简开机自启动服务.保留sshd,network,crond,rsyslog,sysstat. ...

  7. 8Manage:聚焦研发企业利器——研发项目管理

    [导读]研发是企业保持核心竞争力的基石.那么对于研发企业来说,如何计划研发项目目标.保障项目的稳定运行,如何分配人才.资源,把控项目成本呢?这些一系列问题摆在管理者面前!引入8Manage研发项目管理 ...

  8. 未来已来,腾讯AI计算网络

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:由鹅厂网事发表在云+社区 "鹅厂网事"由深圳市腾讯计算机系统有限公司技术工程事业群网络平台部运营,我们希望与业界各位志同道合的伙伴交流 ...

  9. CentOS 6.4 配置 Hadoop 2.6.5

    (以下所有文件:点此链接 里面还有安装的视频教学,我这里是亲测了一次,如有报错请看红色部分.实践高于理论啊兄弟们!!) 一.安装CentOS 6.4 在VMWare虚拟机上,我设置的用户是hadoop ...

  10. 内嵌tomcat启动速度慢

    项目上最近要把内置的jetty换成tomcat, 来更好的支持servlet 3.0 本来以为换个容器, 几十行代码就好了. 实际上换了tomcat后, 一开始启动tomcat, 非常的慢. jett ...