1、用vite搭建项目
使用npm create vite@latest构建项目,配置项选项react->ts+SWC即可。
让后打开项目npm i。
2、项目配置
2.1、vite配置
主要是配置用到的插件(plugins--比如:svg、mock等)、路径、环境变量的使用、打包、路径重写等操作。
// 导入React插件,使用Swc作为JSX到JS的转换器,提高构建速度
import react from '@vitejs/plugin-react-swc'
// 导入Vite的类型定义,用于类型安全的配置
import type { ConfigEnv, UserConfig } from 'vite'
// 导入Vite的环境变量加载函数
import { loadEnv } from 'vite'
// 导入自定义的环境变量处理函数
import { wrapperEnv } from './scripts/utils'
// 导入SVG图标插件
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
// 导入Vite的模拟请求插件
import { viteMockServe } from 'vite-plugin-mock'
// 导入Node.js的路径解析模块
import { resolve } from 'path'
// 定义并导出配置函数,接收命令和模式参数,返回用户配置对象
export default({command, mode}: ConfigEnv): UserConfig => {
// 获取项目根目录的绝对路径
const root = process.cwd()
// 判断当前命令是否为构建命令
const isBuild = command === 'build'
// 根据模式加载环境变量
const env = loadEnv(mode, root)
// 使用自定义函数处理环境变量,可能包括类型转换或逻辑处理
const viteEnv: any = wrapperEnv(env)
// 解构赋值,从处理后的环境变量中提取端口号和控制台日志开关
const { VITE_PORT, VITE_DROP_CONSOLE } = viteEnv
// 返回配置对象
return {
// 部署路径,空字符串意味着部署在根目录
base : '',
// 服务器配置
server: {
// 监听所有网络接口
host: '0.0.0.0',
// 设置服务器端口
port: VITE_PORT,
// 启动服务器时自动打开浏览器
open: true,
// 关闭HTTPS
https: false,
// 代理配置,此处未配置代理规则
proxy: {},
},
// 插件配置
plugins: [
// 启用React插件
react(),
// 配置SVG图标插件
createSvgIconsPlugin({
// 指定图标目录
iconDirs: [resolve(process.cwd(), 'src/assets/icons')],
// 设置生成的SVG图标ID前缀
symbolId: 'icon-[dir]-[name]'
}),
// 配置模拟请求插件
viteMockServe({
// 模拟数据目录
mockPath: 'mock',
// 忽略以_开头的文件
ignore: /^_/,
// 开发环境启用模拟数据
localEnabled: !isBuild,
// 生产环境启用模拟数据
prodEnabled: isBuild,
// 注入代码,用于初始化生产环境的模拟数据
injectCode: `
import { setupProdMockServer } from 'mock/_createProductionServer';
setupProdMockServer()`
})
],
// 构建配置
build: {
// 设置目标ES版本
target: 'es2015',
// 设置CSS兼容性目标
cssTarget: 'chrome86',
// 使用Terser进行代码压缩
minify: 'terser',
// Terser的配置选项
terserOptions: {
// 压缩选项
compress: {
// 保留Infinity关键字
keep_infinity: true,
// 生产环境删除控制台日志
drop_console: VITE_DROP_CONSOLE
}
},
// 设置警告的代码块大小限制
chunkSizeWarningLimit: 2000
},
// 路径别名配置
resolve: {
// 设置@别名指向src目录
alias: {
'@': resolve(__dirname, './src')
}
}
}
}
utils.js
declare type Recordable<T = any> = Record<string, T>
interface ViteEnv {
VITE_PORT: number
VITE_PROXY: [string, string][]
VITE_DROP_CONSOLE: boolean
}
// read all environment variable configuration files to process.env
export function wrapperEnv(envConf: Recordable): ViteEnv {
const result: any = {}
for (const envName of Object.keys(envConf)) {
let realName = envConf[envName].replace(/\\n/g, '\n')
realName = realName === 'true' ? true : realName === 'false' ? false : realName
if (envName === 'VITE_PORT') {
realName = Number(realName)
}
if (envName === 'VITE_PROXY' && realName) {
try {
realName = JSON.parse(realName.replace(/'/g, '"'))
} catch (error) {
realName = ''
}
}
result[envName] = realName
if (typeof realName === 'string') {
process.env[envName] = realName
} else if (typeof realName === 'object') {
process.env[envName] = JSON.stringify(realName)
}
}
return result
}
2.2、eslint+prettier代码检测
在json配置文件中的scripts执行操作下添加:
"lint:eslint": "eslint --cache \"{src,mock,build}/**/*.{js,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,json,ts,tsx,css,less,html,md}\"",
2.2.1、eslint
用到的库:
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.0.0",
"eslint-define-config": "^2.1.0",
"eslint-plugin-prettier": "^5.0.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"vite-plugin-eslint": "^1.8.1",
eslint.config.js配置文件--文件是 ESLint 的配置文件之一,它以 JavaScript 文件的形式存在,用于定义 ESLint 的配置规则。在早期版本的 ESLint 中,配置文件可能被命名为 .eslintrc
、.eslintrc.json
或 .eslintrc.yml
等,但在 ESLint 8.0.0 版本之后,默认推荐使用 eslint.config.{js,cjs,mjs}
这样的命名方式
// 配置文档: https://eslint.nodejs.cn/
import { defineFlatConfig } from 'eslint-define-config'
import configPrettier from 'eslint-config-prettier'
import pluginPrettier from 'eslint-plugin-prettier'
import * as parserTypeScript from '@typescript-eslint/parser'
import pluginTypeScript from '@typescript-eslint/eslint-plugin'
import js from '@eslint/js'
/** @type {import('eslint-define-config').FlatESLintConfig} */
export default defineFlatConfig([
{
...js.configs.recommended,
ignores: ['src/assets/**'],
plugins: {
prettier: pluginPrettier
},
rules: {
...configPrettier.rules,
...pluginPrettier.configs.recommended.rules,
/*
* Eslint规则配置
* 配置文档: https://eslint.nodejs.cn/docs/latest/rules/
*/
// 需要 let 或 const 而不是 var
'no-var': 'error',
// 禁止在定义变量之前使用变量
'no-use-before-define': 'off',
// 声明后永远不会重新分配的变量需要 const 声明
'prefer-const': 'error',
// 禁止不规则空格
'no-irregular-whitespace': 'off',
// 禁止使用 debugger
'no-debugger': 'off',
// 禁止未使用的变量
'no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}
],
// 使用 prettier 插件
'prettier/prettier': [
'error',
{
endOfLine: 'auto'
}
]
}
},
{
files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"],
languageOptions: {
parser: parserTypeScript,
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true
}
},
},
plugins: {
'@typescript-eslint': pluginTypeScript
},
rules: {
...pluginTypeScript.configs.recommended.rules,
/*
* TypeScript规则配置
* 配置文档: https://typescript-eslint.nodejs.cn/rules/)
*/
// 根据参数、属性和变量的默认值或初始值推断其类型
'@typescript-eslint/no-inferrable-types': 'off',
// 禁止使用自定义 ts 模块和命名空间
'@typescript-eslint/no-namespace': 'off',
// 禁止使用 any 类型
'@typescript-eslint/no-explicit-any': 'off',
// 禁止使用特定类型
'@typescript-eslint/ban-types': 'off',
// 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式返回类型声明
'@typescript-eslint/explicit-function-return-type': 'off',
// 不允许在 import 语句中使用 require 语句
'@typescript-eslint/no-var-requires': 'off',
// 禁止空函数
'@typescript-eslint/no-empty-function': 'off',
// 禁止在变量定义之前使用它们
'@typescript-eslint/no-use-before-define': 'off',
// 禁止 @ts-<directive> 注释代码
'@typescript-eslint/ban-ts-comment': 'off',
// 不允许使用后缀运算符的非空断言(!)
'@typescript-eslint/no-non-null-assertion': 'off',
// 要求导出函数和类的公共类方法的显式返回和参数类型
'@typescript-eslint/explicit-module-boundary-types': 'off',
// 使用顶层 type 限定符进行导入
'@typescript-eslint/no-import-type-side-effects': 'error',
// 不允许在可选链表达式后使用非空断言
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
// 禁止定义未使用的变量
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}
],
// 允许在导入上指定 type 关键字
'@typescript-eslint/consistent-type-imports': [
'error',
{
disallowTypeAnnotations: false,
fixStyle: 'inline-type-imports'
}
],
// 允许枚举成员的值是多种不同类型的有效 js 表达式
'@typescript-eslint/prefer-literal-enum-member': [
'error',
{
allowBitwiseExpressions: true
}
]
}
},
{
files: ['*.d.ts'],
rules: {
'eslint-comments/no-unlimited-disable': 'off',
'import/no-duplicates': 'off',
'unused-imports/no-unused-vars': 'off'
}
},
{
files: ['*.?([cm])js'],
rules: {
'@typescript-eslint/no-require-imports': 'off',
'@typescript-eslint/no-var-requires': 'off'
}
}
])
// export default {
// root: true,
// env: {
// browser: true,
// node: true,
// es6: true
// },
// settings: {
// react: {
// version: 'detect'
// }
// },
// // 指定如何解析语法
// parser: '@typescript-eslint/parser',
// // 优先级低于 parse 的语法解析配置
// parserOptions: {
// ecmaVersion: 7,
// sourceType: 'module',
// jsxPragma: 'React',
// ecmaFeatures: {
// jsx: true
// }
// },
// plugins: ['react', '@typescript-eslint', 'react-hooks', 'prettier'],
// // 继承某些已有的规则
// extends: [
// 'eslint:recommended',
// 'plugin:react/recommended',
// 'plugin:@typescript-eslint/recommended',
// 'plugin:react/jsx-runtime',
// 'plugin:react-hooks/recommended',
// 'prettier',
// 'plugin:prettier/recommended'
// ],
// /*
// * 'off' 或 0 ==> 关闭规则
// * 'warn' 或 1 ==> 规则提示为警告(不影响代码执行)
// * 'error' 或 2 ==> 规则提示为错误(代码不能执行,界面报错)
// */
// rules: {
// /*
// * Eslint规则配置
// * 配置文档: https://eslint.nodejs.cn/docs/latest/rules/
// */
// // 需要 let 或 const 而不是 var
// 'no-var': 'error',
// // 禁止在定义变量之前使用变量
// 'no-use-before-define': 'off',
// // 声明后永远不会重新分配的变量需要 const 声明
// 'prefer-const': 'error',
// // 禁止不规则空格
// 'no-irregular-whitespace': 'off',
// // 禁止使用 debugger
// 'no-debugger': 'off',
// // 禁止未使用的变量
// 'no-unused-vars': [
// 'error',
// {
// argsIgnorePattern: '^_',
// varsIgnorePattern: '^_'
// }
// ],
// // 使用 prettier 插件
// 'prettier/prettier': [
// 'error',
// {
// endOfLine: 'auto'
// }
// ],
// /*
// * TypeScript规则配置
// * 配置文档: https://typescript-eslint.nodejs.cn/rules/)
// */
// // 根据参数、属性和变量的默认值或初始值推断其类型
// '@typescript-eslint/no-inferrable-types': 'off',
// // 禁止使用自定义 ts 模块和命名空间
// '@typescript-eslint/no-namespace': 'off',
// // 禁止使用 any 类型
// '@typescript-eslint/no-explicit-any': 'off',
// // 禁止使用特定类型
// '@typescript-eslint/ban-types': 'off',
// // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式返回类型声明
// '@typescript-eslint/explicit-function-return-type': 'off',
// // 不允许在 import 语句中使用 require 语句
// '@typescript-eslint/no-var-requires': 'off',
// // 禁止空函数
// '@typescript-eslint/no-empty-function': 'off',
// // 禁止在变量定义之前使用它们
// '@typescript-eslint/no-use-before-define': 'off',
// // 禁止 @ts-<directive> 注释代码
// '@typescript-eslint/ban-ts-comment': 'off',
// // 不允许使用后缀运算符的非空断言(!)
// '@typescript-eslint/no-non-null-assertion': 'off',
// // 要求导出函数和类的公共类方法的显式返回和参数类型
// '@typescript-eslint/explicit-module-boundary-types': 'off',
// // 使用顶层 type 限定符进行导入
// '@typescript-eslint/no-import-type-side-effects': 'error',
// // 禁止定义未使用的变量
// '@typescript-eslint/no-unused-vars': [
// 'error',
// {
// argsIgnorePattern: '^_',
// varsIgnorePattern: '^_'
// }
// ],
// // 允许在导入上指定 type 关键字
// '@typescript-eslint/consistent-type-imports': [
// 'error',
// {
// disallowTypeAnnotations: false,
// fixStyle: 'inline-type-imports'
// }
// ],
// // 允许枚举成员的值是多种不同类型的有效 js 表达式
// '@typescript-eslint/prefer-literal-enum-member': [
// 'error',
// {
// allowBitwiseExpressions: true
// }
// ],
// 'react-hooks/rules-of-hooks': 'off',
// 'react-hooks/exhaustive-deps': 'off'
// }
// }
.eslintignore配置--用于指定 ESLint 应该忽略不进行 lint 检查的文件或目录
.vscode
.idea
.husky
.local
/public
/docs
/src/assets
dist
node_modules
pnpm-lock.yaml
Dockerfile
eslint.config.js
postcss.config.js
prettier.config.js
commitlint.config.js
*.md
*.woff
*.ttf
2.2.1、prettier
用到的插件:
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"prettier": "^3.1.0",
prettier.config.js配置文件
// 配置文档: https://prettier.nodejs.cn/
/** @type {import('prettier').Config} */
export default {
// 每行最大宽度,超过换行
printWidth: 120,
// 缩进级别的空格数
tabWidth: 2,
// 用制表符而不是空格缩进行
useTabs: false,
// 语句末尾用分号
semi: false,
// 使用单引号而不是双引号
singleQuote: true,
// 在 JSX 中使用单引号而不是双引号
jsxSingleQuote: true,
// 尾随逗号
trailingComma: 'none',
// 对象字面量中括号之间有空格 { foo: bar }
bracketSpacing: true,
// 将多行 HTML(HTML、JSX)元素的 > 放在最后一行的末尾,而不是单独放在下一行
bracketSameLine: false,
// 在唯一的箭头函数参数周围包含括号(avoid:省略括号, always:不省略括号)
arrowParens: 'avoid',
// 换行符使用 lf 结尾 可选值 auto|lf|crlf|cr
endOfLine: 'lf'
}
.prettierignore配置文件
/dist/*
/public/*
/node_modules/**
.local
**/*.svg
eslint.config.js
postcss.config.js
prettier.config.js
commitlint.config.js
2.3、husky
作为提交的代码检测工具
配置如下:
执行文件:
"prepare": "husky install"
"lint:lint-staged": "lint-staged",
安装包:
"husky": "^8.0.3",
"lint-staged": "^15.1.0",
安装过后执行prepare就行了,会生成husky文件。
之后将文件夹里面修改为如下文件:
注意:文件名不需要后缀名
commit-msg:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no-install commitlint --edit $1
pre-commit:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm run lint:lint-staged
2.4、执行的命令
scripts下加入:
"lint": "eslint src/**/*.{ts,tsx} --fix --quiet",
"prepare": "husky install",
"format": "npx prettier --write ."
3、开发配置
3.1、axios二次封装
/utils/axios
import type { InternalAxiosRequestConfig, AxiosResponse, AxiosError } from 'axios'
import axios from 'axios'
import { message } from 'antd'
import { getToken, clearAuthCache } from '@/utils/auth'
// Create axios instance
const service = axios.create({
baseURL: '/api',
timeout: 10 * 1000
})
// Handle Error
const handleError = (error: AxiosError): Promise<AxiosError> => {
if (error.response?.status === 401 || error.response?.status === 504) {
//登录失效,这里自己处理
clearAuthCache()
location.href = '/login'
}
message.error(error.message || 'error')
return Promise.reject(error)
}
// Request interceptors configuration
service.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const token = getToken()
if (token) {
;(config as Recordable).headers['Authorization'] = `${token}`
}
;(config as Recordable).headers['Content-Type'] = 'application/json'
return config
}, handleError)
// Respose interceptors configuration
service.interceptors.response.use((response: AxiosResponse) => {
const data = response.data
if (data.code === 0) {
return data.data
} else {
message.error(data.message)
return Promise.reject('error')
}
}, handleError)
export { service }
3.2、包安装
根据项目而定,需要什么包可以提前规划好,初始化项目
3.3、配置所需要挂载的包
在main里面:
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import { store, persistor } from './stores'
import App from './App'
import '@/design/index.less'
// register svg icon
import 'virtual:svg-icons-register'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<Provider store={store}>
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</Provider>
</React.StrictMode>
)
3.4、配置env
这里配置的env,还有.env.development、.env.production都可以配置,根据需要而定。
.env
VITE_GLOB_APP_TITLE = 'react-admin-design'
VITE_PORT = 8203
VITE_BUILD_GZIP = false
VITE_DROP_CONSOLE = true
3.5、mook配置
安装mookjs
一个是mock数据的,一个是挂载vite的
"mockjs": "^1.1.0",
"vite-plugin-mock": "2.9.8",
在vite的plugins里面添加如下:
里面包含mock路径,一般跟src同级
viteMockServe({
mockPath: 'mock',
ignore: /^_/,
localEnabled: !isBuild,
prodEnabled: isBuild,
injectCode: `
import { setupProdMockServer } from 'mock/_createProductionServer';
setupProdMockServer()`
})
具体的使用去仓库看:https://github.com/goodkets/react-admin-design
大概配置就是这样,还有很多大项目的配置肯定不止如此,比如多语言、测试工具、打包优化、包管理限制等啥的,我这里只用到了基本项目的配置。