React + TypeScript + Vite 实战:从零构建 Color Picker 应用

🎨 React + TypeScript + Vite 实战:从零构建 Color Picker 应用 --- 完整学习日志

阅读时间 :约 15 分钟 | 难度 :入门 ~ 进阶 | 技术栈:React 19 · TypeScript 6 · Vite 8


📖 前言

最近在学习 React + TypeScript 的工程化开发,于是动手实践了一个小项目 ------ Color Picker(颜色选择器)。这个项目虽然不大,但五脏俱全,涵盖了现代 React 开发的诸多核心概念:

  • ✅ TypeScript 类型系统与接口设计
  • ✅ 组件化开发与 Props 通信
  • ✅ useState 状态管理与不可变更新
  • ✅ useEffect 副作用与异步数据获取
  • ✅ CSS 变量 + 暗黑模式适配
  • ✅ Vite 构建工具链配置
  • ✅ 项目目录结构规范(model / api / components)

本文将 逐文件、逐行 地拆解整个项目,力求让每一位读者都能"看得懂、学得会、用得上"。废话不多说,我们开始!


🏗️ 一、项目架构总览

csharp 复制代码
color-picker/
├── index.html                 # 入口 HTML
├── package.json               # 依赖与脚本
├── vite.config.ts             # Vite 构建配置
├── tsconfig.json              # TypeScript 配置入口
├── tsconfig.app.json          # 应用 TS 配置
├── tsconfig.node.json         # Node 端 TS 配置
├── eslint.config.js           # ESLint 代码规范
├── public/
│   ├── favicon.svg            # 网站图标
│   └── icons.svg              # SVG 图标集
└── src/
    ├── main.tsx               # React 应用挂载入口
    ├── App.tsx                # 根组件
    ├── App.css                # 根组件样式
    ├── index.css              # 全局样式 + CSS 变量
    ├── model/
    │   ├── color.ts           # Color 数据模型
    │   └── member.ts          # MemberEntity 数据模型
    ├── api/
    │   └── memberApi.ts       # 模拟 API 接口
    └── components/
        ├── ColorBrowser.tsx   # 颜色预览组件
        ├── ColorPicker.tsx    # 颜色选择器组件
        └── MemberTable.tsx    # 成员列表组件

🎯 核心设计思想 :按职责分层 ------ model 定义数据形状,api 封装数据获取,components 负责 UI 渲染。这种"数据-逻辑-视图"三层分离的架构,是中大型项目的标配。


⚙️ 二、工程化配置解读

2.1 package.json ------ 项目的"身份证"

json 复制代码
{
  "name": "color-picker",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^19.2.6",
    "react-dom": "^19.2.6"
  },
  "devDependencies": {
    "@eslint/js": "^10.0.1",
    "@types/node": "^24.12.3",
    "@types/react": "^19.2.14",
    "@types/react-dom": "^19.2.3",
    "@vitejs/plugin-react": "^6.0.1",
    "eslint": "^10.3.0",
    "eslint-plugin-react-hooks": "^7.1.1",
    "eslint-plugin-react-refresh": "^0.5.2",
    "globals": "^17.6.0",
    "typescript": "~6.0.2",
    "typescript-eslint": "^8.59.2",
    "vite": "^8.0.12"
  }
}
🔍 关键知识点解读:
字段 说明
"type": "module" 启用 ES Module 规范,允许使用 import/export 语法,告别 require()
"private": true 防止意外发布到 npm 仓库
"dev": "vite" 启动 Vite 开发服务器,享受毫秒级热更新(HMR)
"build": "tsc -b && vite build" 先 TypeScript 类型检查,再 Vite 打包。注意顺序:类型检查失败则不会打包,保证产物类型安全
react: ^19.2.6 本项目使用的是 React 19!带来了更强的服务端组件、更好的 Suspense 等特性
typescript: ~6.0.2 TypeScript 6.0!这是最新主版本,~ 表示只接受补丁更新

📌 学习要点 :dependencies vs devDependencies 的区别 ------ 前者是运行时需要的包(react、react-dom),后者是开发/构建时需要的包(TypeScript、Vite、ESLint),不会被打包进最终产物。

2.2 vite.config.ts ------ 构建利器

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

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

