项目概述
文智 AI 全栈交互系统是一个集前端 React 应用、后端 NestJS 服务和 AI 能力于一体的全栈项目。该项目实现了用户登录、文章管理、AI 聊天和语义搜索等核心功能,采用了现代化的技术栈和架构设计,为用户提供了流畅的交互体验。
技术栈概览
前端技术栈
- React + TypeScript
- React Router DOM(前端路由)
- Zustand(状态管理)
- Tailwind CSS(样式框架)
- Shadcn UI(组件库)
- Axios(HTTP 请求库)
- MockJS(前后端解耦开发)
后端技术栈
- NestJS(企业级后端框架)
- PostgreSQL(关系型数据库)
- Prisma(ORM 工具)
- JWT(身份认证)
- bcrypt(密码加密)
AI 技术
- LangChain(AI 应用开发框架)
- Embedding(语义搜索)
- Chatbot(流式输出)
- AI 生成头像
- Commit Message 生成
核心功能模块
- 用户认证系统:登录、注册、JWT 身份验证
- 文章管理系统:文章列表、详情、发布、评论
- AI 交互系统:智能聊天、语义搜索、AI 生成头像、Commit Message 生成
- 性能优化:图片懒加载、无限滚动、组件缓存
面试回答框架
对于每个技术点,我将采用以下回答框架:
- 技术点概述:简要介绍该技术的作用和重要性
- 核心实现:详细说明实现方案和关键代码
- 使用场景:说明在项目中的具体应用
- 优化策略:分享相关的性能优化或最佳实践
- 常见问题:解答面试官可能会问的高频问题
前端技术栈面试回答
React + TypeScript
技术点概述:React 是一个用于构建用户界面的 JavaScript 库,TypeScript 是 JavaScript 的超集,提供了静态类型检查。两者结合使用可以提高代码的可维护性和可靠性。
核心实现:
// 函数式组件示例
import type { Todo } from '../types/todo';
interface Props {
todo: Todo;
onToggle: (id: number) => void;
onRemove: (id: number) => void;
}
export default function TodoItem({
todo,
onToggle,
onRemove
}: Props) {
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.title}
</span>
<button onClick={() => onRemove(todo.id)}>删除</button>
</li>
);
}
使用场景:项目中所有 UI 组件都使用 React + TypeScript 开发,确保类型安全和代码质量。
优化策略:
- 使用函数式组件和 Hooks
- 合理使用 React.memo 和 useMemo 优化渲染性能
- 类型定义清晰,提高代码可读性和可维护性
常见问题:
- Q: 如何处理 React 中的状态管理? A: 对于简单状态使用 useState,对于复杂状态使用 Zustand 进行全局状态管理。
- Q: TypeScript 在项目中的优势是什么? A: 提供静态类型检查,减少运行时错误,提高代码可读性和可维护性。
React Router DOM
技术点概述:React Router DOM 是 React 官方的路由库,用于实现单页应用的路由管理。
核心实现:
// 路由配置示例
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import MainLayout from './layouts/MainLayout';
import Home from './pages/Home';
import Login from './pages/Login';
import Search from './pages/Search';
import RAG from './pages/RAG';
import Chat from './pages/Chat';
import Mine from './pages/Mine';
import Post from './pages/post';
const router = createBrowserRouter([
{
path: '/',
element: <MainLayout />,
children: [
{
path: '',
element: <Home />,
},
{
path: 'login',
element: <Login />,
},
{
path: 'search',
element: <Search />,
},
{
path: 'rag',
element: <RAG />,
},
{
path: 'chat',
element: <Chat />,
},
{
path: 'mine',
element: <Mine />,
},
{
path: 'post/:id',
element: <Post />,
},
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
使用场景:实现页面之间的导航和路由管理,支持动态路由和嵌套路由。
优化策略:
- 使用路由懒加载减少初始加载时间
- 合理使用嵌套路由提高代码组织性
- 实现路由守卫保护需要认证的路由
常见问题:
- Q: 前端路由和后端路由的区别是什么? A: 前端路由在客户端实现,通过 JavaScript 监听 URL 变化并渲染对应组件;后端路由在服务器端实现,根据 URL 路径返回不同的资源。
- Q: 如何实现路由懒加载? A: 使用 React.lazy 和 Suspense 组件实现路由组件的懒加载。
Zustand 状态管理
技术点概述:Zustand 是一个轻量级的状态管理库,基于 Hooks 实现,相比 Redux 更加简洁易用。
核心实现:
// 用户状态管理示例
import { create } from 'zustand';
interface UserState {
user: {
id: number;
name: string;
avatar: string;
} | null;
token: string | null;
isLogin: boolean;
setUser: (user: any) => void;
setToken: (token: string) => void;
logout: () => void;
}
export const useUserStore = create<UserState>((set) => ({
user: null,
token: localStorage.getItem('token'),
isLogin: !!localStorage.getItem('token'),
setUser: (user) => set({ user }),
setToken: (token) => {
localStorage.setItem('token', token);
set({ token, isLogin: true });
},
logout: () => {
localStorage.removeItem('token');
set({ user: null, token: null, isLogin: false });
},
}));
使用场景:管理全局状态,如用户信息、登录状态等。
优化策略:
- 按需创建 store,避免全局单一 store 的臃肿
- 合理使用 selectors 优化组件渲染
- 利用 persist 中间件实现状态持久化
常见问题:
- Q: Zustand 相比 Redux 的优势是什么? A: 代码更简洁,无需 action、reducer 等繁琐概念,基于 Hooks 实现,学习成本低。
- Q: 如何在非 React 环境中访问 Zustand store? A: 使用 store.getState() 方法获取当前状态。
Tailwind CSS 和 Shadcn UI
技术点概述:Tailwind CSS 是一个实用优先的 CSS 框架,Shadcn UI 是基于 Tailwind CSS 的组件库,提供了丰富的 UI 组件。
核心实现:
// Tailwind CSS 配置
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
react(),
tailwindcss()
],
});
// Shadcn UI 组件使用
import { Button } from '@/components/ui/button';
function Example() {
return (
<Button variant="default">
Click me
</Button>
);
}
使用场景:快速构建响应式 UI,提高开发效率。
优化策略:
- 按需引入组件,减少打包体积
- 合理使用 Tailwind 工具类,避免自定义 CSS
- 利用 Shadcn UI 的主题系统实现统一的设计风格
常见问题:
- Q: Tailwind CSS 的优势是什么? A: 实用优先,无需编写传统 CSS,减少样式冲突,响应式设计简单。
- Q: 如何自定义 Shadcn UI 组件? A: 可以直接修改组件源码,或通过覆盖 CSS 变量实现自定义。
MockJS
技术点概述:MockJS 是一个用于生成模拟数据的库,用于前后端解耦开发,允许前端在后端 API 未完成时独立开发和测试。
核心实现:
// mock 配置
import Mock from 'mockjs';
// 模拟用户数据
Mock.mock('/api/users', 'get', {
'code': 200,
'data|10-20': [{
'id|+1': 1,
'name': '@name',
'avatar': '@image(100x100, @color, @name)',
'createdAt': '@datetime'
}]
});
// 模拟登录接口
Mock.mock('/api/login', 'post', (options) => {
const { name, password } = JSON.parse(options.body);
if (name === 'admin' && password === '123456') {
return {
code: 200,
data: {
token: 'mock-token-123456',
user: {
id: 1,
name: 'admin',
avatar: '@image(100x100, @color, admin)'
}
}
};
} else {
return {
code: 401,
message: '用户名或密码错误'
};
}
});
// 模拟文章列表
Mock.mock('/api/posts', 'get', {
'code': 200,
'data|10-20': [{
'id|+1': 1,
'title': '@title(5, 10)',
'content': '@paragraph(3, 5)',
'userId': '@integer(1, 10)',
'createdAt': '@datetime'
}]
});
// 前端 API 服务
import axios from 'axios';
const api = axios.create({
baseURL: '/api',
timeout: 10000
});
export const userApi = {
getUsers: () => api.get('/users'),
login: (data) => api.post('/login', data)
};
export const postApi = {
getPosts: () => api.get('/posts')
};
使用场景:
- 后端 API 未完成时,前端可以独立开发和测试
- 模拟各种边界情况和错误状态
- 减少对后端服务的依赖,提高开发效率
优化策略:
- 按模块组织 mock 数据,提高可维护性
- 模拟真实的 API 响应结构,包括错误处理
- 使用环境变量控制 mock 的启用和禁用
常见问题:
- Q: MockJS 如何与真实 API 切换? A: 使用环境变量或配置文件控制,开发环境使用 mock,生产环境使用真实 API。
- Q: 如何模拟复杂的 API 场景? A: 使用 MockJS 的模板语法和函数功能,模拟各种复杂的数据结构和逻辑。
后端技术栈面试回答
NestJS
技术点概述:NestJS 是一个基于 TypeScript 的企业级后端框架,提供了模块化架构和依赖注入系统。
核心实现:
// 模块示例
import { Module } from '@nestjs/common';
import { PostsController } from './posts.controller';
import { PostsService } from './posts.service';
import { PrismaModule } from '../prisma/prisma.module';
@Module({
imports: [PrismaModule],
controllers: [PostsController],
providers: [PostsService],
})
export class PostsModule {}
// 控制器示例
import { Controller, Get, Query } from '@nestjs/common';
import { PostsService } from './posts.service';
import { PostQueryDto } from './dto/post-query.dto';
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) {}
@Get()
findAll(@Query() query: PostQueryDto) {
return this.postsService.findAll(query);
}
}
使用场景:构建后端 API 服务,处理业务逻辑。
优化策略:
- 合理使用模块划分,提高代码组织性
- 利用依赖注入系统实现解耦
- 使用中间件和拦截器处理横切关注点
常见问题:
- Q: NestJS 相比 Express 的优势是什么? A: 提供了模块化架构、依赖注入、TypeScript 支持等企业级特性,代码结构更清晰。
- Q: 如何实现 NestJS 中的错误处理? A: 使用异常过滤器捕获和处理错误,或直接抛出内置的异常类。
Prisma
技术点概述:Prisma 是一个现代的 ORM 工具,用于数据库访问和管理。
核心实现:
// schema.prisma 文件
model User {
id Int @id @default(autoincrement())
name String @unique
password String
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
userId Int
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Prisma 服务
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
}
使用场景:数据库操作,包括查询、插入、更新和删除。
优化策略:
- 合理使用索引提高查询性能
- 使用事务确保数据一致性
- 避免 N+1 查询问题
常见问题:
- Q: Prisma 相比传统 ORM 的优势是什么? A: 类型安全,自动生成类型定义,查询语法更简洁,支持 migrations。
- Q: 如何处理 Prisma 中的关系查询? A: 使用 include 或 select 方法进行关联查询。
PostgreSQL
技术点概述:PostgreSQL 是一个功能强大的开源关系型数据库,支持复杂查询和事务。
核心实现:
-- 创建用户表
CREATE TABLE users (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
-- 创建文章表
CREATE TABLE posts (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT,
"userId" BigInt NOT NULL,
CONSTRAINT fk_posts_user FOREIGN KEY ("userId") REFERENCES users(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
使用场景:存储和管理应用数据,如用户信息、文章内容等。
优化策略:
- 合理设计数据库 schema,避免冗余
- 使用索引提高查询性能
- 定期进行数据库维护和优化
常见问题:
- Q: PostgreSQL 相比 MySQL 的优势是什么? A: 支持更复杂的数据类型和查询,事务支持更完善,开源且功能强大。
- Q: 如何优化 PostgreSQL 查询性能? A: 使用合适的索引,避免全表扫描,优化查询语句。
JWT 身份认证
技术点概述:JWT(JSON Web Token)是一种用于身份验证的令牌,支持无状态认证。
核心实现:
// JWT 策略
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private prisma: PrismaService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: any) {
const user = await this.prisma.user.findUnique({
where: { id: payload.sub },
});
return user;
}
}
// 登录服务
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from '../prisma/prisma.service';
import * as bcrypt from 'bcrypt';
@Injectable()
export class AuthService {
constructor(
private prisma: PrismaService,
private jwtService: JwtService,
) {}
async login(name: string, password: string) {
const user = await this.prisma.user.findUnique({ where: { name } });
if (!user) {
throw new Error('用户不存在');
}
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
throw new Error('密码错误');
}
const payload = { sub: user.id, name: user.name };
const accessToken = this.jwtService.sign(payload, {
expiresIn: '15m',
});
const refreshToken = this.jwtService.sign(payload, {
expiresIn: '7d',
});
return { accessToken, refreshToken };
}
}
使用场景:用户身份验证和授权。
优化策略:
- 使用双 token 机制(access_token 和 refresh_token)提高安全性
- 设置合理的 token 过期时间
- 实现 token 刷新机制
常见问题:
- Q: JWT 的优势是什么? A: 无状态,便于水平扩展,支持跨域认证。
- Q: 如何处理 JWT 过期? A: 使用 refresh_token 机制,当 access_token 过期时,使用 refresh_token 获取新的 access_token。
AI 相关技术面试回答
LangChain
技术点概述:LangChain 是一个用于开发基于语言模型应用的框架,提供了链式调用语言模型的能力。
核心实现:
// AI 服务
import { Injectable } from '@nestjs/common';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';
@Injectable()
export class AiService {
private chatModel: ChatOpenAI;
constructor() {
this.chatModel = new ChatOpenAI({
modelName: 'gpt-3.5-turbo',
temperature: 0.7,
});
}
async chat(messages: { role: string; content: string }[]) {
const formattedMessages = messages.map(msg =>
msg.role === 'user'
? new HumanMessage(msg.content)
: new AIMessage(msg.content)
);
const response = await this.chatModel.invoke(formattedMessages);
return response.content;
}
async streamChat(messages: { role: string; content: string }[]) {
const formattedMessages = messages.map(msg =>
msg.role === 'user'
? new HumanMessage(msg.content)
: new AIMessage(msg.content)
);
const stream = await this.chatModel.stream(formattedMessages);
return stream;
}
}
使用场景:实现智能聊天功能,处理用户的自然语言请求。
优化策略:
- 合理设置模型参数,如温度、最大 tokens 等
- 使用流式输出提高用户体验
- 实现上下文管理,保持对话连贯性
常见问题:
- Q: LangChain 的核心概念是什么? A: 链(Chains)、代理(Agents)、记忆(Memory)等,用于构建复杂的语言模型应用。
- Q: 如何优化 LangChain 应用的性能? A: 使用流式输出,合理设置模型参数,实现缓存机制。
Embedding 语义搜索
技术点概述:Embedding 是将文本转换为向量表示的技术,用于实现语义搜索。
核心实现:
// 语义搜索服务
import { Injectable } from '@nestjs/common';
import { OpenAIEmbeddings } from '@langchain/openai';
import * as fs from 'fs/promises';
import * as path from 'path';
@Injectable()
export class SearchService {
private embeddings: OpenAIEmbeddings;
private postsWithEmbeddings: any[];
constructor() {
this.embeddings = new OpenAIEmbeddings();
this.loadEmbeddings();
}
private async loadEmbeddings() {
const filePath = path.join(process.cwd(), 'src', 'data', 'posts-embedding.json');
const data = await fs.readFile(filePath, 'utf8');
this.postsWithEmbeddings = JSON.parse(data);
}
async search(query: string, topK: number = 5) {
const queryEmbedding = await this.embeddings.embedQuery(query);
const results = this.postsWithEmbeddings.map(post => {
const similarity = this.cosineSimilarity(queryEmbedding, post.embedding);
return { ...post, similarity };
}).sort((a, b) => b.similarity - a.similarity).slice(0, topK);
return results;
}
private cosineSimilarity(vec1: number[], vec2: number[]): number {
const dotProduct = vec1.reduce((sum, val, i) => sum + val * vec2[i], 0);
const norm1 = Math.sqrt(vec1.reduce((sum, val) => sum + val * val, 0));
const norm2 = Math.sqrt(vec2.reduce((sum, val) => sum + val * val, 0));
return dotProduct / (norm1 * norm2);
}
}
使用场景:实现基于语义的搜索功能,提高搜索准确性。
优化策略:
- 预计算和存储文本向量,减少实时计算开销
- 使用高效的向量相似度计算算法
- 考虑使用向量数据库提高搜索性能
常见问题:
- Q: 语义搜索相比传统关键词搜索的优势是什么? A: 语义搜索理解文本的含义,而不仅仅是关键词匹配,提高搜索准确性。
- Q: 如何处理大规模文本的 Embedding? A: 分批处理,使用向量数据库存储和索引向量。
Chatbot 流式输出
技术点概述:Chatbot 流式输出是指 AI 模型生成回复时,逐字或逐句输出,而不是等待整个回复生成完成。
核心实现:
// 前端 Chatbot Hook
import { useChat } from '@ai-sdk/react';
export function useChatBot() {
const {
messages,
input,
handleInputChange,
handleSubmit,
isLoading,
} = useChat({
api: '/api/chat',
stream: true,
});
return {
messages,
input,
handleInputChange,
handleSubmit,
isLoading,
};
}
// 后端控制器
import { Controller, Post, Body, Res } from '@nestjs/common';
import { AiService } from './ai.service';
import { Response } from 'express';
@Controller('ai')
export class AiController {
constructor(private readonly aiService: AiService) {}
@Post('chat')
async chat(@Body() body: { messages: { role: string; content: string }[] }, @Res() res: Response) {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const stream = await this.aiService.streamChat(body.messages);
for await (const chunk of stream) {
res.write(`data: ${JSON.stringify({ content: chunk.content })}\n\n`);
}
res.write(`data: ${JSON.stringify({ done: true })}\n\n`);
res.end();
}
}
使用场景:实现智能聊天功能,提高用户体验。
优化策略:
- 使用 SSE(Server-Sent Events)实现流式输出
- 合理设置缓冲区大小,平衡实时性和性能
- 实现错误处理和重试机制
常见问题:
- Q: 流式输出相比传统一次性输出的优势是什么? A: 响应更快,用户体验更好,减少等待时间。
- Q: 如何实现流式输出? A: 使用 SSE 或 WebSocket 技术,后端逐块发送数据,前端逐块接收和渲染。
- Q: WebSocket 和 SSE 的相同和不同点是什么? A: 相同点:都支持服务器向客户端推送数据,实现实时通信。不同点:WebSocket 是双向通信,SSE 是单向通信;WebSocket 建立的是持久连接,SSE 是基于 HTTP 的长连接;WebSocket 支持二进制数据,SSE 只支持文本数据;WebSocket 兼容性更好,SSE 在某些旧浏览器中可能不支持。
- Q: 怎么实现流式输出?
A: 后端使用 SSE 或 WebSocket 技术,逐块发送数据;前端使用 EventSource 或 WebSocket API 接收数据,实时渲染。具体实现:后端设置响应头为
text/event-stream,使用res.write()逐块发送数据;前端使用new EventSource()监听数据,或使用 WebSocket 连接接收数据。 - Q: 防抖和节流的区别并且怎么实现防抖和节流?
A: 区别:防抖是在事件触发后等待一段时间再执行,如果在等待期间再次触发,则重新计时;节流是在一定时间内只执行一次,无论事件触发多少次。
实现:
- 防抖:
function debounce(func, delay) { let timer; return function() { const context = this; const args = arguments; clearTimeout(timer); timer = setTimeout(() => { func.apply(context, args); }, delay); }; }- 节流:
function throttle(func, delay) { let lastTime = 0; return function() { const context = this; const args = arguments; const now = Date.now(); if (now - lastTime >= delay) { func.apply(context, args); lastTime = now; } }; }
AI 生成头像
技术点概述:AI 生成头像是利用 AI 模型根据用户输入的描述或特征生成个性化的头像图片。
核心实现:
// AI 头像生成服务
import { Injectable } from '@nestjs/common';
import { OpenAI } from 'openai';
@Injectable()
export class AvatarService {
private openai: OpenAI;
constructor() {
this.openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
}
async generateAvatar(prompt: string): Promise<string> {
const response = await this.openai.images.generate({
prompt: `Generate a professional avatar for a user based on: ${prompt}. The avatar should be clean, modern, and suitable for a professional profile.`,
n: 1,
size: '512x512',
});
return response.data[0].url || '';
}
}
// 前端组件
import React, { useState } from 'react';
import axios from 'axios';
export default function AvatarGenerator() {
const [prompt, setPrompt] = useState('');
const [avatarUrl, setAvatarUrl] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleGenerate = async () => {
setIsLoading(true);
try {
const response = await axios.post('/api/avatar/generate', { prompt });
setAvatarUrl(response.data.url);
} catch (error) {
console.error('Error generating avatar:', error);
} finally {
setIsLoading(false);
}
};
return (
<div className="max-w-md mx-auto">
<h2 className="text-2xl font-bold mb-4">AI 头像生成</h2>
<input
type="text"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
placeholder="描述你想要的头像风格..."
className="w-full p-2 border rounded mb-4"
/>
<button
onClick={handleGenerate}
disabled={isLoading}
className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600"
>
{isLoading ? '生成中...' : '生成头像'}
</button>
{avatarUrl && (
<div className="mt-4">
<img src={avatarUrl} alt="Generated Avatar" className="w-full h-auto rounded" />
</div>
)}
</div>
);
}
使用场景:用户注册或个人资料设置时,生成个性化头像。
优化策略:
- 缓存生成的头像,避免重复生成
- 提供预设的头像风格选项,简化用户输入
- 实现头像生成进度的实时反馈
常见问题:
- Q: 如何提高 AI 生成头像的质量? A: 提供更详细的描述,指定风格、颜色和特征等细节。
- Q: 如何处理生成失败的情况? A: 实现错误处理和重试机制,提供默认头像作为 fallback。
Commit Message 生成
技术点概述:Commit Message 生成是利用 AI 分析代码变更,自动生成符合规范的 commit 消息。
核心实现:
// Commit Message 生成服务
import { Injectable } from '@nestjs/common';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';
@Injectable()
export class CommitMessageService {
private chatModel: ChatOpenAI;
constructor() {
this.chatModel = new ChatOpenAI({
modelName: 'gpt-3.5-turbo',
temperature: 0.3,
});
}
async generateCommitMessage(diff: string): Promise<string> {
const messages = [
new HumanMessage(`Generate a concise and descriptive commit message for the following code changes:\n\n${diff}\n\nFollow the conventional commit format: <type>(<scope>): <description>\n\nTypes: feat, fix, docs, style, refactor, test, chore`),
];
const response = await this.chatModel.invoke(messages);
return response.content;
}
}
// 前端组件
import React, { useState } from 'react';
import axios from 'axios';
export default function CommitMessageGenerator() {
const [diff, setDiff] = useState('');
const [commitMessage, setCommitMessage] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleGenerate = async () => {
setIsLoading(true);
try {
const response = await axios.post('/api/commit/generate', { diff });
setCommitMessage(response.data.message);
} catch (error) {
console.error('Error generating commit message:', error);
} finally {
setIsLoading(false);
}
};
return (
<div className="max-w-2xl mx-auto">
<h2 className="text-2xl font-bold mb-4">Commit Message 生成</h2>
<textarea
value={diff}
onChange={(e) => setDiff(e.target.value)}
placeholder="粘贴你的代码变更 diff..."
className="w-full p-2 border rounded mb-4 h-40"
/>
<button
onClick={handleGenerate}
disabled={isLoading}
className="w-full bg-blue-500 text-white p-2 rounded hover:bg-blue-600"
>
{isLoading ? '生成中...' : '生成 Commit Message'}
</button>
{commitMessage && (
<div className="mt-4">
<h3 className="font-bold mb-2">生成的 Commit Message:</h3>
<div className="p-2 border rounded bg-gray-50">
{commitMessage}
</div>
</div>
)}
</div>
);
}
使用场景:开发者提交代码时,自动生成符合规范的 commit 消息。
优化策略:
- 分析代码变更的具体内容,生成更精准的 commit 消息
- 支持不同的 commit 消息格式,如 conventional commits
- 提供手动编辑的选项,允许开发者调整生成的消息
常见问题:
- Q: 如何确保生成的 commit 消息符合项目规范? A: 在提示中明确指定 commit 消息的格式和类型要求。
- Q: 如何处理大型代码变更的 commit 消息生成? A: 对 diff 进行摘要处理,提取关键变更信息,避免 token 超限。
性能优化面试回答
图片懒加载
技术点概述:图片懒加载是指仅当图片进入视口时才加载,减少初始加载时间和带宽消耗。
核心实现:
// 图片懒加载组件
import React, { useEffect, useRef } from 'react';
interface LazyImageProps {
src: string;
alt: string;
className?: string;
}
export default function LazyImage({ src, alt, className }: LazyImageProps) {
const imgRef = useRef<HTMLImageElement>(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target as HTMLImageElement;
img.src = img.dataset.src || '';
observer.unobserve(img);
}
});
});
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => {
if (imgRef.current) {
observer.unobserve(imgRef.current);
}
};
}, []);
return (
<img
ref={imgRef}
data-src={src}
src="/placeholder.jpg"
alt={alt}
className={className}
/>
);
}
使用场景:文章列表、图片画廊等包含大量图片的页面。
优化策略:
- 使用 IntersectionObserver API 实现高效的懒加载
- 提供合适的占位符,提升用户体验
- 合理设置图片尺寸和格式,减少加载时间
常见问题:
- Q: 如何实现图片懒加载? A: 使用 IntersectionObserver API 监听图片是否进入视口,当进入视口时设置真实的 src 属性。
- Q: 图片懒加载的优势是什么? A: 减少初始加载时间,节省带宽,提高页面性能。
无限滚动
技术点概述:无限滚动是指当用户滚动到页面底部时,自动加载更多数据,提供流畅的浏览体验。
核心实现:
// 无限滚动组件
import React, { useEffect, useRef } from 'react';
interface InfiniteScrollProps {
children: React.ReactNode;
hasMore: boolean;
isLoading: boolean;
onLoadMore: () => void;
}
export default function InfiniteScroll({ children, hasMore, isLoading, onLoadMore }: InfiniteScrollProps) {
const sentinelRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
const entry = entries[0];
if (entry.isIntersecting && hasMore && !isLoading) {
onLoadMore();
}
}, {
threshold: 0.0
});
if (sentinelRef.current) {
observer.observe(sentinelRef.current);
}
return () => {
if (sentinelRef.current) {
observer.unobserve(sentinelRef.current);
}
};
}, [hasMore, isLoading, onLoadMore]);
return (
<div>
{children}
{hasMore && (
<div ref={sentinelRef} className="h-10 w-full" />
)}
{isLoading && (
<div className="text-center py-4">加载中...</div>
)}
</div>
);
}
使用场景:文章列表、商品列表等需要加载大量数据的页面。
优化策略:
- 使用 IntersectionObserver API 实现高效的滚动检测
- 合理设置加载阈值,提前触发加载
- 实现防抖和节流,避免频繁触发加载
常见问题:
- Q: 如何实现无限滚动? A: 使用 IntersectionObserver API 监听页面底部的哨兵元素,当元素进入视口时加载更多数据。
- Q: 无限滚动的优势是什么? A: 提供流畅的浏览体验,避免分页导航,减少用户操作。
组件缓存
技术点概述:组件缓存是指在路由切换时保持组件的状态和 DOM 结构,避免重复渲染和数据加载。
核心实现:
// 使用 react-activation 实现组件缓存
import { KeepAlive, AliveScope } from 'react-activation';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import MainLayout from './layouts/MainLayout';
import Home from './pages/Home';
import Login from './pages/Login';
const router = createBrowserRouter([
{
path: '/',
element: (
<AliveScope>
<MainLayout />
</AliveScope>
),
children: [
{
path: '',
element: (
<KeepAlive name="home">
<Home />
</KeepAlive>
),
},
{
path: 'login',
element: <Login />,
},
],
},
]);
function App() {
return <RouterProvider router={router} />;
}
export default App;
使用场景:首页、仪表盘等用户频繁访问的页面。
优化策略:
- 合理使用 KeepAlive 组件,避免过度缓存
- 实现缓存清理机制,避免内存泄漏
- 结合路由懒加载,进一步提高性能
常见问题:
- Q: 如何实现组件缓存? A: 使用 react-activation 库的 KeepAlive 组件,或手动实现基于 Map 的组件缓存。
- Q: 组件缓存的优势是什么? A: 保持组件状态,避免重复渲染和数据加载,提高用户体验。
项目亮点总结
-
技术栈现代化:使用 React、TypeScript、NestJS、MockJS 等现代技术栈,确保代码质量和可维护性。
-
架构设计合理:前后端分离,模块化架构,依赖注入,提高代码组织性和可扩展性。
-
AI 能力集成:整合 LangChain、Embedding、Chatbot、AI 生成头像、Commit Message 生成等 AI 技术,实现智能交互功能。
-
性能优化到位:图片懒加载、无限滚动、组件缓存等优化措施,提高用户体验。
-
安全措施完善:JWT 双 token 机制、bcrypt 密码加密、错误处理等安全措施,保障系统安全。
面试小贴士
-
准备充分:熟悉项目的技术栈、架构和核心功能,准备好相关的代码示例。
-
结构清晰:回答问题时按照技术点概述、核心实现、使用场景、优化策略、常见问题的框架组织语言。
-
突出亮点:强调项目中的技术亮点和解决的问题,展示自己的技术能力。
-
代码示例:准备一些关键的代码片段,展示自己的编码风格和技术水平。
-
自信表达:保持自信,清晰表达自己的思路和见解,展示良好的沟通能力。
通过以上内容,你可以在面试时自信地介绍文智 AI 全栈交互系统的技术实现和项目亮点,展示自己的全栈开发能力和技术深度。祝你面试成功!