项目地址
常用命令
# 开发
npm run start
# 构建
npm run build
# 快速验证打包后的产物,模拟生产环境,`类 nginx` 的环境
# 执行该命令前请先构建项目 npm run build
npm run serve
# 或者 vite preview
整体架构
基础
前端开发与构建工具 vite
vite 支持 react
用于构建用户界面的 JavaScript 库 React
类型系统 typescript
路由 react-router
react-router-dom
组件库 antd
工具库
基于 promise 的网络请求库 axios
实用工具集 loadsh-es
React hook 状态管理 zustand
日期处理 date-fns
编码规范
可组装的 JavaScript 和 JSX 检查工具 ESLint
一个“有态度”的代码格式化工具 Prettier
git commit 规范 commitlint
目录
├── .vscode # 该项目 vscode 配置
├── config # 项目配置
│ └── proxy.ts # 代理
├── public # 资源文件
├── src # 源码目录
│ ├── component # 全局组件
│ ├── page # 页面组件
│ ├── route # 路由配置
│ ├── store # zustand:react hook 状态管理
│ ├── style # 样式自定义
│ ├── util # 工具库
│ │ └── index.ts # 工具方法
│ ├── app.tsx # 主组件
│ ├── index.css # 全局样式
│ ├── main.tsx # 主入口
│ └── vite-env.d.ts
├── .commitlintrc.js # git commit 规范配置
├── .eslintrc.js # eslint 配置
├── .gitignore # git忽略文件
├── .prettierrc.js # prettier 配置
├── index.html # html模版
├── package.json
├── README.md # 文档说明
├── tsconfig.json # ts 配置
└── tsconfig.node.json # ts node 配置
ESLint 配置教程
# http://eslint.cn/docs/user-guide/getting-started
# 安装
npm install eslint --save-dev
# 设置配置,确定配置自动安排相应插件,并生成.eslintrc.js
./node_modules/.bin/eslint --init
# 调试 https://eslint.org/docs/latest/user-guide/command-line-interface#--fix
npx eslint --fix src/App.tsx
# import 导入排序
# https://github.com/import-js/eslint-plugin-import/blob/6304ddc70fc187e248aa65c69bc8983c5051ecd3/docs/rules/order.md
npm install eslint-plugin-import --save-dev
.eslintrc.js import/order 规则
module.exports = {
...,
plugins: ['eslint-plugin-import'],
rules: {
// 注释符后添加空格
'spaced-comment': ['error', 'always'],
// eslint-plugin-import 导入排序规则
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
pathGroups: [
{
pattern: 'react**',
group: 'builtin',
position: 'before',
},
{
pattern: '@/**',
group: 'internal',
position: 'before',
},
{
pattern: '.scss',
group: 'type',
position: 'after',
},
],
pathGroupsExcludedImportTypes: ['react'],
'newlines-between': 'never',
alphabetize: { order: 'asc', caseInsensitive: true },
},
],
},
};
vscode 配置
{
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.fixAll.eslint": true
}
}
git commit 规范
演示
git add .
git commit -m 'xxx' # no no no
git commit -m 'feat: 新功能' # yes
| 类型 | 描述 |
|---|---|
| build | 编译相关的修改,例如发布版本、对项目构建或者依赖的改动 |
| feat | 新特性、新功能 |
| fix | 修复 bug |
| refactor | 代码重构 |
| docs | 文档修改 |
| chore | 其他修改, 比如改变构建流程、或者增加依赖库、工具等 |
| style | 代码格式修改, 注意不是 css 修改 |
| revert | 回滚到上一个版本 |
| ci | 持续集成修改 |
| perf | 优化相关,比如提升性能、体验 |
| test | 测试用例修改 |
配置教程
# 安装 lint-staged husky
npm install lint-staged husky --save-dev
# 在package.json中添加脚本
npm set-script prepare "husky install"
# 初始化husky,将 git hooks 钩子交由,husky执行
npm run prepare
# 初始化 husky, 会在根目录创建 .husky 文件夹
npx husky add .husky/pre-commit "npx lint-staged"
# 安装 commitlint 相关依赖
npm install @commitlint/cli @commitlint/config-conventional --save-dev
# .husky/commit-msg
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
// package.json 增加配置
{
...,
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json,css,scss}": [
"prettier --write"
]
}
}
// .commitlintrc.js 配置
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
['build', 'feat', 'fix', 'refactor', 'docs', 'chore', 'style', 'revert', 'ci', 'perf', 'test'],
],
'type-case': [0],
'type-empty': [0],
'scope-empty': [0],
'scope-case': [0],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [0, 'always', 72],
},
};
zustand:react hook 状态管理
// 根目录store
// eg1: 简单使用 useBear.ts
import { create } from '@/store';
interface BearState {
bear: number;
increase: () => void;
reduce: () => void;
}
const useBearStore = create<BearState>()((set) => ({
bear: 0,
increase: () => set((state) => ({ bear: state.bear + 1 })),
reduce: () => set((state) => ({ bear: state.bear - 1 })),
}));
export default useBearStore;
// eg2: 持久化用户信息
import { create, persist, createJSONStorage } from '@/store';
interface UserInfoProps {
name: string;
phone: string | number;
}
interface UserInfoState {
isLogin: boolean;
token: string;
userInfo: UserInfoProps | null;
setUserInfo: (value: UserInfoProps) => void;
setToken: (token: string) => void;
reset: () => void;
}
/**
* 登录信息
* token信息
* 用户信息
* 持久化storage
*/
const useUserInfoStore = create<UserInfoState>()(
persist(
(set) => ({
isLogin: false,
token: '',
userInfo: null,
setUserInfo: (userInfo: UserInfoProps) => {
set(() => ({ userInfo, isLogin: true }));
},
setToken: (token: string) => {
set(() => ({ token }));
},
reset: () => {
set(() => ({ userInfo: null, isLogin: false, token: '' }));
},
}),
{
name: 'USER_INFO',
storage: createJSONStorage(() => localStorage),
},
),
);
export default useUserInfoStore;
node 服务
快速验证打包后的产物,模拟生产环境,
类 nginx的环境
# 执行该命令前请先构建项目 npm run build
npm run serve
参考 quick-nginx
- 备注:vite preview 支持这个功能
// vite.config.ts
preview: {
proxy: {
'/api': {
target: 'http:api.com',
changeOrigin: true,
},
}
};
antd form 表单封装
基础表单
- input
- number
- password
- textarea
- radio
- select
- checkbox
- switch
checkbox select radio 的 options 支持异步获取
日期时间
- datepicker
- daterangepicker
- timepicker
- timerangepicker
上传
- upload
自定义表单
- priceUnit
表单联动
/demo/form/linkage
筛选表单 FilterForm + PageTable
/demo/form/filter
colType 属性
| colType | default | large |
|---|---|---|
| xxl | 4 | 8 |
| xl | 6 | 12 |
| lg | 8 | 16 |
| md | 12 | 24 |
| xs | 24 | 24 |
useTable
hook/useTable
axios 二次封装
util/fetch.ts
- 示例 page/demo/project/fetch
遇到问题?
Qa1: Cannot access '...' before initialization? es module 循环引用导致
// https://github.com/vitejs/vite/issues/3033
// vite.config.ts
export default defineConfig({
plugins: [
// your plugins,
{
name: 'singleHMR',
handleHotUpdate({ modules }) {
modules.map((m) => {
m.importedModules = new Set();
m.importers = new Set();
});
return modules;
},
},
],
});
继续思考其他解决方案
Qa2: antd 样式在低版本浏览器无法识别
Ant Design 支持最近 2 个版本的现代浏览器。如果你需要兼容旧版浏览器,请根据实际需求进行降级处理:样式兼容
Qa3: 部分用户(老板)没有关闭网页的习惯,在网页有新版本更新或问题修复时,用户继续使用旧的版本,影响用户体验和后端数据准确性。也有可能会出现报错(文件 404)、白屏的情况。
- 方法一:引入 React 错误边界来解决该问题,通过友好的提醒,让用户刷新浏览器。
// src/component/error-boundary
import { ErrorBoundary as ErrorBoundaryComp } from 'react-error-boundary';
import styles from './index.module.scss';
function ErrorFallback({ error }: any) {
function goHome() {
location.href = '/';
}
return (
<div className={styles.ErrorBoundaryBox}>
<h1>Something went wrong: </h1>
<pre style={{ color: 'red' }}>{error.message}</pre>
<button onClick={() => location.reload()}>刷新试试</button>
<button onClick={goHome}>返回首页</button>
</div>
);
}
const ErrorBoundary: React.FC<any> = ({ children }) => {
return <ErrorBoundaryComp FallbackComponent={ErrorFallback}>{children}</ErrorBoundaryComp>;
};
export default ErrorBoundary;
// src/component/layout
<ErrorBoundary>
<Outlet />
</ErrorBoundary>
- 方法二:检测网页更新并通知用户刷新,支持 vite、umijs 和 webpack 插件。例如:plugin-web-update-notification
我们使用
react 错误边界完全可以自己实现类似检测网页更新的功能。