极其简洁!这得益于 Vite 的"约定优于配置"理念:

  • @vitejs/plugin-react:提供 React Fast Refresh(热更新时不丢失组件状态)、JSX 编译等能力
  • defineConfig:提供完整的 TypeScript 类型提示,写配置时 IDE 会自动补全

2.3 tsconfig.json ------ TypeScript 编译配置

主配置文件通过 Project References 拆分为两个子配置:

json 复制代码
{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}

tsconfig.app.json 中值得关注的关键配置:

配置项 值 含义
target es2023 编译目标为 ES2023,现代浏览器已全面支持
module esnext 使用最新的 ES Module 标准
jsx react-jsx 使用 React 17+ 的自动 JSX 运行时 ,无需手动 import React
moduleResolution bundler 专为打包器(Vite/webpack)设计的模块解析策略
verbatimModuleSyntax true 强制使用 import type 导入纯类型,确保类型导入在编译后被完全擦除
noUnusedLocals true 未使用的局部变量报错 ------ 保持代码干净
noUnusedParameters true 未使用的参数报错 ------ 同上
erasableSyntaxOnly true TS 6.0 新特性 :只允许使用可被擦除的语法(如 enum 不可用,推荐用 const 对象替代)

💡 "jsx": "react-jsx" 这个配置非常实用。在 React 17 之前,每个 .tsx 文件顶部都需要 import React from 'react',有了它之后,编译器会自动注入 JSX 运行时的导入,代码更简洁!


🧱 三、数据模型层(Model)

好的架构从数据建模开始。本项目定义了两个核心模型:

3.1 Color 模型

typescript 复制代码
// src/model/color.ts
export interface Color {
  red: number;
  green: number;
  blue: number;
}

极简却精妙:

  • 使用 RGB 色彩空间 ,每个通道 0~255,与 CSS rgb() 函数天然对应
  • interface(而非 type)更利于后续扩展(可以声明合并)
  • 三个字段精确描述一种颜色,无冗余、无歧义

3.2 MemberEntity 模型

typescript 复制代码
// src/model/member.ts
export interface MemberEntity {
  id: number;
  login: string;
  avatar_url: string;
}

模拟的是 GitHub 用户数据:

  • id:用户唯一标识(数字类型,注意 GitHub API 返回的 id 是 number)
  • login:GitHub 用户名
  • avatar_url:头像链接

🎓 最佳实践 :Model 文件只定义类型,不包含任何逻辑。这是"单一职责原则(SRP)"在类型系统上的体现。当项目扩展到几十上百个接口时,你会发现这种分离带来的可维护性优势。


🌐 四、API 层 ------ 模拟异步数据获取

typescript 复制代码
// src/api/memberApi.ts
import type { MemberEntity } from '../model/member';

export const getMembersCollection = (): Promise<MemberEntity[]> => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        {
          id: 1457912,
          login: "brauliodiez",
          avatar_url: "https://avatars.githubusercontent.com/u/1457912?v=3"
        },
        {
          id: 4374977,
          login: "Nasdan",
          avatar_url: "https://avatars.githubusercontent.com/u/4374977?v=3"
        }
      ])
    }, 500);
  })
}
🔍 设计精要:
技巧 说明
import type 纯类型导入,编译后完全消失,对运行时零影响
Promise<MemberEntity[]> 返回值类型明确,调用方知道会拿到什么
setTimeout 500ms 模拟网络延迟,让 UI 有 loading 感知
返回完整数据 模拟真实 API 的返回形状,方便后期替换为真实 fetch 调用

📌 替换为真实 API 只需改一行 :将 getMembersCollection 内部改为 return fetch('https://api.github.com/orgs/lemoncode/members').then(res => res.json()),其余代码完全不变 ------ 这就是接口抽象的魅力!


🧩 五、组件层 ------ 逐行拆解

5.1 入口文件 main.tsx

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

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)
🔑 关键点:
  1. createRoot(React 18+ API) :取代旧版的 ReactDOM.render,是 Concurrent Mode(并发模式) 的入口
  2. ! 非空断言 :document.getElementById('root')! 告诉 TypeScript "这个元素一定存在",避免返回 HTMLElement | null 的类型守卫
  3. <StrictMode> :开发环境下的严格模式,会故意双重调用某些生命周期方法,帮助你发现副作用问题
  4. ./index.css :全局样式的导入,Vite 会将其注入到 <head> 中

5.2 根组件 App.tsx ------ 状态管理的核心

tsx 复制代码
import { useState } from 'react'
import ColorBrowser from './components/ColorBrowser';
import ColorPicker from './components/ColorPicker';
import type { Color } from './model/color';
import MemberTable from './components/MemberTable';

function App() {
  const [color, setColor] = useState<Color>({
    red: 20,
    green: 0,
    blue: 10,
  })

  return (
    <>
      <ColorBrowser color={color} />
      <ColorPicker color={color} onColorUpdated={setColor} />
      <MemberTable />
    </>
  )
}

export default App
🧠 深入理解:

状态提升(Lifting State Up):

scss 复制代码
App (状态持有者)
 ├── ColorBrowser  ← 接收 color,只读展示
 ├── ColorPicker   ← 接收 color + onColorUpdated,读写
 └── MemberTable   ← 独立管理自己的状态
  • color 状态定义在 App(单一数据源 ),确保 ColorBrowser 和 ColorPicker 看到的是同一份颜色数据
  • ColorPicker 通过 onColorUpdated={setColor} 回调通知父组件更新状态 ------ 这是 React 单向数据流的经典实现

泛型 useState<Color> :TypeScript 自动推断出 color 的类型为 Color,setColor 的参数类型也被约束为 Color。如果你尝试 setColor({ red: "hello" }),编辑器会立刻报错!


5.3 ColorBrowser 组件 ------ 颜色预览

tsx 复制代码
import * as React from 'react';
import type { Color } from '../model/color';

interface Props {
  color: Color;
}

const ColorBrowser: React.FC<Props> = (props) => {
  const divStyle: React.CSSProperties = {
    width: '11rem',
    height: '7rem',
    backgroundColor: `rgb(${props.color.red}, ${props.color.green}, ${props.color.blue})`,
  }
  return (
    <div style={divStyle}></div>
  )
}

export default ColorBrowser;
📝 知识点解析:
技术点 详解
React.FC<Props> 函数组件类型标注,FC = FunctionComponent,自动包含 children 属性
React.CSSProperties CSS 属性的 TypeScript 类型,写错属性名 IDE 会报错
模板字符串拼接 rgb(${r}, ${g}, ${b}) 动态生成 CSS 颜色值
纯展示组件 无自身状态,无副作用 ------ 纯函数式组件的典范

🎨 React.CSSProperties 是项目中最容易被忽视却非常实用的类型。它能防止你写出 backgrounColor(拼写错误)这样的 bug,因为 TypeScript 会立刻提醒!


5.4 ColorPicker 组件 ------ 滑块交互

tsx 复制代码
import * as React from 'react';
import type { Color } from '../model/color';

interface Props {
  color: Color;
  onColorUpdated: (color: Color) => void;
}

const ColorPicker: React.FC<Props> = (props) => {
  return (
    <div>
      <input
        type="range"
        min={0}
        max={255}
        value={props.color.red}
        onChange={(e) =>
          props.onColorUpdated({ ...props.color, red: +e.target.value })
        }
      />
      {props.color.red}
      <br />
      <input
        type="range"
        min={0}
        max={255}
        value={props.color.green}
        onChange={(e) =>
          props.onColorUpdated({ ...props.color, green: +e.target.value })
        }
      />
      {props.color.green}
      <br />
      <input
        type="range"
        min={0}
        max={255}
        value={props.color.blue}
        onChange={(e) =>
          props.onColorUpdated({ ...props.color, blue: +e.target.value })
        }
      />
      {props.color.blue}
    </div>
  )
}

export default ColorPicker;
🧬 核心技巧剖析:

1. 不可变更新(Immutable Update)

typescript 复制代码
{ ...props.color, red: +e.target.value }
//  ↑                ↑
//  展开旧对象        覆盖 red 字段,创建一个全新对象

这是 React 状态更新的黄金法则 ------ 永远不要直接修改原对象 !React 通过 Object.is() 对比新旧状态来决定是否重新渲染。如果你直接 props.color.red = 100,React 无法检测到变化。

