Vite4.4+Typescript+React18+Zustand 最新搭建企业级前端项目

先附上源码地址:github.com/cwjbjy/vite...

本文基于vite4+react18+typescript5+react-router-dom6+zustand4+antd5

1. 初始化项目

  1. node版本要求:目前我的node版本v16.20.0,Vite需要Node.js版本14.18+,16+。
  1. vscode插件安装:ES7+ React/Redux/React-Native snippets,Typescript React Code Snippets,vscode-styled-components,Prettier - Code formatter,ESlint

  2. 创建项目

npm create vite\@latest vite-react-ts-seed -- --template react-ts

  1. 安装项目依赖

yarn

2. 配置 tsconfig

  1. 修改tsconfig.json
js 复制代码
{
    "compilerOptions": {
      "target": "ESNext", // 将代码编译为最新版本的 JS
      "lib": ["ESNext", "DOM", "DOM.Iterable"],// 引入 ES 最新特性和 DOM 接口的类型定义
      "module": "ESNext", // 使用 ES Module 格式打包编译后的文件
      "skipLibCheck": true,// 跳过对 .d.ts 文件的类型检查
      "baseUrl": ".", //查询的基础路径
      "paths": { "@/*": ["src/*"] }, //路径映射,配合别名使用
  
      /* Bundler mode */
      "moduleResolution": "node", // 使用 Node 的模块解析策略
      "allowImportingTsExtensions": true, //允许在模块导入语句中使用Typescript文件的扩展名(.ts)
      "allowJs": true, //允许使用js
      "resolveJsonModule": true, // 允许引入 JSON 文件
      "isolatedModules": true, // 要求所有文件都是 ES Module 模块。
      "noEmit": true,// 不输出文件,即编译后不会生成任何js文件
      "jsx": "react-jsx", //将JSX代码转换为普通的JavaScript代码
      "esModuleInterop": true, // 允许使用 import 引入使用 export 导出
  
      /* Linting */
      "strict": true,// 开启所有严格的类型检查
      "forceConsistentCasingInFileNames": true, // 不允许对同一个文件使用不一致格式的引用
      "noUnusedLocals": true,//报告未使用的局部变量的错误
      "noUnusedParameters": true,//报告函数中未使用参数的错误
      "noFallthroughCasesInSwitch": true//确保switch语句中的任何非空情况都包含
    },
    "include": ["src"], //需要检测的文件
    "references": [{ "path": "./tsconfig.node.json" }] //为文件进行不同配置
  }
  1. 修改tsconfig.node.json
js 复制代码
{
    "compilerOptions": {
      "composite": true,
      "skipLibCheck": true,
      "module": "ESNext",
      "moduleResolution": "Node",
      "allowSyntheticDefaultImports": true //允许从没有默认导出的模块进行默认导入
    },
    "include": ["vite.config.ts"]
}
  1. 新建 src/typings.d.ts(在src文件夹下新建)
js 复制代码
//声明window上自定义属性,如事件总线
declare interface Window {
    eventBus: unknown;
  }

提示:遇到ts报错,有些时候是配置未生效,可以重启vscode或ts服务(vscode快捷键 ctrl+shift+p调出命令行,输入Restart TS Server)

  1. 修改package.json
js 复制代码
"scripts": {
    "ts": "tsc --noEmit",
  },

运行 yarn ts 即可查看文件是否有ts类型错误

3. 配置路径别名

  1. 安装

yarn add @types/node -D

  1. 修改vite.config.ts
js 复制代码
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path"; //这个path用到了上面安装的@types/node

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  //这里进行配置别名
  resolve: {
    alias: {
      "@": path.resolve("./src"), // @代替src
    },
  },
});
  1. ts 路径映射在上述修改tsconfig.json时通过baseUrl与paths属性已完成

4. 配置 ESLint 和 prettier

  1. 安装
js 复制代码
//eslint 安装
yarn add eslint -D

