React是怎么玩的?
React其实是一个特别简单的东西。简单到什么程度呢?简单到它自己几乎啥都做不了,一上项目就得动用React全家桶才能完成需求。
React
先说说裸的React:React本身就是一个单纯的View层,给它什么样的数据,它就渲染什么样的DOM。
就这么简单。
数据放在哪
React需要渲染数据,那么数据从哪里来呢?
最直接的想法就是把数据存在要用的地方,比如存在组件内部的state
。
如果别的组件也要用怎么办?再存一份?从直觉上来讲,这显然是不妥的,但是问题似乎也不大。
那么如果相同的数据渲染在了多个地方,如何让其他地方知道这里的数据变了?每次修改完数据如何同步到所有用到这块数据的地方?
这就有了一个大坑,Redux就是用来填这个坑的。涉及服务端渲染的时候这个坑就更明显了。
Redux
再回过头来看React,React拿到什么样的数据,就渲染什么样的View,所以我们只要把所有的数据抽出来,然后多个组件共用一份数据,数据发生变化后让React自动渲染就好了。这样的话,不管是从哪个组件造成的数据变化,修改的都是同一份数据,于是所有用到这份数据的地方都会更新。这就是Redux里面的store
,也就是一个全局的state
。每个组件都可以通过connect
与store
中的数据关联起来,当数据发生变化的时候,自动触发render
。
同样的,造成数据修改的操作可能有很多,他们的结果可能是一样的,如果在每个组件内部都把业务逻辑写一遍,一方面过于麻烦,另一方面出错的可能性更大,而且不易抽象。所以我们希望把一个操作写成一个独立的方法,只要把数据传给它,它就能完成后续的复杂操作,如与服务器交互、获取数据。而我们只需要给它取个名字,就可以在各种场景下方便地使用。这就是Redux里的action
。
安全起见,我们始终不应该直接修改state
,一方面这可能造成意料之外的数据破坏,另一方面无法快速得知数据发生了变化,所以通常要通过一个包装的方法来修改,这个方法始终返回一个新的对象作为新的state
。这就是Redux里的reducer
。
总结一下,就是所有的数据都放在全局的state
里(也就是store
),所有的复杂操作都通过action
来触发,所有的数据修改都通过reducer
生成新的state
完成。
Redux的思想也很简单,但是开发起来十分麻烦,因为针对一种数据,要分别定义每个操作的名字常量、每个操作的方法函数以及一堆处理数据的方法,而且分布在constants
、actions
、reducers
三个地方,很不直观。dva就是用来填这个坑的。
dva
dva是阿里提供的一个库,把 React + redux + redux-saga + react-router 整合到了一起。一样的思想,更好的开发体验。
dva中没有了action
的概念,而多了effect
,实际上与action
类似,也是用来进行复杂操作,每一个effect
里面既可以派发新的effect
,也可以触发reducer
进行数据更新,所有的名字都是用namespace/name
的形式,相同的namespace
下还可以直接简写为name
,不需要再去写大量的冗余代码来定义常量了。
除此之外,每一类数据可以写在一个model
中,其effect
和reducer
写在一起,管理起来更方便,更有逻辑,代码更简洁。
而且由于引入了redux-saga
,所有的复杂操作都是用generator
完成,可以用同步的方式写异步代码,可读性更好,而且方便测试(我没试过 - -!)。
延伸
-
什么情况下用React组件内的state?
实际上很多情况下都应该避免,因为组件内的state需要自己维护,让组件变复杂,而且无法统一进行控制。因此只有在对一些独立的完全不受其他数据影响的地方可以适度使用,比如一个dropdown的开关,又比如一个一次性的表单。
-
数据应该在什么时机获取?
最直观的方法显然是在
componentWillMount
或者componentDidMount
之类的生命周期函数中发出获取数据的action
,但是这样不利于服务端渲染,也会让组件变复杂,如果多处需要获取数据,可能要写一些重复代码。比较好的方法是监听路由变化的
action
,根据路由来判断用户是否有进入该路由的权限,再进行数据获取和组件渲染。