2. +e.target.value 的一元加号技巧

typescript 复制代码
e.target.value     // "128" --- 字符串类型
+e.target.value    // 128  --- 数字类型(number)

<input> 的 value 始终是 string,但 Color 接口要求 number。+ 运算符是一种简洁的类型转换方式,等价于 Number(e.target.value)。

3. <input type="range"> 滑块控件

原生 HTML5 的滑块输入:

属性 含义
min={0} 最小值 0
max={255} 最大值 255(RGB 单通道上限)
value={...} 受控组件的当前值
onChange={...} 滑块拖动时的回调

5.5 MemberTable 组件 ------ 异步数据 + 列表渲染

tsx 复制代码
import * as React from 'react';
import { getMembersCollection } from '../api/memberApi';
import type { MemberEntity } from '../model/member';

const MemberRow = (props) => {
  const member = props.member
  return (
    <tr>
      <td>
        <img src={member.avatar_url} style={{ maxWidth: '10rem' }} />
      </td>
      <td><span>{member.id}</span></td>
      <td><span>{member.login}</span></td>
    </tr>
  )
}

const MemberTable: React.FC = () => {
  const [memberCollection, setMemberCollection] = React.useState<MemberEntity[]>([]);

  React.useEffect(() => {
    (async () => {
      const members = await getMembersCollection();
      setMemberCollection(members);
    })()
  }, []);

  return (
    <>
      <table>
        <thead>
          <tr>
            <th>Avatar</th>
            <th>Id</th>
            <th>Name</th>
          </tr>
        </thead>
        <tbody>
          {
            memberCollection.map((member: MemberEntity) => (
              <MemberRow key={member.id} member={member} />
            ))
          }
        </tbody>
      </table>
    </>
  )
}

export default MemberTable;
🔬 深度剖析:

1. useEffect + IIFE(立即执行的异步函数表达式)

typescript 复制代码
React.useEffect(() => {
  (async () => {
    const members = await getMembersCollection();
    setMemberCollection(members);
  })()
}, []);

这个模式非常经典!原因在于:

  • useEffect 的回调不能直接是 async 函数(因为 async 函数返回 Promise,而 useEffect 期望返回 void 或 cleanup 函数)
  • 解决之道:在回调内部定义一个 async 的 IIFE(Immediately Invoked Function Expression),立即调用它

2. 空依赖数组 [] 的含义

表示 "只在组件首次挂载 时执行一次"。相当于类组件中的 componentDidMount。

⚠️ 这意味着如果 API 数据更新了,组件不会 自动重新获取。对于真实场景,可能需要加入 [refreshFlag] 依赖或使用 React Query 等方案。

3. key 属性的重要性

tsx 复制代码
memberCollection.map((member) => (
  <MemberRow key={member.id} member={member} />
))

key 是 React 列表渲染的唯一标识符,用于:

  • 追踪列表中的每个元素
  • 优化 diff 算法,避免不必要的 DOM 操作
  • 永远不要用数组索引 index 作为 key(除非列表是静态的、不会增删改的)

4. 子组件 MemberRow 的分离

将 <tr> 抽成独立组件 MemberRow,遵循单一职责原则。当行逻辑变复杂(如点击事件、条件样式)时,修改不会影响表格整体结构。


🎨 六、样式系统设计

6.1 CSS 变量 + 暗黑模式

