We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React 15.x 升 React 16.x 是一次内部重构,对于使用者来说,原来的使用方式仍然可用,额外加了新的功能;而Antd 3.x 升 Antd 4.x, 在我的认知范围里,可以称作是飞(po)跃(huai)性的重构, 因为以前很多写法都不兼容了,组件代码重构,使用者的代码也得重构。但这次重构解决了3.x的很多问题,比如:
说这么多,还是直接来张图吧,我个人项目的打包体积变化:Antd 3.x VS Antd 4.x
Antd 3.x
Antd 4.x
升4.x之后,gzip少了150kb,也就是包大小少了500多kb,这不香么。
我和我的小组,为了用更爽的方式来开发迭代,针对于antd的Form和Table等组件做了一些简单的二次封装,形成了组件库antd-doddle。虽然Antd 4.x推出快小半年了,受疫情影响,今年业务迭代比较缓慢,没有新系统,也觉得暂时没必要去重构业务代码,所以一直只关注不动手。最近比较闲,组件库针对Antd 4.0做了适应性重构,作为一个胶水层,最大程度的去磨平4.0版本Form这种破坏性变动,减少以后业务代码升级4.x版本的调整量。
Antd 4.x到底做了哪些变化,在官方文档可以看到。
这篇文章主要讲针对于4.x Form的变化,我重构组件库的思路。
Antd-doddle 2.x文档地址, 支持4.x: http://doc.closertb.site, 首次加载较慢,请耐心等候。
Antd-doddle 1.x文档地址, 支持3.x: http://static.closertb.site, 首次加载较慢,请耐心等候。
试用项目Git 地址
项目在线试用地址, 请勿乱造
4.x 中除了Icon,最大的更改就在于Form,我自己感受到的变化是:
最大的改变
componentShouldUpdate
根据上面的变化点,针对性的做重构,由外向内层层剖析;
由于以前FormGroup组件,除了收集form方法和公共配置,也作为一个标识,接管了组件内部的渲染层;3.x版本其form实例由Form.create,即业务代码提供;4.x与其相似,只不过是通过hooks生成form实例。
变化点主要在于4.x版本Form要提供initialValues的设置,且这是一个defaultValue的设置,所以我们需要拓展,让其支持values为异步数据时,表单项的值能跟随其改变, 其原理很简单,监听value的变化,并重置表单数据,实现代码如下:
// 伪代码,只涉及相关改动 const FormGroup = (props, ref) => { const { formItemLayout = layout, children, datas = {}, ...others } = props; // 兼容了非hooks 组件调用的写法,内部再声明一个ref, 以备用; const insideRef = useRef(); const _ref = ref || insideRef; const formProps = { initialValues: {}, // why ...formItemLayout, ...others }; // 如果datas 值变化,重置表单的值 useEffect(() => { const [data, apiStr] = Type.isEmpty(datas) ? [undefined, 'resetFields'] : [datas, 'setFieldsValue']; // 函数式组件采用form操作; if (props.form) { props.form[apiStr](data); return; } // 如果是类组件,才采用ref示例更新组件 if (typeof _ref === 'object') { _ref.current[apiStr](data); } }, [datas]); return ( <Form {...formProps} ref={_ref}> {deepMap(children, extendProps, mapFields)} </Form>); };
上面有句代码 initialValues: {},会让人困惑, 为什么没有赋值为datas呢;这个又是antd的一个隐藏知识点,举个例子:
initialValues: {},
form.setFieldsValue({ name: 'antd', age: 18 }); // 后面想清空 form.setFieldsValue({}); // 最后发现上面清空根本没生效,原因可以自己想想
所以当我们想做一些需求,比如先编辑一个表单,表单中有数据了;但没做操作关闭了,然后点了新增按钮,传了一个空对象,这事就发现bug了,上一次编辑的数据还在,除了这个,还有一些其他的业务场景会用到,在antd中也有一个类似的issue:
在表单里有很多元素且拥有initialValue的时候, 如何简单的清空表单
所以在这个组件设计上,就将initialValues默认置空数据,然后设置的数据采用setFieldsValue来重置。如果想清空表单,直接传入一个空数据,被组件检测到后,内部调用resetFields来实现。快夸一下天才的我。
initialValues
setFieldsValue
resetFields
相比于FormGroup的变化,子组件FormRender相比较就变化小一点,主要在适应第二点变动,用代码的方式更直观:
// 3.x const render = renderType[type]; content = ( <FormItem label={name} rules={gerateRule(required, pholder, rules)} {...formProps} > {getFieldDecorator(key, { initialValue: data, rules: gerateRule(required, pholder, rules) })( render({ field: common, name, enums: selectEnums, containerName }))} </FormItem>);
重构之后
// 4.x const render = renderType[type]; content = ( <FormItem name={key} label={name} rules={gerateRule(required, pholder, rules)} {...formProps} > {render({ field: common, name, enums: selectEnums, containerName })} </FormItem>);
主要实现三种联动,根据其他表单项的变化,来改变关联表单项
由于以前每次表单项变化,都会引起其他表单项render,所以可以暴力的通过FormGroup增加数据层(useRef)与监(bao)听(guo)每个表单项的onChange来实现;
新的Form新增了onFormChange回调来支持增量式数据收集,但第四点变动,让老的方案GG;表单项的联动需要依赖shouldUpdate来实现,这也是官方推荐的方案;
其本质是,设置了shouldUpdate属性的FormItem,其仅仅作为一个容器,这个容器监听了类似onFormChange这种事件,然后根据shouldUpdate来判断是否需要重新渲染容器内的子元素,子元素渲染实现是一个应用React renderPrrops的设计模式;
所以联动方案似乎变得更简单了, 就多一层FormItem包裹,看部分代码实现:
const render = renderType[type]; content = shouldUpdate ? ( <FormItem shouldUpdate={shouldUpdate} noStyle> {form => { const datas = form.getFieldsValue(); const require = typeof required === 'function' ? required(initData, datas) : required; const disabled = typeof disableTemp === 'function' ? disableTemp(initData, datas) : disableTemp; return finalEnable(initData, datas) ? (<FormItem key={key} name={key} label={name} dependencies={dependencies} rules={gerateRule(require, pholder, rules)} {...formProps} {...otherFormPrrops} > {render({ field: Object.assign(common, { disabled }), name, enums: selectEnums, containerName })} </FormItem>) : selfRender(datas, form)} } </FormItem>) : /* 非联动实现 */
除了上面这些变化,其实还有很多边边角角的变化,比如:
还有,还有一些未考虑好的的新增特性。
实现一个小的编辑框,类似下面这样:
重构前的代码
import React from 'react'; import { FormGroup } from 'antd-doddle'; import { editFields } from './fields'; const { FormRender } = FormGroup; function Edit({ id, form, data }) { const { getFieldDecorator } = form; return ( <FormGroup getFieldDecorator={getFieldDecorator} required> {editFields.map(field => <FormRender key={field.key} field={field} data={data} />)} </FormGroup> ); } export default FormGroup.create()(Edit);
import React from 'react'; import { FormGroup } from 'antd-doddle'; import { editFields } from './fields'; const { FormRender } = FormGroup; function Edit({ id, data, ...others }) { const [form] = FormGroup.useForm(); return ( <FormGroup required form={form} datas={data}> {editFields.map(field => <FormRender key={field.key} field={field} />)} </FormGroup> ); } export default Edit
不仔细看,是不是不易察觉到变化
因为用的还不像3.x版本那么熟练,很多特性还没用上,所以先重构一个简易版本,来完成日常场景,后面再慢慢迭代。
如果感兴趣,可以fork项目或查看项目文档
The text was updated successfully, but these errors were encountered:
No branches or pull requests
前言
React 15.x 升 React 16.x 是一次内部重构,对于使用者来说,原来的使用方式仍然可用,额外加了新的功能;而Antd 3.x 升 Antd 4.x, 在我的认知范围里,可以称作是飞(po)跃(huai)性的重构, 因为以前很多写法都不兼容了,组件代码重构,使用者的代码也得重构。但这次重构解决了3.x的很多问题,比如:
说这么多,还是直接来张图吧,我个人项目的打包体积变化:
Antd 3.x
VSAntd 4.x
关于升级
我和我的小组,为了用更爽的方式来开发迭代,针对于antd的Form和Table等组件做了一些简单的二次封装,形成了组件库antd-doddle。虽然Antd 4.x推出快小半年了,受疫情影响,今年业务迭代比较缓慢,没有新系统,也觉得暂时没必要去重构业务代码,所以一直只关注不动手。最近比较闲,组件库针对Antd 4.0做了适应性重构,作为一个胶水层,最大程度的去磨平4.0版本Form这种破坏性变动,减少以后业务代码升级4.x版本的调整量。
Antd 4.x到底做了哪些变化,在官方文档可以看到。
这篇文章主要讲针对于4.x Form的变化,我重构组件库的思路。
Antd-doddle 2.x文档地址, 支持4.x: http://doc.closertb.site, 首次加载较慢,请耐心等候。
Antd-doddle 1.x文档地址, 支持3.x: http://static.closertb.site, 首次加载较慢,请耐心等候。
试用项目Git 地址
项目在线试用地址, 请勿乱造
FormGroup重构思路
Form组件变化
4.x 中除了Icon,最大的更改就在于Form,我自己感受到的变化是:
最大的改变
就是增量式更新,3.x版本,任意表单项改变,都会造成Form.create包裹的全部表单项重新render,这是非常大的性能消耗;而4.x之后,任意表单项改变,只有设置了shouldUpdate属性的表单项有可能执行render,类似于React 16新增的componentShouldUpdate
;根据上面的变化点,针对性的做重构,由外向内层层剖析;
FormGroup的变化
由于以前FormGroup组件,除了收集form方法和公共配置,也作为一个标识,接管了组件内部的渲染层;3.x版本其form实例由Form.create,即业务代码提供;4.x与其相似,只不过是通过hooks生成form实例。
变化点主要在于4.x版本Form要提供initialValues的设置,且这是一个defaultValue的设置,所以我们需要拓展,让其支持values为异步数据时,表单项的值能跟随其改变, 其原理很简单,监听value的变化,并重置表单数据,实现代码如下:
上面有句代码
initialValues: {},
会让人困惑, 为什么没有赋值为datas呢;这个又是antd的一个隐藏知识点,举个例子:所以当我们想做一些需求,比如先编辑一个表单,表单中有数据了;但没做操作关闭了,然后点了新增按钮,传了一个空对象,这事就发现bug了,上一次编辑的数据还在,除了这个,还有一些其他的业务场景会用到,在antd中也有一个类似的issue:
在表单里有很多元素且拥有initialValue的时候, 如何简单的清空表单
所以在这个组件设计上,就将
initialValues
默认置空数据,然后设置的数据采用setFieldsValue
来重置。如果想清空表单,直接传入一个空数据,被组件检测到后,内部调用resetFields
来实现。快夸一下天才的我。FormRender的变化
相比于FormGroup的变化,子组件FormRender相比较就变化小一点,主要在适应第二点变动,用代码的方式更直观:
重构之后
联动表单的实现变化
主要实现三种联动,根据其他表单项的变化,来改变关联表单项
由于以前每次表单项变化,都会引起其他表单项render,所以可以暴力的通过FormGroup增加数据层(useRef)与监(bao)听(guo)每个表单项的onChange来实现;
新的Form新增了onFormChange回调来支持增量式数据收集,但第四点变动,让老的方案GG;表单项的联动需要依赖shouldUpdate来实现,这也是官方推荐的方案;
其本质是,设置了shouldUpdate属性的FormItem,其仅仅作为一个容器,这个容器监听了类似onFormChange这种事件,然后根据shouldUpdate来判断是否需要重新渲染容器内的子元素,子元素渲染实现是一个应用React renderPrrops的设计模式;
所以联动方案似乎变得更简单了, 就多一层FormItem包裹,看部分代码实现:
其他
除了上面这些变化,其实还有很多边边角角的变化,比如:
还有,还有一些未考虑好的的新增特性。
使用对比
实现一个小的编辑框,类似下面这样:
重构前的代码
重构之后
不仔细看,是不是不易察觉到变化
重构感受
因为用的还不像3.x版本那么熟练,很多特性还没用上,所以先重构一个简易版本,来完成日常场景,后面再慢慢迭代。
如果感兴趣,可以fork项目或查看项目文档
The text was updated successfully, but these errors were encountered: