前戏
前端主流开发框架,react,vue,angular三分天下互不相让,随意一点火星便能引发框架大战,但是框架选择主观性本身就很强,因此这里就不评说了,一较高下不是几句话说得清的,我个人比较喜欢react,也只是因为其他的我用的不多,也许只是先入为主那么简单。react带给我们的颠覆我在这里就不班门弄斧了,大神们已经讲太多了。虽然react好用,不过始终是个基础框架,react最大的亮点在于它的状态管理,然鹅,使用原生react最大的不爽也是在于状态管理。使用原生react容易造成状态管理混乱的问题,为了解决这个问题,后来FB提出flux的思想,慢慢发展出redux,以及成熟的react-redux和mobx。个人认为react-redux很成熟很好用,有些缺点但是并不伤大雅,mobx以不同的思路来解决这个问题,而且比react-redux更加小巧一点,也不错。不过,有时候成熟度越高,也就意味着限制越多,一些庞大的项目由于本身项目的复杂性,再引入react-redux倒是很合适,但是当我们的项目没有大到使用react-redux这些框架,一旦引入这类框架就会发现,我其实只想要让你管管我的状态,但你管的太宽了,所有的路径都给我规划完了,比如我在客厅,需要喊一声在卧室的老婆,我原本只是想喊一嗓子就好了,结果你也让我打个电话,杀鸡用牛刀我不反对,不过你这把刀太沉了,反而影响了我的效率这就不合适了吧。好吧,react-reudx&mobx,拜拜,我们真的不合适。
激情
既然不合适,那我们只能另觅新欢了,从react出发,或许我们可以自己倒腾一个简单可用的状态管理框架呢。那么我们来分析一下我们最大的需求是什么呢,react原生状态管理最大的问题在哪呢?就是任意两个组件之间通信的问题,我需要的是两个组件之间的通信,是绝密的,不需要中间任何节点的感知。这当然有办法,利用事件机制嘛,好吧,我们这次稍微不那么low一点,既然是react的套餐,那还是要稍微要点血缘关系的。那究竟怎么搞?我们先来整理一下思路,其实这个思路可以参考一下flux,redux的设计,我们也需要一个中央数据中心store,对我们的数据进行集中式管理,任意节点之间的通信其实都是通过这个store进行的。那具体如何做,让我们回归本质,react的通信是如何体现的,是由某个节点发起的某个动作导致状态变化,然后该状态变化同步到UI的变化,所以归根结底,就是这个简单的公式,store->UI。任意节点状态的变化,映射到中央数据管理中心store,由store进行广播出去,这就是实现了最简单的中心化状态管理。举个简单的栗子来进行说明。
举个栗子
我定义了一个store,就是个管理state的对象,里面有个bindContext方法,每次装载组件的时候会把组件对象实例装入数组contexts,setState方法其实就是将每一个组件分别调用一次setState。然后我定义了一个App组件,里面挂载Module1和Module2两个组件,Module2下挂载一个SubModule21的子组件。当我每次需要组件之间通信,比如SubModule21中点击“清空Module1的值”按钮,这个就是SubModule21与Module1组件之间的通信,其实Module1并没有感知到是谁在对它进行操作,我们更新的其实是store中的数据状态,并由它统一下发到组件。代码如下:
index.html代码:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>测试</title>
</head>
<body>
<div id="page-container"></div>
</body>
</html>import React from 'react';
import ReactDOM from 'react-dom';
/**
* 统一状态管理对象
*/
let store = {
//目标组件对象
contexts: [],
//state数据
state: {
},
//注册绑定组件与状态数据,用于setState
bindContext(_context) {
this.contexts.push(_context);
return this.state;
},
setState(bs_state, context) {
this.contexts.map((item) => {
React.Component.prototype.setState.call(item, bs_state);
})
}
}
/**
* 入口组件
*/
class App extends React.Component {
constructor() {
super();
store.bindContext(this);
}
render() {
return ( < div >
< Module1 / >
< Module2 / >
< /div>
)
}
}
/**
* siblings1
*/
class Module1 extends React.Component {
constructor() {
super();
this.state = Object.assign({}, store.bindContext(this));
}
render() {
return ( < div >
< p > -- -- -- --Module1-- -- -- -- - < /p> < input type = "number"
value = {
this.state.m1Var
}
onChange = {
this.setValue
}
/> < /div>
)
}
setValue(e) {
console.log(e.target.value);
store.setState({
m1Var: e.target.value
})
}
}
/**
* siblings2
*/
class Module2 extends React.Component {
constructor() {
super();
this.state = Object.assign({}, store.bindContext(this));
}
render() {
return ( < div >
< p > -- -- -- -- - Module2-- -- -- -- - < /p>
兄弟节点Module1的值: {
this.state.m1Var
} < SubModule21 / >
< /div>
)
}
}
/**
* Module2的子组件
*/
class SubModule21 extends React.Component {
constructor() {
super();
this.state = Object.assign({}, store.bindContext(this));
}
render() {
return ( < div >
< p > -- -- -- - SubModule21-- -- -- -- < /p> < button onClick = {
this.clean
} > 清空Module1的值 < /button> < /div>
)
}
clean() {
store.setState({
m1Var: ""
})
}
}
ReactDOM.render( < App / > , document.getElementById("page-container"));不知道并大家注意到没有,这个思路一定程度上是逆react开发的,因为即使是父子组件之间,其实是没有通过props进行联系的,都是统一使用setState来进行数据的下发,几乎抛弃了props。似乎是有点问题,但是细细想来其实是没有问题的,因为react原本就是数据驱动的,setState和props只是不同场景下传递数据的方式罢了,不同的设计思路,只是方式就会有所区别。核心,还是在于状态数据的变化。
高潮
本篇只是将其中的核心思想进行简单的呈现,相信大家会发现这段代码存在很多的问题,很明显store对象存在部分逻辑问题,细细分析会发现一些亟需优化的点,并且大量存在bindContext这样的样板代码十分不优雅,不过这并不影响我们呈现核心思想。当然只有核心思想还是不够的,需要进行继续完善,最终的目的是完成一个简约却五脏俱全的框架。
好吧,这次高潮有点短暂。。。
结语
或许这个算不上一个真正的框架,可能是个非主流的玩意儿,但会让我们在不需要redux或者mobx的时候多一种选择。优秀的框架有很多很多,但我们不能一直只是当个使用者,我们需要一同参与建设,即使最后出来的玩意可能只有我们自己使用,这个思想或许没人认同,那又何妨,我们知道我们比创造出这玩意之前的我们更加牛逼了。我会尽量使用react原生提供的一些特性来完成,或许可以从中发现更多react中不曾关注到的点。我相信,技术,是需要多玩玩的。
欢迎拍砖!!!