vite-react脚手架搭建

hush2024-08-20访问量: 0clireacttypescriptvite
vite-react脚手架搭建

前言

找个前端规范的最佳实践, 先从脚手架开始, 该脚手架只会提供最基础的功能, 上层功能还得继续封装

该脚手架支持如下功能:

  1. tailwindCSS
  2. react-router、mobx
  3. error-boundary
  4. eslint、prettier、commitlint、lint-staged

创建基础脚手架模版

  1. 在node安装的情况下,本文用 pnpm 进行安装

    先执行如下命令

pnpm create vite

选择 templatereact-ts

  1. 选择使用 pnpm 管理包工具,执行如下命令即安装依赖且启动项目服务
pnpm i
pnpm dev
  1. 删除多余文件, 只保留 main.tsxApp.tsx 文件,并移除tsx文件中引用的各项依赖
|-- src
|-- App.tsx # 主组件
|-- main.tsx # 入口文件

将src文件夹下添加如下文件夹

|-- src
|-- assets # 样式以及图片
|-- components # 基础组件
|-- config # 公共配置
|-- features # 业务组件
|-- pages # 路由对应页面
|-- routers # 路由配置
|-- stores # 全局状态
|-- types # types
|-- utils # 工具库
|-- App.tsx # 主组件
|-- main.tsx # 入口文件

集成开发插件

开发环境支持配置如下:

  1. 支持别名
  2. 支持接口代理
  3. 集成eslint、eslint-plugin-simple-import-sort, 支持格式化导入项
  4. 集成prettier
  5. 集成husky、commitlint、lint-staged等
  6. 集成unplugin-auto-import, 自动导入常用api

配置alias

先安装 @types/node

pnpm add -D @types/node

编辑 vite.config.ts 文件

import { resolve } from "path";
export default defineConfig({
resolve: {
alias: {
"@": resolve(__dirname, "./src"),
},
extensions: [".js", ".json", ".ts", "tsx"],
},
});

编辑 tsconfig.json 文件, 添加如下配置

"compilerOptions":{
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}

配置proxy

编辑 vite.config.ts 文件, 这里根据自身需求来

server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:8001',
changeOrigin: false,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},

集成eslint

vite 默认已经集成了eslint, 因此这里我先不做补充, 直接集成 eslint-plugin-simple-import-sort

pnpm add -D eslint-plugin-simple-import-sort

补充 eslint.config.js 文件:

import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
import simpleImportSort from "eslint-plugin-simple-import-sort";
export default tseslint.config({
ignores: ["dist", "node_modules"]
}, {
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
"simple-import-sort": simpleImportSort,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": [
"warn",
{
allowConstantExport: true
},
],
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
},
}, );

集成prettier

因为这里会使用 tailwindCSS , 所以先把 tailwindCSS 插件先一并装上

pnpm add -D prettier prettier-plugin-tailwindcss

根目录创建 .prettierrc 文件, 写入如下内容, 这里的规则可以自行修改, 我只提供一个示例

{
"useTabs": true,
"tabWidth": 2,
"printWidth": 100,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"semi": true,
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindConfig": "./tailwind.config.js"
}

集成提交三件套

提交三件套为 huskylint-stagedcommitlint , 主要是对提交时做文件校验以及提交文案校验

此时 husky 版本来到v9

pnpm add -D husky lint-staged @commitlint/cli @commitlint/config-conventional

修改 package.json 文件如下:

{
"scripts": {
"prepare": "husky",
"lint": "eslint .",
"prettier": "prettier --write **.{tsx,ts,json}",
"lint-staged": "lint-staged"
// ...
},
"lint-staged": {
"*.{js,ts,tsx,jsx}": ["pnpm eslint"],
"*.{js,ts,tsx,jsx,css,less,scss}": ["pnpm prettier"]
}
}

执行 pnpm prepare 命令, 会自动生成 .husky 文件夹, 里边手动创建 commit-msg 文件写入如下内容:

npx --no -- commitlint --edit \

手动创建 pre-commit 文件,写入如下内容:

pnpm lint-staged

根目录添加 commitlint.config.js 文件

export default {
extends: ["@commitlint/config-conventional"],
// 以下时我们自定义的规则
rules: {
"subject-case": [0],
"type-enum": [
2,
"always",
[
"bug", // 此项特别针对bug号,用于向测试反馈bug列表的bug修改情况
"feat", // 新功能(feature)
"fix", // 修补bug
"docs", // 文档(documentation)
"style", // 格式(不影响代码运行的变动)
"refactor", // 重构(即不是新增功能,也不是修改bug的代码变动)
"test", // 增加测试
"chore", // 构建过程或辅助工具的变动
"revert", // feat(pencil): add ‘graphiteWidth’ option (撤销之前的commit)
"merge", // 合并分支, 例如: merge(前端页面): feature-xxxx修改线程地址
],
],
},
};

此时完成配置, 测试一下, 提交一个错误的文件提交

上述报错阻止提交成功, 证明lint-staged 配置成功

继续测试提交一个正确的文件, 但是 commit 语句不规范, 按照 git commit -m "123" 提交

此时也报错, 提示没有 subject 以及 type, 因此补充完整结构再次提交, git commit -m "feat: init" , 此时成功提交

集成自动引入api

使用 unplugin-auto-import 插件

pnpm add -D unplugin-auto-import

修改 vite.config.ts 文件

import AutoImport from "unplugin-auto-import/vite";
export default defineConfig({
plugins: [
react(),
AutoImport({
imports: ["react", "mobx", "react-router", "react-router-dom"],
}),
],
// ...
});

编写 app.tsx 文件, 引入useState, 但不需要 import 语句, 重新 pnpm dev