//eslint react规则
yarn add eslint-plugin-react -D

//eslint react hooks
yarn add eslint-plugin-react-hooks -D

//eslint 验证react组件可以安全的被快速更新
yarn add eslint-plugin-react-refresh -D

//eslint 识别ts语法
yarn add @typescript-eslint/parser -D

//eslint ts默认规则补充
yarn add @typescript-eslint/eslint-plugin -D

//eslint import规则
yarn add eslint-plugin-import -D

//eslint prettier插件安装
yarn add eslint-plugin-prettier -D

//用来解决与eslint的冲突
yarn add eslint-config-prettier -D 

//安装prettier
yarn add prettier -D
  1. 修改 .eslintrc.cjs
js 复制代码
module.exports = {
    root: true,
    env: { browser: true, es2020: true, node: true },
    extends: [
      'eslint:recommended',
      'plugin:react/recommended',
      'plugin:react/jsx-runtime', // react补充配置
      'plugin:@typescript-eslint/recommended',
      'plugin:react-hooks/recommended',
      'plugin:prettier/recommended',
      'eslint-config-prettier',
    ],
    parserOptions: {
      ecmaVersion: 'latest',
      sourceType: 'module',
      ecmaFeatures: {
        jsx: true,
      },
    },
    settings: {
      react: {
        version: 'detect',
      },
    },
    ignorePatterns: ['dist', 'node_modules', '.eslintrc.cjs'],
    parser: '@typescript-eslint/parser',
    plugins: ['react', '@typescript-eslint', 'react-refresh', 'import', 'prettier'],
    rules: {
      'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
      //import导入顺序规则
      'import/order': [
        'error',
        {
          //按照分组顺序进行排序
          groups: ['builtin', 'external', 'parent', 'sibling', 'index', 'internal', 'object', 'type'],
          //通过路径自定义分组
          pathGroups: [
            {
              pattern: 'react*', //对含react的包进行匹配
              group: 'builtin', //将其定义为builtin模块
              position: 'before', //定义在builtin模块中的优先级
            },
            {
              pattern: '@/components/**',
              group: 'parent',
              position: 'before',
            },
            {
              pattern: '@/utils/**',
              group: 'parent',
              position: 'after',
            },
            {
              pattern: '@/apis/**',
              group: 'parent',
              position: 'after',
            },
          ],
          //将react包不进行排序,并放在前排,可以保证react包放在第一行
          pathGroupsExcludedImportTypes: ['react'],
          'newlines-between': 'always', //每个分组之间换行
          //根据字母顺序对每个组内的顺序进行排序
          alphabetize: {
            order: 'asc',
            caseInsensitive: true,
          },
        },
      ],
      '@typescript-eslint/no-explicit-any': ['off'], //允许使用any
      '@typescript-eslint/no-this-alias': [
        'error',
        {
          allowedNames: ['that'], // this可用的局部变量名称
        },
      ],
      '@typescript-eslint/ban-ts-comment': 'off', //允许使用@ts-ignore
      '@typescript-eslint/no-non-null-assertion': 'off', //允许使用非空断言
      'no-console': [
        //提交时不允许有console.log
        'warn',
        {
          allow: ['warn', 'error'],
        },
      ],
      'no-debugger': 'warn', //提交时不允许有debugger
    },
  };

rules更多配置:eslint.org/docs/latest...

  1. 新建 .prettierrc
js 复制代码
{
    "endOfLine": "auto",
    "printWidth": 120,
    "semi": true,
    "singleQuote": true,
    "tabWidth": 2,
    "trailingComma": "all",
    "bracketSpacing": true
  }
  1. 新建 .prettierignore
js 复制代码
# 忽略格式化文件 (根据项目需要自行添加)
node_modules
dist
  1. 重启vscode使配置生效

  2. 配置package.json

可以看到App.tsx文件在import处飘红,因为结尾没有使用分号,并且import导入规则也不符合我们设定的规范

