React项目从0到1搭建

前言:React项目一般都是基于create-react-app等脚手架快速搭建,初始化项目。现在有点空闲的时间,琢磨如何从0到1进行搭建。

搭建基础项目

一、初始化&基本配置

bash 复制代码
npm init && git init

修改package.json

json 复制代码
{
  "name": "react-ts-web",
  "version": "1.0.0",
  "description": "a react typescript web template",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "react",
    "typescript",
    "vite"
  ],
  "homepage": "https://github.com/xxx/xxx",
  "bugs": {
    "url": "https://github.com/xxx/xxx/issues"
  },
  "author": "xxx <xxx@xx.com>",
  "license": "ISC"
}

package.json的全部字段描述 参考

二、安装核心依赖,搭建开发环境

1. 安装依赖

bash 复制代码
pnpm add react react-dom && pnpm add -D vite typescript @types/react-dom @types/react @types/node

2. tsconfig.json初始化,修改

bash 复制代码
npx tsc --init
json 复制代码
{
  "references": [{ "path": "./tsconfig.vite.json" }], // 指定工程引用依赖
  "compilerOptions": {
    "target": "ES2020", // 指定编译的ECMAScript目标版本
    "lib": ["ES2020", "DOM", "DOM.Iterable"], // 编译过程中需要引入的库文件的列表
    "jsx": "react-jsx", // 指定 jsx 代码的生成,无需在每个jsx文件中引入React
    "module": "ESNext", // 指定生成哪个模块系统代码
    "moduleResolution": "Node", // 决定如何处理模块
    "allowImportingTsExtensions": true, // 允许TypeScript文件通过TypeScript特定的扩展名如.ts, .mts, 或 .tsx互相导入
    "resolveJsonModule": true, // 允许引入 JSON 文件
    "allowJs": true, // 是否允许编译javascript文件
    "noEmit": true, // 设置是否输出 js 文件,一般是设置为 false,将打包等工作交给 vite/webpack 等工具
    "isolatedModules": true, // 将每个文件做为单独的模块
    "esModuleInterop": true, // 支持合成模块的默认导入
    "forceConsistentCasingInFileNames": true, // forceConsistentCasingInFileNames
    "strict": true, // 启用所用严格的类型检查
    "skipLibCheck": true, // 跳过对 .d.ts 文件的类型检查
    "types": ["node", "vite/client"],
    "baseUrl": ".", // 用于设置解析非相对模块名称的基本目录,相对模块不会受到baseUrl的影响
    "paths": {
      // 用于设置模块名到基于baseUrl的路径映射
      "@/*": ["./src/*"]
    }
  },
  "include": ["src", "@types"],
  "exclude": ["node_modules"]
}

3. 新增tsconfig.vite.json

json 复制代码
{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

作用: tsconfig.vite.json 专门为vite.config.ts提供的ts配置文件。
单独配置原因: 运行环境不同,项目的代码运行在浏览器环境,而vite.config.ts运行在Node环境。相应的两者需要的接口类型也各不一样。

4. 按照所示,搭建基础架子

App.tsx

tsx 复制代码
export default function App() {
  return (
    <div>
      <h1>Welcome to React Web.</h1>
    </div>
  );
}

main.tsx

tsx 复制代码
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const root = createRoot(document.querySelector('#app')!);
root.render(
  <StrictMode>
    <App />
  </StrictMode>,
);

使用 StrictMode 来启用组件树内部的额外开发行为和警告

vite.config.ts

ts 复制代码
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
});

修改 package.json, 添加 vite 启动、打包脚本和 ts 类型检测,内容太多就只展示新增内容

json 复制代码
{
  ...
  "scripts": {
    "serve": "vite",
    "build": "vite build",
    "tsc": "tsc --noEmit"
  },
}

新增 .gitignore。git提交时,忽略文件提交

sh 复制代码
node_modules
dist
.DS_Store

三、配置ESLint + prettier

eslint: 代码检查工具,主要用来发现代码错误、统一代码风格。 prettier: 对代码进行格式化,并不关注代码质量潜在问题的检查,倾向于团队的代码风格的规范或统一。

1. ESLint 配置

这里使用alloy团队的一套 规范,基本上按照文档配置操作即可。

这里的 eslint 配置个人比较喜欢用json,就采用了json格式

2. 将 prettier 作为 ESLint 的规则来使用,让 ESLint 托管 prettier。

bash 复制代码
pnpm add -D eslint-config-prettier prettier eslint-plugin-prettier eslint-config-prettier

修改 .eslintrc.json