发现根目录多了一个 auto-imports.d.ts 文件, 这个文件就是相当于ts类型声明文件, 将项目中用到的api类型从对应的插件中导入, 防止代码报错, 在编译时会自动引入对应的插件

这个文件需要在 tsconfig.json 中引入一下, 且不需要提交, 因此修改 tsconfig.json 文件 include 配置

{
"include": ["src", "src/vite-env.d.ts", "auto-imports.d.ts"]
}

.gitignore 中添加 auto-imports.d.ts

集成应用插件

集成react-router-dom

执行如下命令安装 react-router-dom , 此时 react-router 版本到了6

pnpm add react-router-dom

routers 文件夹下创建 index.tsx 文件,并写入

import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom';
const Home = lazy(() => import('@/pages/Home/index.tsx'));
const About = lazy(() => import('@/pages/About/index.tsx'));
const NotFound = lazy(() => import('@/features/NotFound/index.tsx'));
const routers = [
{
path: '/',
element: <Navigate to="/home" replace={true} />
},
{
path: '/home',
element: <Home />
},
{
path: '/about',
element: <About />
},
{
path: '*',
element: <NotFound />
}
];
const browserRouter = createBrowserRouter(routers);
const Routes = () => <RouterProvider router={browserRouter} />;
export default Routes;

其中 AboutHome 为在 pages 中定义好的组件,内容随意

集成react-error-boundary

react-error-boundary 用来捕获组件错误

编辑 app.tsx 文件中写入如下代码

import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { ErrorFallback, Loading } from '@/features';
import Routes from './routers/index.tsx';
function App() {
return (
<>
<Suspense fallback={<Loading />}>
<ErrorBoundary fallback={<ErrorFallback />}>
<Routes />
</ErrorBoundary>
</Suspense>
</>
);
}
export default App;

ErrorFallback, Loading 为 features 中的组件,具体可以自行定义内容

集成Mobx

引入 mobx 作为状态管理

pnpm add mobx mobx-react

stores 文件夹中创建 index.ts 文件 和 countStore.ts 示例文件

编写 countStore.ts 文件

import { action, makeObservable, observable } from 'mobx';
class CounterStore {
constructor() {
makeObservable(this, {
count: observable,
increment: action,
});
}
count = 0;
increment() {
console.log(this);
this.count++;
}
}
export default new CounterStore();

index.ts 进行统一导出

import countStore from './countStore';
export { countStore };

集成tailwindcss

安装如下依赖:

pnpm add -D tailwindcss postcss autoprefixer

这里使用 iconify 中的 icon 组件, 因此继续安装如下两个插件, 如果你不用这个组件库可以忽略

pnpm add -D @iconify/json @iconify/tailwind

根目录新建文件 tailwind.config.js , 写入如下内容:

const {
addDynamicIconSelectors
} = require("@iconify/tailwind");
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {},
},
plugins: [addDynamicIconSelectors()],
};

根目录新建文件 postcss.config.js , 写入如下内容:

export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

在 assets/styles/index.css 顶部添加如下内容:

@tailwind base;
@tailwind components;
@tailwind utilities;

该css应在 main.tsx 中被引用

此时 tailwindcss 已经集成完毕, 在 app.tsx 中测试一下

写入一个div类名

function App() {
return (
<>
<button className="rounded-md border px-5 py-2 hover:shadow-md">
test
</button>
<button className="icon-[ep--menu]" />
</>
);
}
export default App;

展示效果如下:

icon-[ep--menu] 为 iconify 的图标, 可以在 iconify 中找到

集成请求库

我这里使用的是自己封装的 @hushaha/request , 如果你使用axios或者fetch则可以跳过这里

pnpm add @hushaha/request

在 utils/request/index.ts 中添加如下代码

import { createRequest } from '@hushaha/request';
import { apis, APISchemaRes } from './schema';
const { apiList: http } = createRequest<APISchemaRes>(
{
baseURL: '/api'
},
apis
);
export { http };

在 utils/request/schema.ts 中添加如下代码

import type { APISchemaResponse, ApiSchemas } from '@hushaha/request';
export interface APISchemaRes extends APISchemaResponse {
getUser: {
request: {
petId: string;
};
response: {
code: number;
data: {
id: number;
name: string;
};
};
};
}
export const apis: ApiSchemas<APISchemaRes> = {
getUser: {
path: 'GET pet/:petId',
headers: {
'Content-Type': 'application/json'
}
}
};

此时即可在组件中通过如下方式调取接口

import { http } from '@/utils';
const getUserData = async () => {
const res = await http.getUser({ petId: '1' })
}

tips

这整篇文章代码量居多, 具体的每个插件更新迭代速度比较快, 大版本更新可能会导致使用方法有差异, 因此最好的方式是知道自己想要什么, 然后针对性去看对应文档的使用方法, 再逐步集成

主要常用的插件如上方介绍, 如果有新的好使的插件可以自行封装进去, 至于其他的技术栈脚手架, 道理都是一样的, 主要分为开发插件及应用插件, 开发插件大同小异, 应用插件按照业务场景来, 因此无论是 next、vue、monorepo仓库等, 都可以参照这个流程进行封装

这个脚手架已经集成到我的 hush-cli 脚手架中, 可以通过如下命令快速创建

pnpm create hush-cli

hush-cli 就是一个基于vite二次封装的脚手架库, 把常用的插件封装进去了, 后续会继续补充更多插件类型以及使用示例, 可以向我提 issues 一起维护

参考

博客地址

q-request

create-hush-cli

iconify

tailwindcss

©copyright 2021-2024 by Hush