修改package.json

js 复制代码
"scripts": {
    "lint": "eslint src --fix --ext .ts,.tsx,.js,.jsx --max-warnings 0",
  },

运行 yarn lint,可以看到上述eslint(prettier/prettier)问题都将被修复

5. 配置 husky、lint-staged、@commitlint/cli

husky:一个为git客户端增加hook的工具

lint-staged:仅对Git 代码暂存区文件进行处理,配合husky使用

@commitlint/cli:让commit信息规范化

  1. 创建git仓库

git init

  1. 安装
js 复制代码
//目前husky为8.0.3版本
yarn add husky -D 

yarn add lint-staged@^13.3.0 -D

yarn add @commitlint/cli@^17.6.1 -D

yarn add @commitlint/config-conventional@^17.6.1 -D

提示:lint-staged,@commitlint/cli,@commitlint/config-conventional 最新包需要Node.js版本18+,因此这里安装固定版本

  1. 运行
js 复制代码
//生成 .husky 的文件夹
npx husky install

// 添加 hooks,会在 .husky 目录下生成一个 pre-commit 脚本文件
npx husky add .husky/pre-commit "npx --no-install lint-staged"

// 添加 commit-msg
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'
  1. 修改package.json
js 复制代码
"lint-staged": {
    "src/**/*.{js,jsx,ts,tsx,json}": [
      "yarn run lint",
      "prettier --write"
    ]
  }
  1. 新建commitlint.config.cjs

提示:由于package.json的"type": "module",需将commonjs文件显示声明为.cjs

js 复制代码
module.exports = {
    extends: ['@commitlint/config-conventional'],
  };

提交格式:

js 复制代码
git commit -m <type>[optional scope]: <description> //注意冒号后面有空格
- type:提交的类型(如新增、修改、更新等)
- optional scope:涉及的模块,可选
- description:任务描述

type类型:

类别 含义
feat 新功能
fix 修复 bug
style 样式修改(UI校验)
docs 文档更新
refactor 重构代码(既没有新增功能,也没有修复 bug)
perf 优化相关,比如提升性能、体验
test 增加测试,包括单元测试、集成测试等
build 构建系统或外部依赖项的更改
ci 自动化流程配置或脚本修改
revert 回退某个commit提交
  1. 提交示范(非规范提交,将commit提交失败)
js 复制代码
git commit -m 'feat: 增加 xxx 功能'
git commit -m 'bug: 修复 xxx 功能'

6. vscode 保存自动格式化

新建.vscode文件夹,在.vscode下新建 settings.json

js 复制代码
{
    "editor.codeActionsOnSave": {
      "source.fixAll": true
    }
  }

之后每次文件有修改,保存时,都会自动格式化

7. 配置路由

react-router-dom(6.18.0)最新指南

8.配置状态管理库

还在用redux?试试更方便的zustand

9. 配置scss

antd5已经采用css-in-js方案,不再需要安装less。我们这里展示安装scss示例

  1. 安装

yarn add sass -D

  1. 配置全局 scss 样式文件

新建 src/assets/styles/index.scss

js 复制代码
$test-color: red;

配置vite.config.ts

js 复制代码
css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "@/assets/styles/index.scss";',
      },
    },
  },

新建src/index.scss

js 复制代码
body {
    color: $test-color;
  }

在组件中引入,修改main.tsx

js 复制代码
import './index.scss';

重启项目,查看效果

提示:平时写react组件样式时,推荐使用styled-components

10. 配置antd

  1. 安装

yarn add antd

  1. 使用
js 复制代码
import { Button } from 'antd';

const Login = () => {
  return (
    <div>
      登录页
      <Button type="primary">Button</Button>
    </div>
  );
};

export default Login;

11. 配置环境变量

新建 .env(所有环境生效).env.development(开发环境配置) .env.production(生产环境配置)

  1. 定义变量

以 VITE_ 为前缀定义变量

