镇江网站建设 的公司,汕头个人建站模板,win2008r2搭建php网站,办公室装饰袋鼠云数栈从2016年发布第⼀个版本开始#xff0c;就始终坚持着以技术为核⼼、安全为底线、提效为⽬标、中台为战略的思想#xff0c;坚定不移地⾛国产化信创路线#xff0c;不断推进产品功能迭代、技术创新、服务细化和性能升级。
在数栈过去的产品迭代中受限于当前组件的…袋鼠云数栈从2016年发布第⼀个版本开始就始终坚持着以技术为核⼼、安全为底线、提效为⽬标、中台为战略的思想坚定不移地⾛国产化信创路线不断推进产品功能迭代、技术创新、服务细化和性能升级。
在数栈过去的产品迭代中受限于当前组件的版本积累了很多待解决的问题随着新的功能需求不断增加很多原先的组件以及交互设计需要进行优化。
2月伴随着数栈 UI5.0 的焕新升级数栈前端团队一起将组件框架 antd 从 v3.x 升级到了 v4.x更新组件的 UI提升产品的交互体验使数栈产品能够更加灵活地适应未来产品功能迭代的需求。
本文将总结归纳袋鼠云数栈前端框架Antd 从3.x 升级到4.x 的相关步骤及在这个过程中踩过的坑解决的问题。
兼容性问题
第三方依赖兼容问题
· React - 最低 v16.9部分组件使用 hooks 重构 react 升级相关文档https://sourl.cn/6bM4Ep
· Less - 最低 v3.1.0建议升级到 less 4.x
· ant-design/icons-antd - 不再内置 Icon 组件请使用独立的包。
对 3.x 的兼容性处理
或许是考虑到部分组件升级的毁坏性antd4.x 中依然保留了对 3.x 版本的兼容废弃的组件通过 ant-design/compatible 保持兼容例如 Icon、Form。
注意建议 ant-design/compatible 仅在升级过程中稍作依赖升级 4.x 请完全剔除对该过渡包的依赖。
升级步骤
只有一步
ant-design/codemod-v4 自带升级脚本会自动替换代码。
# 通过 npx 直接运行
npx -p ant-design/codemod-v4 antd4-codemod apps/xxxx# 或者全局安装
# 使用 npm
npm i -g ant-design/codemod-v4
# 或者使用 yarn
yarn global add ant-design/codemod-v4# 运行
antd4-codemod src
注意该命令和脚本只会进行代码替换不会进行 AntD 的版本升级需要手动将其升级至4.22.5。
该命令完成的工作1. 将 Form 与 Mention 组件通过 ant-design/compatible 包引入 2. 用新的 ant-design/icons 替换字符串类型的 icon 属性值 3. 将 Icon 组件 type “” 通过 ant-design/icons 引入 4. 将 v3 LocaleProvider 组件转换成 v4 ConfigProvider 组件 5. 将 Modal.method() 中字符串 icon 属性的调用转换成从 ant-design/icons 中引入
antd4-codemod 上图这类报错是 Icon 组件自动替换错误有 2 种处理方式
· 报错文件的 Icon 比较少的情况可以直接手动替换该文件中的 Icon 组件具体替换成 Icon 中的哪个组件可以根据 type 在 Icon 文档中找 Icon文档https://sourl.cn/neHBVS
· 下图中是具体报错的节点可以看到 JSXSpreadAttribute 节点也就是拓展运算符中没有 name 属性所以把 Icon 组件的拓展运算符改一下再执行替换脚本就可以了。 antd4 问题修复
styled-components
styled-components 依赖需要转换写法。 Icon
不要使用兼容包的 icon。 在 3.x 版本中Icon 会全量引入所有 svg 图标文件增加了打包产物 在 4.x 版本中对 Icon 进行了按需加载将每个 svg 封装成一个组件。
注意antd 不再内置 Icon 组件请使用独立的包 ant-design/icons。
· 使用
import { Icon } from antd;
mport { SmileOutlined } from ant-design/icons;const Demo () (divIcon typesmile /SmileOutlined /Button icon{SmileOutlined /} //div
);
· 兼容
import { Icon } from ant-design/compatible;
const Demo () (divIcon typesmile /Button iconsmile //div
);
Form
antd Form 从 v3 到 v4https://sourl.cn/7TiRfp
● Form.create()
在 3.x 中表单中任意一项的修改都会导致 Form.create() 包裹的表单重新渲染造成性能消耗 在 4.x 中Form.create() 不再使用。 如果需要使用 form 的 api例如 setFieldsValue 等需要通过 Form.useForm() 创建 Form 实体进行操作。
· 函数组件写法
// antd v4
const Demo () {const [form] Form.useForm();React.useEffect(() {form.setFieldsValue({username: Bamboo,});}, []);return (Form form{form} {...props} ... /Form)
};
· 如果是 class component也可以通过 ref 获取
class Demo extends React.Component {formRef React.createRef();componentDidMount() {this.formRef.current.setFieldsValue({username: Bamboo,});}render() {return (Form ref{this.formRef}Form.Item nameusername rules{[{ required: true }]}Input //Form.Item/Form);}
}
当我们使用 From.create() 的时候可能会传入参数做数据处理例如
export const FilterForm: any Form.createProps({onValuesChange: (props, changedValues, allValues) {const { onChange } props;onChange(allValues);},
})(Filter);
由于 Form.create 的删除需要放到 Form 中。
Formref{this.formRef}layoutverticalclassNamemeta_formonValuesChange{(_, allValues) {const { onChange } this.props;onChange(allValues);}}● getFieldDecorator
在 4.x 中不在需要 getFieldDecorator 对 Item 进行包裹。注意以下问题
· 将之前写在 getFieldDecorator 中的 name/rules 等移到属性中
· 初始化在 form 中处理避免同名字段冲突问题
· 关于表单联动的问题官方提供了 shouldUpdate 方法。
const Demo () (Form initialValues{{ username: yuwan }}Form.Item nameusername rules{[{ required: true }]}Input //Form.Item/Form
);
● initialValue
· 历史问题
initialValue 从字面意来看就是初始值 defaultValue但是可能会有部分同学使用他的时候会误以为 initialValue 等同于 value。造成这样的误解是因为在 3.x 的版本中一直存在一个很神奇的问题受控组件的值会跟随 initialValue 改变。
看下面的例子点击 button 修改 username, input 框的 value 也会随之改变。
const Demo ({ form: { getFieldDecorator } }) (const [username, setUsername] useState();const handleValueChange () {setUsername(yuwan);}return (FragmentFormForm.Item{getFieldDecorator(username, {initialValue: username,rules: [{ required: true }],})(Input /)}/Form.Item/FormButton onClick{handleValueChange}Change/Button/Fragment)
);const WrappedDemo Form.create()(Demo);
但当 input 框被编辑过initialValue 和 input 的绑定效果就消失了正确的做法应该是通过 setFieldsVlaue 方法去 set 值。
· 4.x 版本的 initialValue
在 4.xantd 团队已经把这个 bug 给解了并且一是为了 name 重名问题二是再次强调其初始值的功能现在提到 Form 中了。当然如果继续写在 Form. Item 中也是可以的但需要注意优先级。
● shouldUpdate
前面有说过form 表单不再会因为表单内部某个值的改变而重新渲染整个结构而设有 shouldUpdate 为 true 的 Item任意变化都会使该 Form. Item 重新渲染。
它会接收 render props从而允许你对此进行控制。这里稍微注意一下请勿在设置 shouldUpdate 的外层 Form. Item 上添加 name 否则你会得到一个 error。
Form.Item shouldUpdate{(prev, next) prev.name ! next.name}{form form.getFieldValue(name) antd (Form.Item nameversionInput //Form.Item)}
/Form.Item
在使用 shouldUpdate 的时候需要在第一个 Form.Item 上加上 noStyle否则就会出现下面这种留白占位的情况。 ● validateTrigger
onBlur 时不再修改选中值且返回 React 原生的 event 对象。如果你在使用兼容包的 Form 且配置了 validateTrigger 为 onBlur 请改至 onChange 以做兼容。
● validator
在 antd3 时我们使用 callback 返回报错。但是 antd4 对此做了修改自定义校验接收 Promise 作为返回值。示例参考如下 · antd3 的写法
FormItem label具体时间 {...formItemLayout}{getFieldDecorator(specificTime, {rules: [{required: true,validator: (_, value, callback) {if (!value || !value.hour || !value.min) {return callback(具体时间不可为空);}callback();},},],})(SpecificTime /)}
/FormItem
· antd4 的写法
FormItemlabel具体时间{...formItemLayout}namespecificTimerules{[{required: true,validator: (_, value) {if (!value || !value.hour || !value.min) {return Promise.reject(具体时间不可为空);}return Promise.resolve();},},]}
SpecificTime /)
/FormItem
● validateFields
不再支持 callback该方法会直接返回一个 Promise可以通过 then / catch 处理。
this.formRef.validateFields().then((values) {onOk({ ...values, id: appInfo.id || });
}).catch(({ errorFields }) {this.formRef.scrollToField(errorFields[0].name);})
或者使用 async/await。
try {const values await validateFields();
} catch ({ errorFields }) {scrollToField(errorFields[0].name);
}
● validateFieldsAndScroll
该 api 被拆分了将其拆分为更为独立的 scrollToField 方法。
onFinishFailed ({ errorFields }) {form.scrollToField(errorFields[0].name);
};
● form.name
在 antd 3.x 版本绑定字段时可以采用 . 分割的方式。如
getFieldDecorator(sideTableParam.primaryKey)
getFieldDecorator(sideTableParam.primaryValue)
getFieldDecorator(sideTableParam.primaryName)
在最终获取 values 时antd 3.x 的版本会对字段进行汇总得到如下
const values {sideTableParam: {primaryKey: xxx,primaryValue: xxx,primaryName: xxx,}
}
而在 antd 4.x下会得到如下的 values 结果
const values {sideTableParam.primaryKey: xxx,sideTableParam.primaryValue: xxx,sideTableParam.primaryName: xxx,
}
· 解决方法
在 antd 4.x 版本传入数组。
name{[sideTableParam, primaryKey]}
name{[sideTableParam, primaryValue]}
name{[sideTableParam, primaryName]}
使用 setFieldsValue 设置值
setFieldsValue({sideTableParam: [{primaryKey: xxx,primaryValue: xxx,primaryName: xxx,},],
});
当我们使用 name{[sideTableParam, primaryKey]} 方式绑定值的时候与其关联的 dependencies/getFieldValue 都需要设置为[sideTableParam, primaryKey]例如
FormItem dependencies{[[alert, sendTypeList]]} noStyle{({ getFieldValue }) {const isShowWebHook getFieldValue([alert, sendTypeList])?.includes(ALARM_TYPE.DING);return (isShowWebHook RenderFormItem({item: {label: WebHook,key: [alert, dingWebhook],component: Input placeholder请输入WebHook地址 /,rules: [{required: true,message: WebHook地址为必填项,},],initialValue: taskInfo?.alert?.dingWebhook || ,},}));}}
/FormItem
当我们希望通过 validateFields 拿到的数据是数组时例如这样 我们可以设置为这样
const formItems keys.map((k: React.Key) (Form.Item key{k} required label名称Form.ItemnoStylename{[names, k]}rules{[{ required: true, message: 请输入标签名称 },{ validator: utils.validateInputText(2, 20) },]}Input placeholder请输入标签名称 style{{ width: 90%, marginRight: 8 }} //Form.Itemi classNameiconfont iconicon_deletecata onClick{() this.removeNewTag(k)} //Form.Item
));
● Tooltip
Form 支持属性 tooltip能够在 label 后产生一个问号直接做提示。 ● extra
针对于想放置于组件下面内容可以使用 extra 来实现。 FormItemlabel过滤条件extra{Tooltip title{customSystemParams}系统参数配置nbsp;QuestionCircleOutlined //Tooltip}
Input.TextArea /
/FormItem
● Form 在数栈的变化
通过这次 UI 升级和 antd 升级之后Form 表单在数栈中的应用发生了较大的变化从老版本的 label/component 横向排版改为了纵向改版在横向空间不⾜的情况下使⽤上下结构能有效提⾼填写表单的效率。 Select
● rc-select
· 底层重写
• 解决些许历史问题
1rc-select rc-select-tree 的 inputValue searchValue 之争。rc-select-tree 是 rc-select 结合 tree 写的一个组件相似但又不同searchValue 就是其中一点也不是没人提过 issue只是人的忘性很大时间长了就忘了、混淆了导致在 rc-select 中甚至出现了 searchValue 的字样。
2inputValue 历史问题this.state.inputValue。 3onSelect 清空了值又会被 onChange 赋值回来。
• 模块复用
在新版的 rc-select 中antd 官方抽取了一个 generator 方法。它主要接收一个 OptionList 的自定义组件用于渲染下拉框部分。这样我们就可以直接复用选择框部分的代码而自定义 Select 和 TreeSelect 对应的列表或者树形结构了。
● labelInValue
在 3.x 版本为 {key: string, label: ReactNode}
在 4.x 版本为 {value: string, label: ReactNode}
Table
● fixed 固定列时文字过长导致错位的问题被完美解决了。
· 历史原因
3.x 中对 table fixed 的实现是写了两个 table, 顶层 fixed 的是一个底层滚动的是一个这样出现这种错位的问题就很好理解了。
要解决也不是没有办法可以在特定的节点去测算表格列的高度但是这个行为会导致重排影响性能问题。
· 解决方案
4.x 中table fixed 不在通过两个 table 来实现他使用了一个 position 的新特性position: sticky;
元素根据正常文档流进行定位然后相对它的最近滚动祖先nearest scrolling ancestor和 containing block (最近块级祖先 nearest block-level ancestor)包括 table-related 元素基于 top、right、bottom 和 left 的值进行偏移偏移值不会影响任何其他元素的位置。
优点
• 根据正常文档流进行定位
• 相对最近滚动祖先 最近块级祖先进行偏移
缺点
• 不兼容 IE11
解决了使用 absolute | fixed 脱离文档流无法撑开高度的问题也不再需要对高度进行测量。
● table.checkbox
· 问题描述
升级后checkbox 宽度被挤压了。 · 解决方案
通过在 rowSelection 中设置 columnWidth 和 fixed 解决。
const rowSelection {fixed: true,columnWidth: 45,selectedRowKeys,onChange: this.onSelectChange,
};
● 渲染条件
antd4 Table 对渲染条件进行了优化对 props 进行“浅比较”如果没有变化不会触发 render。
● 类名更改
.ant-table-content 更改为 .ant-table-container
.ant-form-explain 更改为 .ant-form-item-explain
● dataIndex 修改
在 antd3.0 的时候我们采用 user.userName 能够读到嵌套的属性。
{title: 账号,dataIndex: user.userName,key: userName,width: 200,
}
antd4.0 对此做了修改同 Form 的 name。
{title: 账号,dataIndex: [user, userName],key: userName,width: 200,
}
● table pagination showSizeChanger
· 问题描述
升级 antd4 后发现一些表格分页器多了 pageSize 切换的功能代码中 onChange 又未对 size 做处理会导致底部分页器 pageSize 和数据对不上因此需要各自排查 Table 的 pagination 和 Pagination 组件和请求列表接口的参数。 TablerowKeyuserIdpagination{{total: users.totalCount,defaultPageSize: 10,}}onChange{this.handleTableChange}style{{ height: tableScrollHeight }}loading{this.state.loading}columns{this.initColumns()}dataSource{users.data}scroll{{ x: 1100, y: tableScrollHeight }}
/handleTableChange (pagination: any) {this.setState({current: pagination.current,},this.search);
};
search (projectId?: any) {const { name, current } this.state;const { project } this.props;const params: any {projectId: projectId || project.id,pageSize: 10,currentPage: current || 1,name: name || undefined,removeAdmin: true,};this.loadUsers(params);
};
antd4.0 对此做了修改同 Form 的 name。
TablerowKeyuserIdpagination{{showTotal: (total) 共${total}条,total: users.totalCount,current,pageSize,}}onChange{this.handleTableChange}style{{ height: tableScrollHeight }}loading{this.state.loading}columns{this.initColumns()}dataSource{users.data}scroll{{ x: 1100, y: tableScrollHeight }}
/handleTableChange (pagination: any) {this.setState({current: pagination.current,pageSize: pagination.pageSize,},this.search);
};search (projectId?: any) {const { name, current, pageSize } this.state;const { project } this.props;const params: any {projectId: projectId || project.id,pageSize,currentPage: current || 1,name: name || undefined,removeAdmin: true,};this.loadUsers(params);
};
另外一些同学在 Table 中既写了 onChange也写了 onShowSizeChange这个时候要注意当切换页码条数的时候两个方法都会触发onShowSizeChange 先触发onChange 后触发这个时候如果 onChange 内未对 pageSize 做处理可能导致切页失败看下面代码就明白了写的时候稍微注意一下即可。 ● table sorter columnKey · 问题描述
表格中如果要对表格某一字段进行排序需要在 columns item 里设置 sorter 字段然后在 onChange 里拿到 sorter 对象进行参数处理再请求数据。
需要注意的是很多用到了 sorter.columnKey 来进行判断容易出现问题sorter.columnKey columns item.key如果未设置 key那么获取到的 columnKey 就为空导致搜索失效要么设置 key再进行获取。同理 sorter.field columns item.dataIndex设置 dataIndex通过 sorter.field 进行获取两者都可以。
columns{[{title: 创建时间,dataIndex: gmtCreate1,key: aa,sorter: true,render(n: any, record: any) {return DateTime.formatDateTime(record.gmtCreate);}},...]
}
onChange{(pagination: any, filters: any, sorter: any) {console.log(pagination, --pagination);console.log(filters, --filters);console.log(sorter, --sorter);
}}
● Table 在数栈的变化
通过这次 UI 升级和 antd 升级之后表格在数栈中的应用发生了较大的变化减⼩了表格默认⾼度增加⼀屏可浏览的数据数量。 Tree
Tree 组件取消 value 属性现在只需要添加 key 属性即可。
特别注意 此问题会导致功能出问题需要重点关注
在项目中经常在 TreeItem 中增加参数如 TreeItem value{value} data{data} 。在拖拽等回调中就可以通过 nodeData.props.data 的方式获取到 data 的值。但在 antd4 中获取参数的数据结构发生了改变原先直接通过 props 点出来的不行了。
· 有两种方式取值
1不使用props。直接采用 nodeData.data 的方式也可以直接拿到。
2继续使用 props。在antd4中还是可以通过 props 找到参数只不过 antd 会把所有参数使用 data 进行包裹就需要改成 nodeData.props.data.data。
· 新版数据结构如下 · drag
拖拽节点位置的确定与 3.x 相比进行了变更官网并没有说明。具体如下图 左侧为 3.x右侧为 4.x。 在3.x版本只要把节点拖拽成目标节点的上中下即代表着目标节点的同级上方子集同级下方
在 4.x 版本是根据当前拖拽节点与目标节点的相对位置进行确定最终的拖拽结果。
当拖拽节点处于目标节点的下方且相对左侧对齐的位置趋近于零则最终的位置为目标节点的同级下方。 当拖拽节点处于目标节点的下方且相对左侧一个缩近的位置则最终的位置为目标节点的子集。 当拖拽节点处于目标节点的上方且相对左侧对齐的位置趋近于零则最终的位置为目标节点的同级上方。 Pagination
Pagination自 4.1.0 版本起会默认将 showSizeChanger 参数设置为 true 因而在数据条数超过50时pageSize 切换器会默认显示。这个变化同样适用于 Table 组件可通过 showSizeChanger: false 关闭。
如果 size 属性值为 small则删除 size 属性。
Drawer
当我们在 Drawer 上 设置了 getContainer{false} 属性之后Drawer 会添加上 .ant-drawer-inline 的类名导致我们 position: fixed 失效。 Button
在 antd 3.0 中危险按钮采用 type。 使用如下涉及改动点 type、dangr 属性。 Tabs
使标签页不被选中。
// 3.x
activeKey{undefined}
// 4.x
activeKey{null}
总结
该篇文章详细讲解了数栈前端团队如何从 antd3 升级到 antd4 的详细步骤以及团队在实践过程中发现的一些问题和对应的解决方案为后续产品的开发体验打了基础也提供了一种更好的交互体验。
未来数栈前端团队将会持续关注产品体验以及开发中的技术痛点以开发更好的产品为导向助力业务发展。 《数据治理行业实践白皮书》下载地址https://fs80.cn/380a4b
想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友浏览袋鼠云官网https://www.dtstack.com/?srcszcsdn
同时欢迎对大数据开源项目有兴趣的同学加入我们一起交流最新开源技术信息号码30537511项目地址https://github.com/DTStack