从开始学习dva到引入到实际项目中也有几个月的时间,下面分享一下实际的经验,另外也有一些比较含糊或者疑惑的地方,看看大家有没有有些好的思路。
dva整体构架比较清晰,但是实际使用的时候,还是需要做很多处理。
dva model扩展
在我们的基础库中,实现了Model.extend方法,所有的model都通过这个方法来创建, extend具体对以下几个方面进行了扩展.
state
根据我们的业务特点(主要为管理系统),每个model我们都默认添加了loading, confirmLoading, spinner属性
subscriptions
subscriptions中对路由的监听 写法太过繁琐,特别是在包含参数的情况下。我们扩展了实现了listen方法
subscriptions: { setup({ dispatch, listen }) { //action为 redux action listen('/user/list', { type: 'fetchUsers'}); //action为 回调函数 listen('/user/:id/detail', ({ query, params }) => { const id = params[0]; dispatch({ type: 'fetchDetail', payload: { id } }) }); //支持对多个path的监听 listen({ '/user/list': ({ query, params }) => {}, '/user/query': ({ query, params }) => {}, }); }
effects扩展
dva中处理loading状态非常繁琐,而dva提供的全局loading对我们业务并不适用,因为往往一个model中,有些call请求需要loading,有些call请求又不需要loading,所以我们对effects中的saga函数进行了扩展
- callWithLoading 调用请求时,自动处理loading状态
- callWithConfirmLoading 调用请求时,自动处理confirmLoading状态
- callWithSpinning 调用请求时,自动处理spinning状态
如果发送ajax请求,需要处理相关的状态,就可以使用上面的方法,如果不需要就使用原始的call方法, 使用开发中可以灵活选择.
另外,在我们的业务中,call请求成功或者失败后,往往需要弹出对应的业务提示框(消息内容有时候不是由后端提供),故我们在上面的函数中,都提供额外的参数,支持请求结果的消息框处理。
//请求开始前会将loading = true, 请求结束后,将loading=false const users = yeild callWithLoading(service.user.getList,user); const users = yeild callWithLoading(service.user.getList,user,{successMsg:'加载用户成功',errorMsg:'加载用户失败'});
effects中经常需要需要更新state,但是往往又不想为每次修改都命名一个reducer函数,所以扩展了update方法,调用updateState reducer来更新state
//更新当前model的state yield update({ users })
reducers扩展
为了配合effects中的扩展,我们默认实现了如下方法
showLoading,hideLoading... 等加载状态控制方法
updateState 直接更新state数据
resetState 重置state数据
request 扩展
fetch在实际项目中用使用,实在太过鸡肋,我们在fetch的基础上封装了Http类,实现了简单的中间件机制,方便各个项目使用,并且实现了一些常见的中间件(token中间件,统一错误弹框中间件等)
export default Http.create([dataTransformMiddleware, domainMiddleware, contentMiddleware, headerMiddleware]);
antd form扩展
antd form写法 个人觉得实在太过繁琐,一个带表单验证的输入框,往往需要FormItem & getFieldDecortor & Input 三层结构,而且混合着jsx以及js的语法.
为了简化form相关的代码,我们重新定义了field属性结构,并且提供了HForm 以及HFormItem组件。
HForm 主要应用与一些简单的form表单,不需要太多的交互
HFormItem 则是form中每一项输入框,用于复杂布局/交互的form中
const fields = [ { key: 'name', name: '名称', required: true, }, { key: 'gender', name: '性别', enums: { MALE: '男', FAMALE: '女' } }, { key: 'birthday', name: '生日', type: 'date' } ]; function HFormBase({ form }) { const formProps = { fields item: {}, form }; const onSubmit = () => { validate(form, fields)((values) => { results = values; }); }; return ( <div> <HForm {...formProps} /> <Button onClick={onSubmit} type="primary" >提交</Button> </div> ); }
const fields = [ { key: 'name', name: '名称', required: true }, { key: 'age', name: '年龄' }, { key: 'birthday', name: '生日', type: 'date' }, { key: 'desc', name: '自我介绍', type: 'textarea' } ]; function HFormItemBase({ form }) { const layout = { labelCol: { span: 6 }, wrapperCol: { span: 18 } }; const itemProps = { form, item: {}, ...layout }; const rules = { number: [{ pattern: /^\d+$/, message: '请输入数字' }] }; const submit = () => { validate(form, fields)((values) => { console.log(values); }); }; const fieldMap = getFields(fields).toMapValues(); return ( <Form > <Row> <Col span="12" ><HFormItem {...itemProps} field={fieldMap.name} inputProps={{ disabled: true }} placeholder="指定placeholder" /></Col> </Row> <Row> <Col span="12" ><HFormItem {...itemProps} field={fieldMap.age} rules={rules.number} onChange={onChange} /></Col> <Col span="12" ><HFormItem {...itemProps} field={fieldMap.birthday} /></Col> </Row> <Row> <Col span="12" ><HFormItem {...itemProps} field={fieldMap.desc} /></Col> </Row> <Row> <Col span="12"><Button type="primary" onClick={submit} >提交</Button></Col> </Row> </Form> ); }