css 复制代码
:root {
  --text: #6b6375;
  --text-h: #08060d;
  --bg: #fff;
  --border: #e5e4e7;
  --accent: #aa3bff;
  --accent-bg: rgba(170, 59, 255, 0.1);
  --accent-border: rgba(170, 59, 255, 0.5);
  font: 18px/145% var(--sans);
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

@media (prefers-color-scheme: dark) {
  :root {
    --text: #9ca3af;
    --text-h: #f3f4f6;
    --bg: #16171d;
    --border: #2e303a;
    --accent: #c084fc;
    --accent-bg: rgba(192, 132, 252, 0.15);
    --accent-border: rgba(192, 132, 252, 0.5);
  }
}
✨ 设计亮点:
技巧 说明
CSS 自定义属性(变量) 定义全局设计令牌(Design Tokens),统一管理颜色、间距、字体
prefers-color-scheme: dark 系统级暗黑模式媒体查询,自动适配用户系统偏好
font-synthesis: none 禁止浏览器合成加粗/倾斜字体,避免字体失真
text-rendering: optimizeLegibility 优化文本渲染清晰度(移动端建议换成 optimizeSpeed)
-webkit-font-smoothing: antialiased macOS/iOS 字体抗锯齿,渲染更细腻

🎯 核心思想 :CSS 变量本质是"一处修改,全局生效"。如果要换主题色,只需改 --accent 一个变量,所有依赖它的组件样式自动更新。

6.2 CSS 嵌套语法

css 复制代码
#next-steps ul {
  list-style: none;
  padding: 0;
  display: flex;
  gap: 8px;
  margin: 32px 0 0;

  .logo {
    height: 18px;
  }

  a {
    color: var(--text-h);
    font-size: 16px;
    /* ... */

    &:hover {
      box-shadow: var(--shadow);
    }
  }

  @media (max-width: 1024px) {
    margin-top: 20px;
    flex-wrap: wrap;
    justify-content: center;
  }
}

项目使用了 CSS 原生嵌套(CSS Nesting) 语法,这是现代 CSS 的重大升级:

  • .logo 嵌套在 ul 内,编译为 #next-steps ul .logo
  • &:hover 引用父选择器,编译为 #next-steps ul a:hover
  • @media 写在选择器内部,作用域更清晰

📌 目前 CSS 嵌套在 Vite 中开箱即用,无需 Sass/Less 预处理器。

6.3 CSS Modules 替代方案?

本项目使用全局 CSS 方式组织样式。在大型项目中,更推荐使用 CSS Modules (.module.css)或 CSS-in-JS 方案(如 Tailwind CSS、styled-components)来避免样式冲突。不过对于学习项目,全局 CSS 更直观,理解成本更低。


🔄 七、数据流全景图

scss 复制代码
┌──────────────────────────────────────────────────┐
│                     App (状态中心)                  │
│                                                    │
│  const [color, setColor] = useState<Color>({       │
│    red: 20, green: 0, blue: 10                     │
│  })                                                │
│                                                    │
│  ┌─────────────────┐  ┌─────────────────────┐     │
│  │  ColorBrowser    │  │    ColorPicker       │     │
│  │                  │  │                      │     │
│  │  color → 显示    │  │  color → range 值    │     │
│  │  RGB 色块        │  │  onChange → setColor │     │
│  │  (只读, 纯展示)   │  │  (读写, 修改状态)    │     │
│  └─────────────────┘  └─────────────────────┘     │
│                                                    │
│  ┌─────────────────────────────────────────────┐  │
│  │               MemberTable                     │  │
│  │                                               │  │
│  │  useState<MemberEntity[]>  (独立状态)         │  │
│  │  useEffect → getMembersCollection()           │  │
│  │  .map() → <MemberRow />                       │  │
│  └─────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────┘
🔑 关键原则总结:
  1. 单向数据流:数据从父组件流向子组件(通过 Props),子组件通过回调函数通知父组件更新
  2. 状态提升:共享状态放在最近的公共祖先组件中
  3. 组件自治 :MemberTable 的状态是私有的,与 color 状态互不干扰

📊 八、技术要点速查表

技术点 具体实现 所在文件
TypeScript 接口 interface Color { red, green, blue } model/color.ts
类型导入 import type { Color } from '...' 多个文件
泛型 useState useState<Color>({...}) App.tsx
Props 类型标注 React.FC<Props> + interface Props 各组件
CSS 对象类型 React.CSSProperties ColorBrowser.tsx
不可变更新 { ...obj, field: newVal } ColorPicker.tsx
字符串转数字 +e.target.value ColorPicker.tsx
useEffect + async IIFE 模式 (async () => {...})() MemberTable.tsx
受控组件 <input value={...} onChange={...} /> ColorPicker.tsx
列表 key <MemberRow key={member.id} /> MemberTable.tsx
CSS 变量 --accent, --bg, --border index.css
暗黑模式 prefers-color-scheme: dark index.css
CSS 嵌套 &:hover, & > div App.css

