尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

HZERO前端开发实战:基于React与DataSet的企业级应用构建指南

HZERO前端开发实战:基于React与DataSet的企业级应用构建指南 1. 项目概述从零到一构建HZERO前端应用如果你正在接触企业级中后台项目尤其是基于微服务架构的那么“HZERO”这个名字大概率已经出现在你的技术雷达上了。它不是一个具体的UI框架而是一个企业级的微服务应用平台提供了一整套开箱即用的解决方案涵盖了从后端服务治理到前端开发范式的方方面面。今天我们不聊后端聚焦于前端。当我们在说“HZERO前端开发”时我们实际上在谈论一套基于React技术栈深度融合了Choerodon UI和DataSet数据管理模型的开发实践。这不仅仅是写几个React组件那么简单它关乎如何在复杂的企业级业务场景下高效、规范、可维护地组织你的前端代码。很多刚接触的开发者会感到困惑文档看了Demo也跑了但一到自己动手从零开始搭一个功能完整的页面就不知从何下手。路由怎么配表单数据如何与后端API优雅交互表格的查询、分页、排序如何与DataSet结合页面权限如何控制这些问题单点看都有答案但串联成一个流畅的开发流程就需要一个完整的脉络。本文的目的就是充当这个脉络。我将以一个典型的“用户管理”模块为例手把手带你走通HZERO前端开发的完整链路从项目初始化、路由配置、模型定义到列表页、详情页、创建/编辑页的实现最后涉及权限与部署。过程中我会重点分享那些官方文档可能一笔带过但在实际开发中会让你卡壳的“坑”和“技巧”。2. 环境搭建与项目初始化选对起跑线在开始写业务代码之前一个稳定且符合规范的项目基础是至关重要的。HZERO前端基于React官方推荐使用Umi作为开发框架和构建工具。Umi提供了路由、构建、部署等开箱即用的能力并且其插件体系与HZERO生态完美融合。2.1 技术栈选型与工具准备首先明确我们的核心依赖React 17/18: 组件化开发的基石。Umi 3/4: 企业级前端应用框架。本文以Umi 3为例因其在HZERO生态中更为成熟稳定。Choerodon UI: 基于Ant Design进行企业级定制的React UI组件库。它不仅是UI组件更深度集成了DataSet数据管理模型这是HZERO前端开发的核心。hzero-front-ui: HZERO官方提供的前端基础组件和工具包包含了许多平台级的功能封装。初始化项目我强烈推荐使用Umi的官方脚手架umijs/create-umi-app它能帮你生成一个最符合Umi规范的项目结构。# 使用 npm npx umijs/create-umi-app my-hzero-app # 或使用 yarn yarn create umijs/umi-app my-hzero-app在创建过程中选择app模板并确保包含typescript。创建完成后进入项目目录安装核心依赖cd my-hzero-app npm install antd choerodon-ui/dataset choerodon-ui/pro choerodon-ui/boot hzero-front-ui --save # 同时安装Umi的相关插件 npm install umijs/plugin-model umijs/plugin-initial-state --save-dev注意choerodon-ui/pro是Choerodon UI的主包包含了所有组件。choerodon-ui/dataset和choerodon-ui/boot是DataSet模型和其启动配置所必需的。hzero-front-ui的版本需要与你后端的HZERO平台版本大致对应避免API不兼容。2.2 核心配置文件详解项目初始化后你需要重点关注config/config.ts(或.umirc.ts) 这个Umi的配置文件。这里是连接所有能力的枢纽。// config/config.ts import { defineConfig } from umi; export default defineConfig({ // 1. 路由配置 routes: [ { path: /, component: /pages/index }, { path: /user, component: /pages/user }, ], // 2. 开启Choerodon UI的按需编译与主题定制 theme: { primary-color: #3F51B5, // 定制主色 }, // 3. 配置额外的Babel插件用于Choerodon UI按需加载 extraBabelPlugins: [ [ import, { libraryName: choerodon-ui/pro, libraryDirectory: es, style: true, }, choerodon-ui/pro, ], ], // 4. 代理配置解决本地开发跨域问题 proxy: { /hzero: { target: http://your-hzero-backend-domain.com, // 你的HZERO后端地址 changeOrigin: true, // pathRewrite: { ^/api: }, // 根据后端实际情况决定是否需要重写 }, }, // 5. 开启Umi的model插件状态管理 model: {}, // 6. 定义全局常量 define: { process.env.API_HOST: http://your-hzero-backend-domain.com, }, });这里最关键的几点是代理和按需加载。代理配置让你在本地开发时能无缝对接后端API而按需加载的Babel配置能显著减小最终打包体积。很多同学在引入Choerodon UI后发现打包文件巨大问题往往就出在这里。2.3 初始化DataSet全局配置DataSet是Choerodon UI的灵魂它是一个前端数据管理模型将表单字段、表格数据、查询条件、提交动作等统一管理。在使用前需要在应用入口进行配置。通常我们在src/app.tsx文件中进行全局配置// src/app.tsx import React from react; import { Boot } from choerodon-ui/boot; // 这是一个自定义的Axios实例用于统一处理请求拦截如添加Token、响应拦截和错误处理 const axiosInstance axios.create({ baseURL: /hzero, // 与代理配置对应 timeout: 30000, }); // 请求拦截器示例添加认证Token axiosInstance.interceptors.request.use((config) { const token localStorage.getItem(access_token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); // 响应拦截器示例统一处理错误 axiosInstance.interceptors.response.use( (response) response, (error) { if (error.response?.status 401) { // 跳转到登录页 window.location.href /login; } // 可以在这里统一处理后端返回的业务错误信息 return Promise.reject(error); } ); // 使用Boot.configure方法全局配置DataSet Boot.configure({ // 配置axios实例 axios: axiosInstance, // 配置反馈组件如message的全局属性 feedback: { duration: 3 }, // 配置字段的默认校验信息 field: { defaultValidationMessages: { valueMissing: 请输入{label}, typeMismatch: 请输入正确的{label}, }, }, }); export function rootContainer(container) { return React.createElement(React.StrictMode, null, container); }这个配置步骤经常被忽略导致后续使用DataSet发起请求时遇到跨域、认证失败或错误信息无法统一处理的问题。提前做好这步能为后续开发扫清很多障碍。3. 路由设计与页面骨架搭建在单页应用SPA中路由是应用的导航骨架。Umi采用了约定式路由与配置式路由相结合的方式对于中后台项目配置式路由因其清晰、可集中管理权限而更受青睐。3.1 配置式路由与布局组件我们继续修改config/config.ts中的路由部分构建一个典型的后台管理布局。// config/config.ts routes: [ { path: /, component: /layouts/BasicLayout, // 全局布局组件 routes: [ { path: /, redirect: /dashboard, // 默认重定向到仪表盘 }, { path: /dashboard, component: /pages/Dashboard, name: 仪表盘, // 用于菜单显示 icon: DashboardOutlined, }, { path: /system, name: 系统管理, icon: SettingOutlined, routes: [ { path: /system/user, component: /pages/system/User/List, name: 用户管理, hideInMenu: false, // 在菜单中显示 }, { path: /system/user/:id, component: /pages/system/User/Detail, name: 用户详情, hideInMenu: true, // 不在菜单中显示通过链接进入 }, { path: /system/user/new, component: /pages/system/User/Edit, name: 新建用户, hideInMenu: true, }, ], }, ], }, { path: /login, component: /pages/Login, layout: false, // 登录页不需要全局布局 }, ],对应的我们需要创建布局组件src/layouts/BasicLayout/index.tsx// src/layouts/BasicLayout/index.tsx import React from react; import { ProLayout } from choerodon-ui/pro-layout; // Choerodon UI的布局组件 import { Link, useLocation, useModel } from umi; import { Avatar, Dropdown, Menu } from choerodon-ui/pro; const BasicLayout: React.FC (props) { const location useLocation(); const { initialState } useModel(initialState); // 假设有全局初始状态模型 const menuHeaderRender (logo, title) ( Link to/ {logo} h1 style{{ marginLeft: 10px }}{title || HZERO应用}/h1 /Link ); const rightContentRender () ( Dropdown overlay{ Menu Menu.Item keyprofile个人中心/Menu.Item Menu.Item keylogout退出登录/Menu.Item /Menu } Avatar sizesmall src{initialState?.currentUser?.avatar} / /Dropdown ); return ( ProLayout titleHZERO Demo logo/logo.svg location{location} menuHeaderRender{menuHeaderRender} menuItemRender{(item, dom) Link to{item.path!}{dom}/Link} rightContentRender{rightContentRender} // 将Umi路由配置转换为ProLayout需要的menuData route{props.route} {props.children} /ProLayout ); }; export default BasicLayout;ProLayout组件极大地简化了后台布局的开发它自动处理了菜单、页头、页脚、面包屑等。关键在于menuItemRender属性它决定了菜单项如何渲染为路由链接。这里的一个常见坑是如果路由配置中的path是相对路径需要处理好与Link组件的结合否则点击菜单可能无法正确跳转。3.2 动态路由与权限注入在实际的HZERO平台中菜单和路由通常不是前端硬编码的而是根据用户权限从后端动态获取的。这涉及到动态路由。Umi提供了patchRoutes方法允许你在运行时修改路由配置。我们可以在src/app.tsx中增加动态路由的逻辑// src/app.tsx (续) export async function patchRoutes({ routes }) { // 1. 从后端API获取当前用户有权限的菜单列表 const menuData await fetch(/hzero/v1/menus/current-user).then(res res.json()); // 2. 定义一个递归函数将后端菜单数据转换为Umi路由格式 const convertMenuToRoute (menus) { return menus.map(menu ({ path: menu.path, component: menu.component ? dynamic({ loader: () import(/pages${menu.component}), // 动态导入组件 }) : undefined, name: menu.name, icon: menu.icon, routes: menu.children ? convertMenuToRoute(menu.children) : undefined, // 可以携带其他元信息如权限编码 meta: { permission: menu.permission }, })); }; // 3. 找到布局路由通常是path为‘/’的那条将动态生成的路由插入其children中 const layoutRoute routes.find(r r.path /); if (layoutRoute layoutRoute.routes) { // 假设我们把动态路由插入到固定路由如仪表盘之后 const dynamicRoutes convertMenuToRoute(menuData); layoutRoute.routes [...layoutRoute.routes, ...dynamicRoutes]; } }同时为了在组件内方便地判断权限我们可以结合Umi的useAccess钩子需要先定义access.ts或自定义的权限Hooks。动态路由是HZERO前端进阶的必由之路它实现了前端资源与后端权限服务的解耦让应用更加灵活。4. 数据模型DataSet的核心概念与实战终于来到了HZERO前端开发最核心、也最具特色的部分——DataSet。你可以把它理解为一个超级加强版的FormikReact Query 状态管理库。它管理着数据的生命周期查询、绑定、校验、提交。4.1 创建第一个DataSet用户查询列表假设我们有一个用户查询接口GET /hzero/v1/users支持分页、排序和条件查询如按用户名、状态过滤。我们为这个列表页创建一个DataSet。首先在src/pages/system/User目录下创建一个listDS.ts文件// src/pages/system/User/listDS.ts import { DataSet } from choerodon-ui/pro; export default function useListDS() { // 1. 定义查询字段 const queryFields [ { name: username, // 字段名对应后端查询参数 label: 用户名, type: string, maxLength: 30, }, { name: enabled, label: 状态, type: boolean, // 值列表用于渲染下拉框或开关 lookupCode: HPFM.ENABLED_FLAG, // 通常来自值列表API这里简化 options: [ { value: true, meaning: 启用 }, { value: false, meaning: 禁用 }, ], }, ]; // 2. 创建DataSet实例 const listDS new DataSet({ // 主查询配置 queryFields, // 绑定查询表单的字段 autoQuery: false, // 是否在组件挂载后自动查询建议false手动控制 paging: server, // 服务端分页 pageSize: 10, // 每页大小 // 数据请求配置 transport: { read: { url: /hzero/v1/users, // 查询API method: get, // 请求参数转换器将DataSet的查询参数分页、排序、条件转换为后端需要的格式 params: ({ dataSet, params, data }) { const { currentPage, pageSize, sortField, sortOrder } dataSet; const queryParams { ...params }; // HZERO后端常见的分页参数格式 queryParams.page currentPage - 1; // 后端页码通常从0开始 queryParams.size pageSize; if (sortField sortOrder) { queryParams.sort ${sortField},${sortOrder}; } // 将查询表单的值合并进去 Object.assign(queryParams, data); return queryParams; }, }, }, // 数据响应转换器将后端返回的数据转换为DataSet能识别的格式 dataToJSON: normal, // 或 dirty 等 // 事件钩子 events: { // 查询成功后的回调 query: ({ dataSet, data }) { console.log(查询成功数据, data); }, // 查询失败后的回调 queryError: ({ error }) { console.error(查询失败, error); }, }, }); return listDS; }这个listDS实例将被注入到我们的列表页面组件中。transport.read.params和dataToJSON是两个关键配置点。不同后端接口的数据格式约定可能不同HZERO后端通常遵循一定的RESTful和分页规范你需要根据实际接口调整这个转换逻辑。我见过很多同学在这里踩坑前端拼命传参后端却说没收到问题就出在参数格式没对上。4.2 在React组件中使用DataSet有了DataSet组件内的逻辑会变得异常清晰。我们创建列表页组件src/pages/system/User/List/index.tsx// src/pages/system/User/List/index.tsx import React, { useRef } from react; import { observer } from mobx-react-lite; // DataSet基于Mobx组件需要被observer包裹 import { Table, TextField, Select, Button, Form, DataSet } from choerodon-ui/pro; import useListDS from ../listDS; // 导入我们定义的DS const UserListPage: React.FC observer(() { // 1. 初始化DataSet const listDS useListDS(); const formRef useRefForm(null); // 2. 查询按钮处理函数 const handleQuery () { listDS.query(); // 触发DataSet的查询动作 }; // 3. 重置查询表单 const handleReset () { listDS.queryDataSet?.reset(); // 重置查询条件DataSet // 重置后立即查询显示所有数据 listDS.query(); }; // 4. 新建用户 const handleCreate () { // 通过Umi的history跳转到创建页 history.push(/system/user/new); }; // 5. 表格列定义 const columns [ { name: username, header: 用户名, width: 150, sortable: true, // 支持服务端排序 }, { name: realName, header: 真实姓名, width: 150, }, { name: email, header: 邮箱, width: 200, }, { name: enabled, header: 状态, width: 100, renderer: ({ value }) (value ? 启用 : 禁用), }, { header: 操作, width: 150, renderer: ({ record }) ( Button onClick{() handleViewDetail(record.get(id))}查看/Button Button onClick{() handleEdit(record.get(id))}编辑/Button / ), }, ]; const handleViewDetail (id) { history.push(/system/user/${id}); }; const handleEdit (id) { history.push(/system/user/${id}?actionedit); }; return ( div style{{ padding: 24px }} h2用户管理/h2 {/* 查询表单区域 */} Form dataSet{listDS.queryDataSet} ref{formRef} columns{3} style{{ marginBottom: 16px }} TextField nameusername placeholder请输入用户名 / Select nameenabled placeholder请选择状态 clearButton / div style{{ textAlign: right, gridColumn: 3 }} Button onClick{handleReset}重置/Button Button colorprimary onClick{handleQuery} style{{ marginLeft: 8px }} 查询 /Button Button onClick{handleCreate} style{{ marginLeft: 8px }} 新建 /Button /div /Form {/* 数据表格区域 */} Table dataSet{listDS} columns{columns} queryBarnone // 我们使用自定义的查询表单所以隐藏Table自带的 pagination{{ showSizeChanger: true, showQuickJumper: true, pageSizeOptions: [10, 20, 50], }} style{{ marginTop: 16px }} / /div ); }); export default UserListPage;观察这个组件你会发现状态逻辑几乎都收敛到了DataSet中。查询条件绑定在Form的dataSet{listDS.queryDataSet}上表格数据绑定在Table的dataSet{listDS}上。点击“查询”按钮只是调用了listDS.query()DataSet会自动收集查询表单的值组织参数发起请求并将返回的数据填充到表格中。分页、排序等操作也只需在Table上配置DataSet会自动处理。4.3 创建/编辑页的DataSet设计列表页的DS主要用于“读”而创建/编辑页的DS则侧重于“写”。我们创建一个用于表单提交的DataSetsrc/pages/system/User/editDS.ts// src/pages/system/User/editDS.ts import { DataSet } from choerodon-ui/pro; export default function useEditDS(isNew: boolean, initialData?: any) { const editDS new DataSet({ // 字段定义包含校验规则 fields: [ { name: username, type: string, label: 用户名, required: true, maxLength: 30, validator: async (value) { if (!isNew) return true; // 编辑时不做重复校验 if (value) { // 调用后端接口校验用户名是否已存在 const res await axios.get(/hzero/v1/users/check?username${value}); return res.data.valid ? true : 用户名已存在; } return true; }, }, { name: realName, type: string, label: 真实姓名, required: true }, { name: email, type: string, label: 邮箱, required: true, pattern: /^[^\s][^\s]\.[^\s]$/, // 简单邮箱正则 }, { name: enabled, type: boolean, label: 状态, defaultValue: true, // 新建时默认启用 }, ], // 数据提交配置 transport: { // 创建 create: ({ data: [data] }) ({ url: /hzero/v1/users, method: post, data, // DataSet会自动将变更的数据组织好 }), // 更新 update: ({ data: [data] }) ({ url: /hzero/v1/users/${data.id}, method: put, data, }), // 读取用于编辑时回显数据 read: (props) ({ url: /hzero/v1/users/${props.params.id}, method: get, transformResponse: (response) { // 对后端返回的数据进行预处理适配DataSet字段 return response; }, }), }, // 事件 events: { submit: ({ dataSet, data }) { console.log(提交成功, data); message.success(isNew ? 创建成功 : 更新成功); // 提交成功后返回列表页 history.push(/system/user); }, submitError: ({ error }) { console.error(提交失败, error); message.error(error.message || 操作失败); }, }, }); // 如果是编辑模式并且传入了初始数据ID则加载数据 React.useEffect(() { if (!isNew initialData?.id) { editDS.query(initialData.id); // 触发read transport } }, [isNew, initialData?.id]); return editDS; }在编辑页组件中我们这样使用// src/pages/system/User/Edit/index.tsx import React from react; import { observer } from mobx-react-lite; import { Form, TextField, Select, Button } from choerodon-ui/pro; import { useParams, history } from umi; import useEditDS from ../editDS; const UserEditPage: React.FC observer(() { const { id } useParams{ id?: string }(); const isNew !id; const editDS useEditDS(isNew, { id }); const handleSubmit async () { // 触发表单校验 const valid await editDS.validate(); if (valid) { // 提交数据DataSet会根据当前记录状态新建或更新自动调用对应的transport editDS.submit(); } else { message.error(请检查表单填写是否正确); } }; const handleCancel () { history.goBack(); }; return ( div style{{ padding: 24px, maxWidth: 600px }} h2{isNew ? 新建用户 : 编辑用户}/h2 Form dataSet{editDS} labelWidth{100} TextField nameusername disabled{!isNew} / {/* 编辑时用户名不可改 */} TextField namerealName / TextField nameemail / Select nameenabled / div style{{ marginTop: 24px, textAlign: center }} Button onClick{handleCancel}取消/Button Button colorprimary onClick{handleSubmit} style{{ marginLeft: 16px }} 提交 /Button /div /Form /div ); }); export default UserEditPage;这里的关键在于editDS.submit()方法。它会自动判断DataSet中记录的状态status为add或update然后调用对应的transport.create或transport.update配置。这种设计让创建和编辑页的逻辑高度统一代码复用率极高。5. 深入DataSet高级特性与性能优化掌握了基础用法后我们来看看如何用DataSet解决更复杂的问题并优化性能。5.1 字段联动与动态校验业务表单中字段之间常常存在联动。例如选择某个国家后城市下拉框的选项需要动态更新。DataSet通过字段的dynamicProps属性可以优雅地实现。fields: [ { name: country, type: string, label: 国家, lookupCode: COUNTRY, // 假设有国家值列表 }, { name: city, type: string, label: 城市, // dynamicProps 是一个函数返回动态的属性对象 dynamicProps: { // 根据country字段的值动态决定city字段的lookupCode lookupCode: ({ record }) { const country record.get(country); return country ? CITY.${country} : null; // 例如 COUNTRY.US - CITY.US }, // 也可以动态控制是否必填、是否禁用 required: ({ record }) record.get(country) CN, // 仅在中国时城市必填 }, }, ]动态校验也是如此validator函数可以访问到记录和其他字段的值。{ name: endDate, type: date, label: 结束日期, validator: (value, name, record) { const startDate record.get(startDate); if (startDate value value startDate) { return 结束日期不能早于开始日期; } return true; }, }5.2 多数据集DataSets协同与主子表在复杂的页面如一个订单头主表带多个订单行子表我们需要多个DataSet协同工作。// 主表DS const headerDS new DataSet({...}); // 子表DS const lineDS new DataSet({ parent: headerDS, // 关键指定父DataSet autoQuery: false, transport: { read: { url: /hzero/v1/orders/{parent.id}/lines, // 使用父记录ID method: get, }, }, events: { // 当主表记录切换时自动查询对应的子表数据 [parent.record.change]: ({ dataSet }) { const parentRecord dataSet.parent.current; if (parentRecord) { dataSet.query(); // 会自动将 {parent.id} 替换为实际值 } else { dataSet.removeAll(); // 清空子表 } }, }, });在组件中将两个DataSet分别绑定到主表单和子表格即可。这种父子关系让主子表数据的同步变得非常简单。5.3 性能优化缓存与批量提交查询缓存对于不常变动的下拉框数据如值列表不要每次打开表单都去查询。可以在应用初始化时一次性加载并缓存到全局状态如Umi的useModel或者使用DataSet的cacheSelection属性。批量提交子表行项目较多时逐条提交性能差。可以配置子表DS的transport.update为批量接口并在submit事件中通过dataSet.updated获取所有变更的行数据一次性提交。虚拟滚动对于可能包含大量数据的表格使用Choerodon UI Table的virtual属性开启虚拟滚动能极大提升渲染性能。按需加载组件使用Umi的dynamic函数或React的lazySuspense对路由组件进行代码分割减少首屏加载体积。6. 部署与上线前的最后检查开发完成后我们需要将前端应用构建并部署到服务器。Umi提供了强大的构建命令。# 生产环境构建 npm run build构建完成后会在项目根目录生成dist文件夹里面就是静态资源HTML, JS, CSS。你可以将这些文件放到任何静态文件服务器如Nginx, Apache或对象存储如阿里云OSS上。6.1 部署配置要点在部署前务必检查config/config.prod.ts生产环境配置// config/config.prod.ts export default { // 1. 修改公共路径publicPath如果你的应用不是部署在域名根目录下 publicPath: process.env.PUBLIC_PATH || /your-sub-path/, // 2. 修改API请求的基础路径指向生产环境后端 define: { process.env.API_HOST: https://prod.your-domain.com, }, // 3. 配置资源输出文件名哈希利于缓存 hash: true, // 4. 可能需要的CDN配置 // chunks: [vendors, umi], // chainWebpack: function (config, { webpack }) { // config.merge({ // output: { // filename: [name].[contenthash:8].js, // }, // }); // }, };6.2 Nginx配置示例如果你使用Nginx一个基本的配置如下server { listen 80; server_name your-frontend-domain.com; root /path/to/your/dist; index index.html; # 处理前端路由History模式 location / { try_files $uri $uri/ /index.html; } # 代理API请求到后端 location /hzero/ { proxy_pass https://your-backend-domain.com/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control public, immutable; } }关键点是try_files $uri $uri/ /index.html;这行它确保了在直接访问前端路由如/system/user时Nginx能正确返回index.html文件由React Router来处理路由而不是返回404。6.3 常见部署问题排查页面空白控制台报资源加载404检查publicPath配置是否正确以及Nginx的root指令是否指向了正确的dist目录。API请求404或跨域检查Nginx的proxy_pass地址是否正确以及后端服务是否允许前端域名的跨域请求CORS。生产环境通常通过Nginx反向代理解决跨域而不是像开发环境那样配置代理。路由刷新后404确保Web服务器如Nginx配置了对所有非静态文件路径的请求都返回index.html即上述try_files配置。页面加载慢检查是否开启了Gzip压缩静态资源是否配置了长期缓存并考虑使用CDN加速。从环境搭建到部署上线我们完整地走通了一个HZERO前端模块的开发流程。这套以DataSet为核心、Choerodon UI为界面、Umi为工程骨架的开发模式在应对企业级中后台复杂表单、表格和交互时能显著提升开发效率和代码可维护性。当然任何框架都有学习曲线初期可能会觉得配置繁琐但一旦熟悉了这种“数据驱动”的思维你会发现很多重复劳动都被框架消化了你可以更专注于业务逻辑本身。在实际项目中你还会遇到更复杂的场景如工作流审批、图表集成、国际化、主题定制等但万变不离其宗理解好DataSet这个核心其他都是在此基础上添砖加瓦。
返回列表