json 复制代码
{
  "root": true,
  "parserOptions": {
    "project": ["./tsconfig.json", "./tsconfig.vite.json"]
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "extends": ["alloy", "alloy/react", "alloy/typescript", "prettier"],
  "plugins": ["prettier"],
  "env": {
    "browser": true
  },
  "globals": {},
  "rules": {
    "prettier/prettier": "error"
  },
  "ignorePatterns": ["dist", "node_modules"]
}

新增 .prettierrc

sh 复制代码
"eslint-config-alloy/.prettierrc.js"

3. vite.config.ts配置ESLint

typescript 复制代码
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
+ import eslint from 'vite-plugin-eslint'

export default defineConfig({
-   plugins: [react()]
+   plugins: [react(), eslint()],
});

四、配置husky + lint-staged

husky: 给git添加hook钩子,在特定的 Git 操作触发之前或之后执行预定义的脚本
lint-staged: 在 Git 暂存区中运行指定脚本的工具。它通常与 Husky 一起使用

1. 安装husky、lint-staged依赖

bash 复制代码
pnpm dlx husky-init && pnpm install
pnpm add -D lint-staged

2. 新增 .lintstagedrc.json

json 复制代码
{
  "*.{js,jsx}": ["eslint --fix"],
  "*.{ts,tsx}": ["eslint --fix", "bash -c tsc"]
}

通过bash -c tsc,让tsc识别项目中的tsconfig.json配置

五、git commit 规范提交

1. 全局安装 commitizen

bash 复制代码
pnpm add commitizen -g

2. 项目根目录安装

bash 复制代码
pnpm add -D cz-git

3. package.json新增内容

json 复制代码
{
  ...
  "scripts": {
    "commit": "git-cz"  
  },
 "config": {
   "commitizen": {
     "path": "node_modules/cz-git"
   }
  },  
}

4. 添加commitlint.config.cjs

js// 复制代码
/** @type {import('cz-git').UserConfig} */

module.exports = {
  rules: {
    // @see: https://commitlint.js.org/#/reference-rules
  },
  prompt: {
    alias: { fd: 'docs: fix typos' },
    messages: {
      type: '选择你要提交的类型 :',
      scope: '选择一个提交范围(可选):',
      customScope: '请输入自定义的提交范围 :',
      subject: '填写简短精炼的变更描述 :\n',
      body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
      breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
      footerPrefixesSelect: '选择关联issue前缀(可选):',
      customFooterPrefix: '输入自定义issue前缀 :',
      footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
      confirmCommit: '是否提交或修改commit ?'
    },
    types: [
      { value: 'feat', name: 'feat:     新增功能 | A new feature' },
      { value: 'fix', name: 'fix:      修复缺陷 | A bug fix' },
      { value: 'docs', name: 'docs:     文档更新 | Documentation only changes' },
      { value: 'style', name: 'style:    代码格式 | Changes that do not affect the meaning of the code' },
      { value: 'refactor', name: 'refactor: 代码重构 | A code change that neither fixes a bug nor adds a feature' },
      { value: 'perf', name: 'perf:     性能提升 | A code change that improves performance' },
      { value: 'test', name: 'test:     测试相关 | Adding missing tests or correcting existing tests' },
      { value: 'build', name: 'build:    构建相关 | Changes that affect the build system or external dependencies' },
      { value: 'ci', name: 'ci:       持续集成 | Changes to our CI configuration files and scripts' },
      { value: 'revert', name: 'revert:   回退代码 | Revert to a commit' },
      { value: 'chore', name: 'chore:    其他修改 | Other changes that do not modify src or test files' },
    ],
    useEmoji: false,
    emojiAlign: 'center',
    themeColorCode: '',
    scopes: [],
    allowCustomScopes: true,
    allowEmptyScopes: true,
    customScopesAlign: 'bottom',
    customScopesAlias: 'custom',
    emptyScopesAlias: 'empty',
    upperCaseSubject: false,
    markBreakingChangeMode: false,
    allowBreakingChanges: ['feat', 'fix'],
    breaklineNumber: 100,
    breaklineChar: '|',
    skipQuestions: [],
    issuePrefixes: [
      // 如果使用 gitee 作为开发管理
      { value: 'link', name: 'link:     链接 ISSUES 进行中' },
      { value: 'closed', name: 'closed:   标记 ISSUES 已完成' }
    ],
    customIssuePrefixAlign: 'top',
    emptyIssuePrefixAlias: 'skip',
    customIssuePrefixAlias: 'custom',
    allowCustomIssuePrefix: true,
    allowEmptyIssuePrefix: true,
    confirmColorize: true,
    maxHeaderLength: Infinity,
    maxSubjectLength: Infinity,
    minSubjectLength: 0,
    scopeOverrides: undefined,
    defaultBody: '',
    defaultIssues: '',
    defaultScope: '',
    defaultSubject: ''
  }
}

六、添加changelog

bash 复制代码
pnpm add standard-version

1. package.json添加脚本

json 复制代码
{
  ...
  "release": "standard-version"
}

2. 首次生成changelog

version:1.0.0版本

bash 复制代码
npx standard-version --first-release

功能

一、SASS支持

bash 复制代码
pnpm add -D sass

1. 新增文件 src/App.scss

scss 复制代码
.main {
  h1 {
    color: red;
  }
}

2. src/App.tsx修改

jsx 复制代码
import './App.scss';

export default function App() {
  return (
    <div className="main">
      <h1>Welcome to React Web.</h1>
    </div>
  );
}

二、图片支持

1. 新增 ./src/assets/react.png

2. src/App.tsx修改

jsx 复制代码
import './App.scss';
import ReactLogo from './assets/React.png';

export default function App() {
  return (
    <div className="main">
      <h1>Welcome to React Web.</h1>
      <img src={ReactLogo} alt="logo" />
    </div>
  );
}

3. 新增@types/assets.d.ts

ts 复制代码
declare module '*.png';
declare module '*.svg';
declare module '*.jpeg';
declare module '*.jpg';

三、设置路径别名

1. vite.config.ts修改

ts 复制代码
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import eslint from 'vite-plugin-eslint';
+ import path from 'path';

export default defineConfig({
+ resolve: {
+   alias: {
+     '@': path.resolve(__dirname, 'src'),
+   },
+   extensions: ['.js', '.jsx', '.ts', '.tsx'],
+ },
  plugins: [react(), eslint()],
});

2. eslint 修改

bash 复制代码
pnpm add eslint-import-resolver-alias

.eslintrc.json

json 复制代码
{
  "root": true,
  "parserOptions": {
    "project": ["./tsconfig.json", "./tsconfig.vite.json"]
  },
  "settings": {
    "react": {
      "version": "detect"
    },
+   "import/resolver": {
+     "alias": [["@", "./src"]]
+   }
  },
  "extends": ["alloy", "alloy/react", "alloy/typescript", "prettier"],
  "plugins": ["prettier"],
  "env": {
    "browser": true
  },
  "globals": {},
  "rules": {
    "prettier/prettier": "error",
    "no-return-assign": "off"
  },
  "ignorePatterns": ["dist", "node_modules"]
}

3. tsconfig.json 修改

json 复制代码
{
  "references": [{ "path": "./tsconfig.vite.json" }], // 指定工程引用依赖
  "compilerOptions": {
    "target": "ES2020", // 指定编译的ECMAScript目标版本
    "lib": ["ES2020", "DOM", "DOM.Iterable"], // 编译过程中需要引入的库文件的列表
    "jsx": "react-jsx", // 指定 jsx 代码的生成,无需在每个jsx文件中引入React
    "module": "ESNext", // 指定生成哪个模块系统代码
    "moduleResolution": "Node", // 决定如何处理模块
    "allowImportingTsExtensions": true, // 允许TypeScript文件通过TypeScript特定的扩展名如.ts, .mts, 或 .tsx互相导入
    "resolveJsonModule": true, // 允许引入 JSON 文件
    "allowJs": true, // 是否允许编译javascript文件
    "noEmit": true, // 设置是否输出 js 文件,一般是设置为 false,将打包等工作交给 vite/webpack 等工具
    "isolatedModules": true, // 将每个文件做为单独的模块
    "esModuleInterop": true, // 支持合成模块的默认导入
    "forceConsistentCasingInFileNames": true, // forceConsistentCasingInFileNames
    "strict": true, // 启用所用严格的类型检查
    "skipLibCheck": true, // 跳过对 .d.ts 文件的类型检查
+   "baseUrl": ".", // 用于设置解析非相对模块名称的基本目录,相对模块不会受到baseUrl的影响
+   "paths": { // 用于设置模块名到基于baseUrl的路径映射
+     "@/*": ["./src/*"]
+   }
  },
  "include": ["src"],
  "exclude": ["node_modules"]
}

四、React路由配置

1. 安装依赖

bash 复制代码
pnpm add react-router-dom

2. 新增页面

src/pages/404.tsx

tsx 复制代码
import { useNavigate } from 'react-router-dom';

export default () => {
  const navigate = useNavigate();
  const goBack = () => {
    navigate(-1);
  };
  return (
    <>
      <h1>About Page</h1>
      <button onClick={goBack}>返回</button>
    </>
  );
};

src/pages/Home.tsx

tsx 复制代码
import { Link } from 'react-router-dom';

export default () => {
  return (
    <>
      <h1>Home Page</h1>
      <Link to="/about">页面跳转</Link>
    </>
  );
};

src/pages/About.tsx

tsx 复制代码
import { useNavigate } from 'react-router-dom';

export default () => {
  const navigate = useNavigate();
  const goBack = () => {
    navigate(-1);
  };
  return (
    <>
      <h1>About Page</h1>
      <button onClick={goBack}>返回</button>
    </>
  );
};

3. 移除src/App.tsx

4. 定义路由表src/router/index.ts

typescript 复制代码
import About from '../pages/About';
import Home from '../pages/Home';
import NoFound from '../pages/404';

import { RouteObject } from 'react-router-dom';

const routeConfig: RouteObject[] = [
  {
    path: '/',
    Component: Home,
  },
  {
    path: '/about',
    Component: About,
  },
  {
    path: '*',
    Component: NoFound,
  },
];

export default routeConfig;

5. 入口文件main.tsx调整

tsx 复制代码
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
import routeConfig from './router';

const router = createBrowserRouter(routeConfig);

const root = createRoot(document.querySelector('#app')!);
root.render(
  <StrictMode>
    <RouterProvider router={router} />
  </StrictMode>,
);

五、状态管理机制

目前 React 的状态管理机制解决方案是百花齐放,如:reduxreact-toolkitzustandrecoil等。为了降低上手成本,这里使用目前比较火的 zustand。具体就不展开对比各种状态管理机制的不同了。

1. 安装依赖

bash 复制代码
pnpm add zustand

2. 新增 src/store/counter.ts

ts 复制代码
import { create } from 'zustand';

interface CounterStore {
  num: number;
  increase: () => void;
}

const useCounterStore = create<CounterStore>()((set) => ({
  num: 0,
  increase: () => set((state) => ({ num: (state.num += 1) })),
}));

export default useCounterStore;

3. 新增组件 src/components/CountNum.tsx

tsx 复制代码
import useCounterStore from '@/store/counter';

export default () => {
  const num = useCounterStore((state) => state.num);
  return <p>{num} </p>;
};

4. 修改 src/pages/Home.tsx

tsx 复制代码
import { Link } from 'react-router-dom';
+ import useCounterStore from '@/store/counter';
+ import CountNum from '@/components/CountNum';

export default () => {
+ const increase = useCounterStore((state) => state.increase);

  return (
    <>
      <h1>Home Page</h1>
+     <CountNum />
+     <button onClick={increase}>加一</button>
+     <br />
      <Link to="/about">页面跳转</Link>
    </>
  );
};

六、 多环境打包

可以为每个不同的环境,设置特定的内容:接口请求地址、OSS配置等

1. 新增配置文件

env/.env.development

sh 复制代码
VITE_PROJECT_ENV=development
VITE_APP_TITLE=My App (development)

env/.env.production

sh 复制代码
VITE_PROJECT_ENV=production
VITE_APP_TITLE=My App (production)

env/.env.staging

sh 复制代码
VITE_PROJECT_ENV=staging
VITE_APP_TITLE=My App (staging)

env/.env.testing

sh 复制代码
VITE_PROJECT_ENV=testing
VITE_APP_TITLE=My App (testing)

2. vite.config.ts调整

ts 复制代码
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import eslint from 'vite-plugin-eslint';
import path from 'path';

export default defineConfig({
+ envDir: './env',
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
  plugins: [react(), eslint()],
});

3. package.json添加脚本命令

json 复制代码
...
"scripts": {
 ...
 "start:dev": "vite --mode development",
 "start:test": "vite --mode testing",
 "start:staging": "vite --mode staging",
 "start:prod": "vite --mode production",
 "build": "pnpm build:prod",
 "build:dev": "pnpm tsc && vite build --mode development",
 "build:test": "pnpm tsc && vite build --mode testing",
 "build:staging": "pnpm tsc && vite build --mode staging",
 "build:prod": "pnpm tsc && vite build --mode production",
}

4. 页面模版修改

tsx 复制代码
import { Link } from 'react-router-dom';
import useCounterStore from '@/store/counter';
import CountNum from '@/components/CountNum';
+ import { useState } from 'react';

export default () => {
  const increase = useCounterStore((state) => state.increase);
+ const title = useState(import.meta.env.VITE_APP_TITLE);

  return (
    <>
-     <h1>Home Page</h1>
+     <p>{title}</p>
      <CountNum />
      <button onClick={increase}>加一</button>
      <br />
      <Link to="/about">页面跳转</Link>
    </>
  );
};

后续内容,待补充。。。 欢迎指出问题,继续完善!👏

项目代码

链接

参考文献

相关推荐
学习使我快乐012 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19952 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
黄尚圈圈3 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水4 小时前
简洁之道 - React Hook Form
前端
正小安6 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch7 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光7 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   7 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   7 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web7 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery