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 最新搭建企业级前端项目

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

相关推荐
阿伟来咯~35 分钟前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端40 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱43 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai1 小时前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
58沈剑2 小时前
80后聊架构:架构设计中两个重要指标,延时与吞吐量(Latency vs Throughput) | 架构师之路...
架构
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js