js 复制代码
VITE_BASE_URL = '//127.0.0.1:9000/api'
  1. 定义变量ts类型

修改vite-env.d.ts

js 复制代码
/// <reference types="vite/client" />
interface ImportMetaEnv {
    readonly VITE_BASE_URL: string;
  }
  
  interface ImportMeta {
    readonly env: ImportMetaEnv;
  }
  1. 使用变量
js 复制代码
import.meta.env.VITE_BASE_URL 
  1. 在vite.config.ts中使用环境变量

使用 loadEnv 读取环境变量

js 复制代码
import { defineConfig, loadEnv } from 'vite';
//...

export default ({ mode }) => {
  console.log('mode', loadEnv(mode, process.cwd()).VITE_BASE_URL); //127.0.0.1:9000/api  
  return defineConfig({
      //...
  });
};

使用 yarn dev 启动命令,读取 .env 与 .env.development的内容

修改package.json

js 复制代码
"scripts": {
    "test":"vite --mode test", //新增
  },

使用 yarn test 启动命令,读取.env 与 .env.test的内容

12. 配置请求

  1. 安装
js 复制代码
yarn add axios 

yarn add ahooks
  1. 新建utils/axios.ts
js 复制代码
import axios from 'axios';

/*
 * 创建实例
 * 与后端服务通信
 */
const HttpClient = axios.create({
  baseURL: import.meta.env.VITE_BASE_URL,
});

/**
 * 请求拦截器
 * 功能:配置请求头
 */
HttpClient.interceptors.request.use(
  (config) => {
    const token = '222';
    config.headers.authorization = 'Bearer ' + token;
    return config;
  },
  (error) => {
    console.error('网络错误,请稍后重试');
    return Promise.reject(error);
  },
);

/**
 * 响应拦截器
 * 功能:处理异常
 */
HttpClient.interceptors.response.use(
  (config) => {
    return config;
  },
  (error) => {
    return Promise.reject(error);
  },
);

export default HttpClient;
  1. 新建apis/model/userModel.ts

定义请求参数类型与返回数据类型

js 复制代码
//定义请求参数
export interface ListParams {
    id: number; //用户id
  }
  
  export interface RowItem {
    id: number; //文件id
    fileName: string; //文件名
  }
  
  //定义接口返回数据
  export interface ListModel {
    code: number;
    data: RowItem[];
  }
  1. 新建apis/user.ts
js 复制代码
import HttpClient from '../utils/axios';
import type { ListParams, ListModel } from './model/userModel';

export const getList = (params: ListParams) => {
  return HttpClient.get<ListModel>('/list', { params });
};
  1. 使用
js 复制代码
import { useRequest } from 'ahooks';
import { getList } from '@/apis/user';

const Login = () => {
    
  useRequest(getList, {
    defaultParams: [{ id: 2 }],
  });

  return (
    <div>
      登录页
    </div>
  );
};

export default Login;

在chrome的network中可以看到请求了http://127.0.0.1:9000/api/list?id=2

提示:useRequest可以帮助我们避免在请求中执行setState,而路由已切换导致内存泄漏问题。以及请求防抖,节流,轮询等功能。

13. 配置端口与代理

修改vite.config.ts

js 复制代码
export default defineConfig({
    ...
  server: {
    host: '0.0.0.0',
    port: 8080, 
    open: true,
    https: false,
    proxy: {
      '/api': {
        target: '要代理的地址',
        changeOrigin: true,
        ws: true,
        rewrite: (path: string) => path.replace(/^\/api/, ''),
      },
    },
  },
  });

14. 打包配置

修改vite.config.ts

1. 分包

js 复制代码
build: {
    rollupOptions: {
      output: {
        manualChunks: {
          react: ['react', 'react-dom', 'react-router-dom', 'zustand'],
          antd: ['antd'],
        },
      },
    },
  },

vite的打包基于rollup,对rollup感兴趣的,可以看Rollup炼金术:打造NPM包提高开发效率!

2. 生成 .gz 文件

  1. 安装

yarn add vite-plugin-compression -D

  1. 修改vite.config.ts

默认情况下插件在开发 (serve) 和生产 (build) 模式中都会调用,使用 apply 属性指明它们仅在 'build' 或 'serve' 模式时调用

这里打包生成 .gz 插件仅需在打包时使用

js 复制代码
import viteCompression from 'vite-plugin-compression'

  plugins: [
    //...
    {
      ...viteCompression(),
      apply: 'build',
    },
  ],
  1. js和css文件夹分离
js 复制代码
export default defineConfig({
    build: {
      rollupOptions: {
        output: {
          chunkFileNames: "static/js/[name]-[hash].js",
          entryFileNames: "static/js/[name]-[hash].js",
          assetFileNames: "static/[ext]/[name]-[hash].[ext]",
        },
      },
    },
  });

15. vite与webpack使用区别

1. 静态资源处理

webpack:使用require处理

vite:使用 new URL(url, import.meta.url).href 处理

import.meta.url 包含了对于目前 ES 模块的绝对路径

new URL(url , base) 构造函数返回一个新创建的 URL 对象,如果url 是相对 URL,则会将 base 用作基准 URL。如果 url 是绝对 URL,则无论参数base是否存在,都将被忽略

js 复制代码
new URL('../assets/images/home.png', import.meta.url).href

//在src/constants/menus.ts下引入图片
//import.meta.url返回 http://localhost:8080/src/constants/menus.ts

//new URL(...).href返回
//http://localhost:8080/src/assets/images/home.png

示例:

  1. 新建utils/share.ts
js 复制代码
//引入静态资源
export const getAssetsFile = (url: string) => {
    return new URL(`../assets/images/${url}`, import.meta.url).href;
  };
  1. 在assets下新建images文件夹,并放入一张home.png图片

  2. 使用

js 复制代码
import { getAssetsFile } from '@/utils/share'; 

<img src={getAssetsFile('home.png')} /> //在组件内使用

2. 读取文件资源

webpack

js 复制代码
//1.directory:要查找的文件路径
//2.useSubdirectories:是否查找子目录
//3.regExp:要匹配文件的正则
const files = require.context(directory,useSubdirectories,regExp)

vite

js 复制代码
//读取同文件夹下所有.tsx文件
const modules = import.meta.glob('./*.tsx');

16. 结尾

对vite+vue感兴趣的,可参考 Vite4.3+Typescript+Vue3+Pinia 最新搭建企业级前端项目

本篇文章将随评论区留言的建议持续更新

相关推荐
梦帮科技40 分钟前
AI 音乐产品的发布工程:验证门、数据发布、回滚与生产运维纪律
数据结构·数据库·架构·node.js·音视频·动态规划·推荐算法
西安栈上月明软件科技41 分钟前
从 Linux 0.01 到 AI 开源:星图邻的开源实践
人工智能·自然语言处理·架构·开源·fastapi
泯泷43 分钟前
那段文字是谁删的?Yjs 14 正式版之前,一套删除归属方案的实现与边界
前端·javascript·算法
野槐3 小时前
前端项目优化(有了我,会发生...)
前端
Lynne3093 小时前
工业网络,到底该集中还是自治?
网络·架构·数据
aa小小4 小时前
【无标题】
前端
计算机魔术师5 小时前
还在单线程跟 AI 聊天?Claude Code 并行多会话让你一个人干三个人的活
前端
志尊宝5 小时前
Vue3 零基础每日笔记(038):亲手封装 useDebounceFn 与 useThrottleFn——高频事件的流量阀
前端·javascript·vue·html·vue3
JavaGuide5 小时前
对标 MinIO!全新一代分布式文件系统正式发布
前端·后端
风骏时光牛马5 小时前
AI驱动自动化业务工作流搭建实践
前端