新部门,新项目使用regular+redux进行开发,由于自己之前从未接触过,并且手头上没有开发任务,所以趁这个不算短的时间系统性学习了一下。本文主要是对如何使用regular+redux进行一个项目开发的一些思路,随着自己的学习成长会不断更新内容。
文档(中文)
如何摆好使用regular的姿势
regular组件的语法是这样:
1 | const Demo = Regular.extend({ |
可以看到模板是采用字符串的写法。这就有两个问题,第一,如果DOM结构过长(虽然一般情况下需要组件拆分这种情况很少出现),一个组件文件里冗杂着大段的DOM和逻辑代码,想想就很可怕;第二,最最最重要的,如何进行代码提示、代码整理,甚至语法高亮。编写HTML,如果有错编辑器会给你个红叉叉或者波浪线,写得太乱了也可以一键整理代码,但是模板字符串不能这样。当然我们可以编码的时候注意一点,尽量写的干净整洁,控制好缩进,但毕竟写起来比较不爽。
想到了两个解决方案,一是参考编辑器插件对类似框架的支持也写一个插件,二是把DOM结构从组件逻辑代码中抽离出来。第一个方案成本比较大,而且不具通用性,着重考虑第二个。
在文档中看到了rgl-loader,这就简单多了,对每个组件:
- 新建.rgl文件
- 把这个后缀和HTML关联起来(各大编辑器应该都可以做到),这样可以按HTML的语法进行代码提示、代码整理、语法高亮
- webpack中使用rgl-loader解析.rgl后缀的文件
- 将模板文件引入组件中
不过后来实践发现这样做也有两个缺点:
- 如果组件过多,webpack编译的文件也会多很多,速度会变慢
- 由于事件绑定等逻辑是写在模 板字符串上,所以开发时需要不断切换两个文件
但是如果是一个比较复杂的组件,这么做还是一个不错的选择。
自动生成开发文件
假如我们需要新开发一个组件,需要哪些步骤呢?
如果这个组件叫A,在上面提到的模板分离的基础下,需要:
- A.rgl
- A.less
- A.js
- index.js
- aActions.js
- aConstants.js
- aReducers.js
还可能需要往很多其它文件里添加代码,比如路由配置、reducer.js(combineReducers)。新建一个文件需要:右键-新建文件-命名-回车,四个步骤,以上不算修改文件的话至少需要四七二十八,加新建一个文件夹,29个操作。反正我觉得,很烦。有没有什么方法低成本的方法可以简化这些重复的工作呢?虽然没有多少其它技能,但是我们都学过linux呀,新建文件夹的命令有没有!有!新建文件的命令有没有!有!可不可以写入字符串!可以!那就好了嘛~写一段shell脚本运行一下
1 | #!/bin/sh |
我们还可以对这段脚本进行一些扩展,加一些参数,比如不是所有组件新建的时候都要添加redux文件。上面的脚本也有可优化的点,比如目录的配置等等。
regular相关问题
redux
redux的概念真的很简单:
应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你需要编写 reducers。
总结一下:
View:触发action
Action:store数据的唯一来源;{type:[TYPE],data:{}}
Reducer:如何改变store
数据流:View -> Action -> Middleware -> Reducer
不用想我们该怎么在项目中使用它,怎么样把它用起来,换一个思路,如果一个regular项目,开始没有用redux进行状态管理,数据在各个组件间传来传去,后来组件越来越多,层级越来越复杂,数据状态越来越复杂越来越不好管理,我们该如何重构它。
首先,目录
redux
– actions
—— state1Actions.js
—— state2Actions.js
—— ….js
—— statenActions.js
– constants
—— state1Constants.js
—— state2Constants.js
—— ….js
—— statenConstants.js
– fetchs
—— state1Api.js
—— state2Api.js
—— ….js
—— statenApi.js
– reducers
—— state1Reducers.js
—— state2Reducers.js
—— ….js
—— statenReducers.js
—— reducers.js
– store.js
- store.js 调用createStore(),传入中间件和reducers以及初始状态
fetchs 网络请求的集合,如果很多的话我们可以把它按数据分类分成几块。如一个基本的增删改查(假设fetch是我们封装的一个fetch请求方法):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20export const getList = data => fetch(
'/api/list', {
methods: 'GET',
data
});
export const deleteList = data => fetch(
'/api/list:id', {
methods: 'DELETE',
data
});
export const addList = data => fetch(
'/api/list', {
methods: 'POST',
data
});
export const getList = data => fetch(
'/api/list:id', {
methods: 'PATCH',
data
});reducers 接收state和action,返回新的state。它自身是不涉及任何业务逻辑处理的,只管接收action,然后更新state,这里有个纯函数的概念,什么样的输入必然会导致什么样的输出,另外数据不可变,也保证了状态的可跟踪。不同的[state]reducers负责不同的state,reducers.js中调用combineReducers,合并各个子reducer,最终返回一个调用 reducers 对象里所有reducer的reducer,并且构造一个与reducers对象结构相同的state对象。
- constants action传的对象的type字段,reducer根据它判断是在处理哪个action
- actions 编写action creator,返回一个新的action
在组件代码中,我们需要将数据与redux连接起来,使用regular-redux2,这一块没有文档,不过源码也十分易读,况且我们可以直接看react-redux的文档。重点是调用的connect方法,这个方法不会改变原来的组件类,而是返回一个新的已与Redux store连接的组件类。看一下它传的参数:
mapStateToData 指定把把哪个state绑定到regular组件的data上
写法:1
2
3const mapStateToData = state => ({
...state.state1
})mapDispatchToData 将指定action绑定到regular组件的data上
如果传递的是一个函数,该函数将接收一个 dispatch 函数,然后由你来决定如何返回一个对象,这个对象通过 dispatch 函数与 action creator 以某种方式绑定在一起
这里会给所有绑定的action发dispatch,所以我们在action creator里一般是不用发dispatch的
写法:
1 | dispatch => ({ actions: bindActionCreators({ |
- bindStore { store, name: ‘’ }
假设Comp是Regular.extend()创建的一个组件,不用redux我们会写
1 | export default Comp |
抛出组件
使用redux需要改成
1 | const store = createAppStore(); |
以上就是一个简单地使用redux的过程。如果是异步的话,我们的action会复杂一点:
1 | export const getList = (param) => dispatch => { |
reducer中根据不同的action type更新不同的数据就好了。但是在组件代码中很简单,只要调用data.action上的action creator就可以了,只需要传入参数,什么都不用管,所有的数据在redux中变更管理。如果不是数据的变更还是有回调呢?我们可以在state中维护一个状态码,regular的watch事件可以监听数据的改变,再执行相应方法就可以了。还有一种方式,在使用redux-thunk的情况下,我们可以直接返回一个promise,这样就可以处理成功或失败的事件了。代码如
1 | export const getList = (param) => dispatch => ( |
或者可以直接返回getLiveList(),它本身就是个promise。
有个问题是什么样的数据应该放在store里,什么样的数据放在data中,我的原则是:
组件的data中尽量少放数据,从父组件传入或放在redux中,避免组件自身状态的不可预测性。
data中放置的数据应与外界独立(即保证外界无法访问数据并且不会因为这个数据的更改造成自身状态的更改)。
可能引起多个组件状态更改的数据放在redux中。
另外总结几点上面没有提到或者没说清楚的(文档中有些也有写,然后我觉得值得注意的):
- state应跟数据挂钩而不是跟组件,这样一个组件可以连接多个state,不同组件可以操作同一个state中的数据
- 每个action提交的数据应尽可能少
- 异步操作可以像上面一样定义不同的type,也可以同一个type不同的status,见文档
- 对于某些操作(比如更改一个列表的某个数据,发了post请求后往往需要再get重新请求一下列表),可以先改变state,不重新请求,不过有风险,不是任何请求都适合
- redux是适合读源码的,或者源码和文档一起读,很小很简单很清晰
暂时先更新到这里,后续有新想法再更新或修改