🧪 九、运行与验证

bash 复制代码
# 1. 进入项目目录
cd color-picker

# 2. 安装依赖
npm install

# 3. 启动开发服务器
npm run dev
# → Vite 启动在 http://localhost:5173

# 4. 生产构建
npm run build
# → 先 tsc 类型检查,再 vite 打包到 dist/

# 5. 预览生产构建
npm run preview

# 6. 代码质量检查
npm run lint

💡 十、学习心得与最佳实践

✅ 做得好的地方

  1. 类型系统充分利用 :每个 Props 都有明确的 interface 定义,函数返回值都有类型标注,让代码即文档
  2. 组件职责清晰:展示(ColorBrowser)、交互(ColorPicker)、数据(MemberTable)三类组件各司其职
  3. API 层抽象:mock 数据和真实 API 共用相同的接口签名,随时可以切换
  4. 现代化工具链:Vite 8 + React 19 + TypeScript 6,全部是最新版本

🔧 可以改进的地方

改进方向 具体方案
Loading 状态 MemberTable 在数据加载完成前展示骨架屏或 Spinner
Error 状态 API 请求失败时的错误处理和重试机制
CSS Modules 将全局 CSS 迁移为 .module.css,避免样式冲突
使用 React 19 新特性 尝试 use() Hook、useOptimistic 等新 API
自定义 Hook 将 MemberTable 中的 useEffect + useState 抽取为 useMembers() Hook
单元测试 为组件和 API 添加 Vitest + React Testing Library 测试
a11y 无障碍 为滑块添加 aria-label,为颜色块添加合适的语义标签

📚 延伸学习路径

vbnet 复制代码
本项目的知识点 → 下一步学习方向
────────────────────────────────────
TypeScript 接口  → 泛型、工具类型(Partial/Pick/Omit)、条件类型
React 状态管理   → useReducer、Context API、Zustand、Redux Toolkit
组件通信         → Context、Event Bus、状态管理库
CSS 方案         → CSS Modules、Tailwind CSS、styled-components
异步数据         → React Query (TanStack Query)、SWR、RTK Query
构建工具         → Vite 插件开发、esbuild、Rolldown
测试             → Vitest、React Testing Library、Playwright

🎯 十一、总结

这个 Color Picker 项目虽然代码量不大(约 150 行核心代码),但它完整地展示了一个现代 React + TypeScript 项目的标准开发范式:

  1. 类型先行 ------ 先定义数据模型,再写组件
  2. 状态驱动 ------ UI 是状态的函数,state → render
  3. 单向数据流 ------ Props 向下,回调向上
  4. 关注点分离 ------ Model / API / Components 分层清晰
  5. 工程化完备 ------ Vite 构建 + TypeScript 类型检查 + ESLint 代码规范

🧩 代码虽小,架构不小。 理解了这个项目的每一行代码和背后的设计思想,你就掌握了 React 日常开发的 80% 核心技能。剩下的 20%,是在真实项目中不断打磨的工程经验。


相关推荐
光依旧14 分钟前
MCP实战手记(八):从“能跑“到“能上线“——无状态MCP Server的生产落地清单
java·人工智能·spring boot·架构·ai agent·mcp
阿拉斯攀登15 分钟前
从需求文档到方案-无人售货机纯视觉与免密支付
架构
故七月18 分钟前
GEO 信源评估体系:如何判断一条网页信源能否被大模型采信
前端·网络·人工智能
liangshanbo121519 分钟前
Webpack 常见 Plugin 面试题
前端·webpack·node.js
xxwl58521 分钟前
vue3的入门学习
前端·vue.js·学习
用户8471721028422 分钟前
.shot 文件格式规范
前端·后端
web打印社区24 分钟前
政务办事大厅:叫号小票、办理回执网页怎么静默出纸
前端·javascript·vue.js·pdf·html·政务
阿拉斯攀登25 分钟前
双门两路锁四摄-售货机硬件形态的软件建模
架构
abigalexy1 小时前
图解AI应用架构设计
人工智能·ai·架构·系统架构·aigc
C++ 老炮儿的技术栈1 小时前
有符号变量与无符号变量的区别
服务器·开发语言·前端·数据结构·c++·算法·c