前端开发指南
IfAI React/TypeScript 前端开发指南。
快速开始
前置要求
- Node.js 18+
- npm 或 pnpm
- 熟悉 React 19
- TypeScript 经验
开发设置
bash
# 克隆仓库
git clone https://github.com/peterfei/ifai.git
cd ifai
# 安装依赖
npm install
# 启动开发服务器
npm run tauri:dev组件开发
组件结构
typescript
// src/components/Example/Example.tsx
import React from 'react'
import { useExampleStore } from '../../stores/useExampleStore'
export const Example: React.FC = () => {
const { data, actions } = useExampleStore()
return (
<div className="example-component">
{/* 组件 JSX */}
</div>
)
}最佳实践
- 使用带 hooks 的函数组件
- TypeScript Props 使用接口
- 对昂贵的渲染使用记忆化
- 使用 TailwindCSS 进行样式设置
示例组件
typescript
// src/components/AIChat/MessageList.tsx
import React, { useMemo } from 'react'
import { useChatStore } from '../../stores/useChatStore'
import { MessageItem } from './MessageItem'
interface MessageListProps {
className?: string
}
export const MessageList: React.FC<MessageListProps> = ({ className }) => {
const { messages, isLoading } = useChatStore()
const sortedMessages = useMemo(
() => messages.sort((a, b) => a.timestamp - b.timestamp),
[messages]
)
if (isLoading && sortedMessages.length === 0) {
return <div className="loading">加载中...</div>
}
return (
<div className={`message-list ${className || ''}`}>
{sortedMessages.map((message) => (
<MessageItem key={message.id} message={message} />
))}
</div>
)
}Zustand 状态管理
Store 模式
typescript
// src/stores/useExampleStore.ts
import { create } from 'zustand'
interface ExampleState {
data: string[]
isLoading: boolean
error: string | null
}
interface ExampleActions {
fetchData: () => Promise<void>
clearError: () => void
}
type ExampleStore = ExampleState & ExampleActions
export const useExampleStore = create<ExampleStore>((set, get) => ({
// 状态
data: [],
isLoading: false,
error: null,
// 操作
fetchData: async () => {
set({ isLoading: true, error: null })
try {
const data = await invoke<string[]>('get_data')
set({ data, isLoading: false })
} catch (error) {
set({ error: error.message, isLoading: false })
}
},
clearError: () => set({ error: null })
}))使用 Store
typescript
// 在组件中
import { useExampleStore } from '../../stores/useExampleStore'
export const MyComponent = () => {
// 选择特定状态
const data = useExampleStore((state) => state.data)
const fetchData = useExampleStore((state) => state.fetchData)
// 或使用完整 store
const { data, isLoading, fetchData } = useExampleStore()
useEffect(() => {
fetchData()
}, [fetchData])
return <div>{data.join(', ')}</div>
}Tauri IPC 集成
调用命令
typescript
import { invoke } from '@tauri-apps/api/core'
// 简单命令
const result = await invoke('simple_command', { arg1: 'value' })
// 带类型安全
interface CommandArgs {
path: string
options?: { recursive: boolean }
}
interface CommandReturn {
success: boolean
data: string[]
}
const result = await invoke<CommandReturn>('read_directory', {
path: '/src',
options: { recursive: true }
} satisfies CommandArgs)监听事件
typescript
import { listen } from '@tauri-apps/api/event'
useEffect(() => {
const unlisten = listen('file-changed', (event) => {
console.log('文件已更改:', event.payload)
})
return () => {
unlisten.then((fn) => fn())
}
}, [])TailwindCSS 样式
工具类
typescript
<div className="flex items-center justify-between p-4 bg-gray-800 rounded-lg">
<h2 className="text-xl font-bold text-white">标题</h2>
<button className="px-4 py-2 bg-blue-500 hover:bg-blue-600 rounded">
点击我
</button>
</div>自定义组件
typescript
// 带变体的 Button 组件
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'danger'
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
className,
...props
}) => {
const baseClasses = 'px-4 py-2 rounded font-medium transition-colors'
const variantClasses = {
primary: 'bg-blue-500 hover:bg-blue-600 text-white',
secondary: 'bg-gray-500 hover:bg-gray-600 text-white',
danger: 'bg-red-500 hover:bg-red-600 text-white'
}
return (
<button
className={`${baseClasses} ${variantClasses[variant]} ${className || ''}`}
{...props}
/>
)
}Monaco 编辑器集成
基本设置
typescript
import { Editor } from '@monaco-editor/react'
export const CodeEditor: React.FC = () => {
const [code, setCode] = useState('// 您的代码')
return (
<Editor
height="500px"
language="typescript"
theme="vs-dark"
value={code}
onChange={(value) => setCode(value || '')}
options={{
minimap: { enabled: false },
fontSize: 14,
lineNumbers: 'on'
}}
/>
)
}测试
Vitest 单元测试
typescript
// Example.test.ts
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import { Example } from './Example'
describe('Example', () => {
it('正确渲染', () => {
render(<Example />)
expect(screen.getByText('Example')).toBeInTheDocument()
})
})运行测试
bash
# 单元测试
npm run test
# 监听模式
npm run test:watch
# 覆盖率
npm run test:coverage性能优化
记忆化
typescript
import { memo, useMemo, useCallback } from 'react'
// 记忆整个组件
export const ExpensiveComponent = memo(({ data }) => {
const processedData = useMemo(() => {
return data.map(/* 昂贵计算 */)
}, [data])
const handleClick = useCallback(() => {
// 处理点击
}, [])
return <div onClick={handleClick}>{/* ... */}</div>
})调试
React DevTools
bash
# 安装 React DevTools 浏览器扩展
# 组件检查和性能分析Zustand DevTools
typescript
import { devtools } from 'zustand/middleware'
export const useExampleStore = create(
devtools(
(set, get) => ({
// store 实现
}),
{ name: 'ExampleStore' }
)
)