React-nodejs 练习 个人博客
1.主要功能模块:
- 文章管理:CRUD操作
- 用户系统:注册、登录、权限控制
- 评论系统:文章评论功能
-
2.技术栈:
- 前端:React + Ant Design + React Router
- 后端:Express + MongoDB
- 通信:RESTful API + Axios
这个项目采用了典型的MVC架构,前后端完全分离,通过API进行通信。前端负责用户界面和交互,后端负责数据处理和业务逻辑。
3.文件结构
1. blog-project (前端)
---
├── src/
│ ├── App.jsx # 根组件,路由配置
│ ├── App.css # 全局样式
│ ├── main.jsx # 入口文件
│ ├── pages/ # 页面组件
│ │ ├── Home/ # 首页目录
│ │ │ └── index.jsx # 首页组件
│ │ ├── Articles/ # 文章相关页面目录
│ │ │ ├── index.jsx # 文章列表页
│ │ │ └── detail.jsx # 文章详情页
│ │ └── Login/ # 登录页面目录
│ │ └── index.jsx # 登录组件
│ ├── components/ # 可复用组件
│ │ └── Layout/ # 布局组件
│ │ └── index.jsx # 布局组件实现
│ ├── api/ # API接口
│ │ ├── article.js # 文章相关API
│ │ ├── user.js # 用户相关API
│ │ └── request.js # axios请求封装
│ ├── utils/ # 工具函数
│ └── assets/ # 静态资源
---
└── package.json # 项目依赖配置
2. blog-server (后端)
---
├── src/
│ ├── index.js # 服务器入口文件
│ ├── routes/ # 路由定义
│ │ ├── postRoutes.js # 文章路由
│ │ └── userRoutes.js # 用户路由
│ ├── controllers/ # 控制器
│ │ ├── postController.js # 文章控制器
│ │ └── userController.js # 用户控制器
│ ├── models/ # 数据模型
│ │ ├── post.js # 文章模型
│ │ └── user.js # 用户模型
│ ├── middleware/ # 中间件
│ │ └── auth.js # 认证中间件
│ ├── utils/ # 工具函数
│ └── config/ # 配置文件
---
└── package.json # 项目依赖配置
4. 流程原理
1. 整体架构思想
前后端分离架构
- 前端(React + Ant Design)专注于用户界面和交互
- 后端(Express + MongoDB)专注于数据处理和业务逻辑
- 通过 HTTP API 进行通信,实现解耦
MVC 架构模式
- Model(数据模型):MongoDB 的 Schema 定义
- View(视图):React 组件
- Controller(控制器):Express 的路由和控制器
整体工作流程
客户端(React) → 服务端(Express) → 数据库(MongoDB)
-
客户端发起请求
- 用户点击 React Router 的 Link 组件或触发事件
- React 组件通过 axios 发送 HTTP 请求
- 请求经过 axios 拦截器处理(添加 token 等)
-
服务端接收请求
- Express 服务器接收请求
- 通过路由(routes)匹配对应的处理函数
- 控制器(controllers)处理业务逻辑
- 与 MongoDB 数据库交互
-
数据返回流程
- MongoDB 返回查询结果
- Express 处理数据并返回响应
- axios 拦截器处理响应
- React 组件更新状态并重新渲染
2 请求流程示例
用户点击文章列表
↓
React Router 的 Link 组件触发路由变化
↓
React 组件加载,调用 axios 请求
↓
axios 拦截器添加 token
↓
请求发送到 Express 服务器
↓
Express 路由匹配到 postRoutes
↓
控制器调用 MongoDB 查询
↓
数据返回给前端
↓
React 组件更新显示
3 关键组件的工作方式
1. axios 拦截器
- 请求拦截:添加 token、处理请求参数
- 响应拦截:处理响应数据、处理错误
2. Express 路由
- 匹配 URL 和 HTTP 方法
- 调用对应的控制器函数
- 处理请求参数
3. MongoDB 操作
- 通过 Mongoose 模型定义数据结构
- 执行 CRUD 操作
- 返回查询结果
4.错误处理
前端错误:
React 组件 → axios 拦截器 → 显示错误信息
后端错误:
MongoDB 错误 → Express 控制器 → 返回错误响应 → axios 拦截器 → React 组件显示错误
5.具体代码:
前端部分
App.jsx
import { BrowserRouter as Router, Routes, Route, Link, useNavigate } from 'react-router-dom';
import { Layout, Menu, Button } from 'antd';
import './App.css';
import Login from './pages/Login';
import Home from './pages/Home';
import Articles from './pages/Articles';
import { useState } from 'react';// 后续会创建这些组件
const About = () => <div>关于我</div>;const { Header, Content, Footer } = Layout;function App() {const [isLoggedIn, setIsLoggedIn] = useState(!!localStorage.getItem('token'));return (<Router><Layout className="layout"><Header className="header"><div className="logo">我的个人博客</div><Menutheme="dark"mode="horizontal"defaultSelectedKeys={['/']}items={[{key: '/',label: <Link to="/">首页</Link>,},{key: '/articles',label: <Link to="/articles">文章列表</Link>,},{key: '/about',label: <Link to="/about">关于我</Link>,},]}/>{isLoggedIn ? (<Button onClick={() => {localStorage.removeItem('token');setIsLoggedIn(false);}}>退出</Button>) : (<Link to="/login"><Button type="primary">登录</Button></Link>)}</Header><Content><Routes><Route path="/login" element={<Login />} /><Route path="/" element={<Home />} /><Route path="/articles/*" element={<Articles />} /><Route path="/about" element={<About />} /></Routes></Content><Footer style={{ textAlign: 'center' }}>个人博客 ©2024 Created by shandian</Footer></Layout></Router>);
}export default App;
main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import 'antd/dist/reset.css'
import './index.css'ReactDOM.createRoot(document.getElementById('root')).render(<React.StrictMode><App /></React.StrictMode>,
)
article.js
import request from './request';// 获取文章列表
export const getArticles = () => {return request({url: '/posts',method: 'get'});
};// 获取文章详情
export const getArticleById = (id) => {console.log('Calling API for article:', id); // 添加日志return request({url: `/posts/${id}`,method: 'get'});
};// 创建文章
export const createArticle = (data) => {return request({url: '/posts',method: 'post',data});
};// 更新文章
export const updateArticle = (id, data) => {return request({url: `/posts/${id}`,method: 'put',data});
};// 删除文章
export const deleteArticle = (id) => {return request({url: `/posts/${id}`,method: 'delete'});
};// 获取标签列表
export const getTags = () => {return request({url: '/posts/tags',method: 'get'});
};
request.js
import axios from 'axios';
import { message } from 'antd';// 创建axios实例
const request = axios.create({baseURL: 'http://localhost:3000/api',timeout: 5000,headers: {'Content-Type': 'application/json',}
});// 请求拦截器
request.interceptors.request.use(config => {const token = localStorage.getItem('token');if (token) {config.headers.Authorization = `Bearer ${token}`;}// 添加请求日志console.log('Request:', {url: config.url,method: config.method,data: config.data,params: config.params});return config;},error => {console.error('Request Error:', error);return Promise.reject(error);}
);// 响应拦截器
request.interceptors.response.use(response => {// 添加响应日志console.log('Response:', {url: response.config.url,status: response.status,data: response.data});return response.data;},error => {console.error('Response Error:', {url: error.config?.url,status: error.response?.status,data: error.response?.data,message: error.message});if (error.response) {switch (error.response.status) {case 401:message.error('请先登录');localStorage.removeItem('token');window.location.href = '/login';break;case 403:message.error('没有权限');break;case 404:message.error('请求的资源不存在');break;case 500:message.error('服务器错误:' + (error.response.data?.message || '未知错误'));break;default:message.error(`请求错误 (${error.response.status}): ${error.response.data?.message || '未知错误'}`);}} else if (error.request) {message.error('无法连接到服务器,请检查网络连接');} else {message.error('请求配置错误:' + error.message);}return Promise.reject(error);}
);export default request;
user.js
import request from './request';// 用户登录
export const login = (data) => {return request({url: '/users/login',method: 'post',data});
};// 用户注册
export const register = (data) => {return request({url: '/users/register',method: 'post',data});
};// 获取用户信息
export const getUserInfo = () => {return request({url: '/users/info',method: 'get'});
};// 更新用户信息
export const updateUserInfo = (data) => {return request({url: '/user/update',method: 'put',data});
};
index.jsx
import { Outlet } from 'react-router-dom';
import Navigation from '../Navigation';function Layout() {return (<div><Navigation /><main><Outlet /></main></div>);
}export default Layout;
CreateArticle.jsx
import React, { useState } from 'react';
import { Card, Form, Input, Button, Select, message } from 'antd';
import { useNavigate } from 'react-router-dom';
import { createArticle } from '../../api/article';const { TextArea } = Input;const CreateArticle = () => {const [form] = Form.useForm();const [loading, setLoading] = useState(false);const navigate = useNavigate();const onFinish = async (values) => {try {setLoading(true);// 处理标签,将字符串转换为数组const tags = values.tags.split(',').map(tag => tag.trim());await createArticle({...values,tags});message.success('文章创建成功!');navigate('/articles'); // 创建成功后返回文章列表} catch (error) {message.error('创建文章失败:' + (error.message || '未知错误'));} finally {setLoading(false);}};return (<div className="create-article-container"><Card title="创建新文章" className="create-article-card"><Formform={form}layout="vertical"onFinish={onFinish}><Form.Itemname="title"label="文章标题"rules={[{ required: true, message: '请输入文章标题' }]}><Input placeholder="请输入文章标题" /></Form.Item><Form.Itemname="content"label="文章内容"rules={[{ required: true, message: '请输入文章内容' }]}><TextArea rows={15} placeholder="请输入文章内容"style={{ resize: 'none' }}/></Form.Item><Form.Itemname="tags"label="文章标签"help="多个标签请用逗号分隔,如:React,JavaScript,前端"><Input placeholder="请输入标签,用逗号分隔" /></Form.Item><Form.Item><Button type="primary" htmlType="submit" loading={loading} block>发布文章</Button></Form.Item></Form></Card></div>);
};export default CreateArticle;
home.jsx
import React, { useState, useEffect } from 'react';
import { Card, List, Space, Tag, Spin } from 'antd';
import { ClockCircleOutlined, UserOutlined, EyeOutlined } from '@ant-design/icons';
import { Link } from 'react-router-dom';
import { getArticles, getTags } from '../../api/article';
import { getUserInfo } from '../../api/user';
import './index.css';const Home = () => {const [articles, setArticles] = useState([]);const [loading, setLoading] = useState(false);const [userInfo, setUserInfo] = useState(null);const [popularTags, setPopularTags] = useState([]);// 获取最新文章const fetchLatestArticles = async () => {try {setLoading(true);const res = await getArticles({page: 1,pageSize: 5,sort: 'createTime',order: 'desc'});setArticles(res.list);} catch (error) {console.error('获取最新文章失败:', error);} finally {setLoading(false);}};// 获取热门标签const fetchPopularTags = async () => {try {const res = await getTags();setPopularTags(res.slice(0, 10)); // 只显示前10个标签} catch (error) {console.error('获取热门标签失败:', error);}};// 获取用户信息const fetchUserInfo = async () => {try {const res = await getUserInfo();setUserInfo(res);} catch (error) {console.error('获取用户信息失败:', error);}};useEffect(() => {fetchLatestArticles();fetchPopularTags();fetchUserInfo();}, []);return (<div className="home-container"><div className="article-list"><Spin spinning={loading}><ListitemLayout="vertical"size="large"dataSource={articles}renderItem={(item) => (<List.Itemkey={item.id}actions={[<Space><ClockCircleOutlined /> {item.createTime}</Space>,<Space><UserOutlined /> {item.author}</Space>,<Space><EyeOutlined /> {item.views} 次浏览</Space>]}><Card hoverable className="article-card"><Link to={`/article/${item.id}`}><List.Item.Metatitle={<h2>{item.title}</h2>}description={<div><p className="article-summary">{item.summary}</p><Space className="article-tags">{item.tags.map(tag => (<Tag key={tag} color="blue">{tag}</Tag>))}</Space></div>}/></Link></Card></List.Item>)}/></Spin></div><div className="sidebar"><Card title="关于博主" className="about-card">{userInfo ? (<><p>{userInfo.bio || '热爱编程的前端开发者'}</p><p>文章数:{userInfo.articleCount || 0}</p><p>访问量:{userInfo.totalViews || 0}</p></>) : (<p>加载中...</p>)}</Card><Card title="热门标签" className="tags-card"><Space wrap>{popularTags.map(tag => (<Tag key={tag} color="blue"onClick={() => window.location.href = `/articles?tag=${tag}`}style={{ cursor: 'pointer' }}>{tag}</Tag>))}</Space></Card></div></div>);
};export default Home;
login.jsx
import React, { useState } from 'react';
import { Form, Input, Button, Card, message } from 'antd';
import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons';
import { useNavigate } from 'react-router-dom';
import { login, register } from '../../api/user';
import './index.css';const Login = () => {const [isLogin, setIsLogin] = useState(true);const [loading, setLoading] = useState(false);const navigate = useNavigate();const [form] = Form.useForm();const onFinish = async (values) => {try {setLoading(true);if (isLogin) {// 登录请求const res = await login({email: values.email,password: values.password});// 保存tokenlocalStorage.setItem('token', res.token);message.success('登录成功!');navigate('/'); // 登录成功后跳转到首页} else {// 注册请求if (values.password !== values.confirmPassword) {message.error('两次输入的密码不一致!');return;}await register({username: values.username,email: values.email,password: values.password});message.success('注册成功!');setIsLogin(true); // 注册成功后切换到登录界面form.resetFields();}} catch (error) {// 错误已经在request.js中统一处理console.error('操作失败:', error);} finally {setLoading(false);}};const switchMode = () => {setIsLogin(!isLogin);form.resetFields();};return (<div className="login-container"><Card title={isLogin ? "登录" : "注册"} className="login-card"><Formform={form}name="normal_login"className="login-form"onFinish={onFinish}>{!isLogin && (<Form.Itemname="username"rules={[{ required: true, message: '请输入用户名!' },{ min: 3, message: '用户名至少3个字符!' }]}><Input prefix={<UserOutlined />} placeholder="用户名" /></Form.Item>)}<Form.Itemname="email"rules={[{ required: true, message: '请输入邮箱!' },{ type: 'email', message: '请输入有效的邮箱地址!' }]}><Input prefix={<MailOutlined />} placeholder="邮箱" /></Form.Item><Form.Itemname="password"rules={[{ required: true, message: '请输入密码!' },{ min: 6, message: '密码至少6个字符!' }]}><Inputprefix={<LockOutlined />}type="password"placeholder="密码"/></Form.Item>{!isLogin && (<Form.Itemname="confirmPassword"dependencies={['password']}rules={[{ required: true, message: '请确认密码!' },({ getFieldValue }) => ({validator(_, value) {if (!value || getFieldValue('password') === value) {return Promise.resolve();}return Promise.reject(new Error('两次输入的密码不一致!'));},}),]}><Inputprefix={<LockOutlined />}type="password"placeholder="确认密码"/></Form.Item>)}<Form.Item><Button type="primary" htmlType="submit" className="login-form-button"loading={loading}>{isLogin ? '登录' : '注册'}</Button><Button type="link" onClick={switchMode}>{isLogin ? '还没有账号?去注册' : '已有账号?去登录'}</Button></Form.Item></Form></Card></div>);
};export default Login;
后端部分
postController.js
const Post = require('../models/post');// 创建新文章
exports.createPost = async (req, res) => {try {const { title, content, tags } = req.body;// 添加调试日志console.log('userId:', req.userId);console.log('request body:', req.body);const post = new Post({title,content,tags,author: req.userId // 确保这里使用的是 req.userId});const savedPost = await post.save();res.status(201).json({ message: '文章创建成功', post: savedPost });} catch (err) {console.error('Error creating post:', err); // 添加错误日志res.status(400).json({ message: err.message });}
};// 获取所有文章
exports.getPosts = async (req, res) => {try {console.log('Getting posts...'); // 添加调试日志const posts = await Post.find().populate('author', 'email') // 关联作者信息.sort({ createdAt: -1 }); // 按创建时间倒序console.log('Found posts:', posts); // 添加调试日志res.json(posts);} catch (err) {console.error('Error in getPosts:', err); // 添加错误日志res.status(500).json({ message: err.message });}
};// 获取单个文章
exports.getPost = async (req, res) => {try {const post = await Post.findById(req.params.id).populate('author', 'username email');if (!post) {return res.status(404).json({ message: '文章不存在' });}// 增加阅读量post.views = (post.views || 0) + 1;await post.save();res.json(post);} catch (err) {console.error('获取文章详情失败:', err);res.status(500).json({ message: err.message });}
};// 更新文章
exports.updatePost = async (req, res) => {try {const { title, content, tags, status } = req.body;const post = await Post.findById(req.params.id);if (!post) {return res.status(404).json({ message: '文章不存在' });}// 检查是否是文章作者if (post.author.toString() !== req.userId) {return res.status(403).json({ message: '没有权限修改此文章' });}post.title = title || post.title;post.content = content || post.content;post.tags = tags || post.tags;post.status = status || post.status;await post.save();res.json({ message: '文章更新成功', post });} catch (err) {res.status(400).json({ message: err.message });}
};// 删除文章
exports.deletePost = async (req, res) => {try {const post = await Post.findById(req.params.id);if (!post) {return res.status(404).json({ message: '文章不存在' });}if (post.author.toString() !== req.userId) {return res.status(403).json({ message: '没有权限删除此文章' });}await Post.deleteOne({ _id: req.params.id });res.json({ message: '文章删除成功' });} catch (err) {res.status(500).json({ message: err.message });}
};// 获取所有标签
exports.getTags = async (req, res) => {try {const posts = await Post.find();// 获取所有文章的标签,去重const tags = [...new Set(posts.flatMap(post => post.tags))];res.json(tags);} catch (err) {console.error('Error getting tags:', err); // 添加错误日志res.status(500).json({ message: err.message });}
};
userController.js
const jwt = require('jsonwebtoken');
const User = require('../models/User');// 注册新用户
exports.register = async (req, res) => {try {const { username, email, password } = req.body;const user = new User({ username, email, password });await user.save();res.status(201).json({ message: '用户创建成功' });} catch (err) {res.status(400).json({ message: err.message });}
};// 登录
exports.login = async (req, res) => {try {const { email, password } = req.body;// 查找用户const user = await User.findOne({ email });if (!user) {return res.status(401).json({ message: '用户不存在' });}// 验证密码const isMatch = await user.comparePassword(password);if (!isMatch) {return res.status(401).json({ message: '密码错误' });}// 生成 JWT tokenconst token = jwt.sign({ userId: user._id },process.env.JWT_SECRET,{ expiresIn: '24h' });// 返回用户信息和 tokenres.json({token,user: {id: user._id,username: user.username,email: user.email,role: user.role}});} catch (err) {res.status(500).json({ message: err.message });}
};// 获取所有用户
exports.getUsers = async (req, res) => {try {const users = await User.find({}, '-password');res.json(users);} catch (err) {res.status(500).json({ message: err.message });}
};// 获取单个用户
exports.getUser = async (req, res) => {try {const user = await User.findById(req.params.id, '-password');if (!user) {return res.status(404).json({ message: '用户不存在' });}res.json(user);} catch (err) {res.status(500).json({ message: err.message });}
};// 获取用户信息
exports.getUserInfo = async (req, res) => {try {console.log('Getting user info for userId:', req.userId); // 添加调试日志const user = await User.findById(req.userId).select('-password');if (!user) {return res.status(404).json({ message: '用户不存在' });}console.log('Found user:', user); // 添加调试日志res.json(user);} catch (err) {console.error('Error in getUserInfo:', err); // 添加错误日志res.status(500).json({ message: err.message });}
};module.exports = exports;
auth.js
const jwt = require('jsonwebtoken');module.exports = (req, res, next) => {try {// 添加调试日志console.log('Auth Header:', req.headers.authorization);const authHeader = req.headers.authorization;if (!authHeader) {return res.status(401).json({ message: '未提供认证token' });}const token = authHeader.split(' ')[1];console.log('Using JWT_SECRET:', process.env.JWT_SECRET); // 添加调试日志console.log('Extracted token:', token); // 调试日志if (!token) {return res.status(401).json({ message: 'token格式错误' });}// 确保使用正确的 JWT_SECRETconst decoded = jwt.verify(token, process.env.JWT_SECRET);console.log('Decoded token:', decoded); // 调试日志req.userId = decoded.userId;next();} catch (err) {console.error('Auth error:', err); // 错误日志res.status(401).json({ message: '认证失败' });}
};
post.js
const mongoose = require('mongoose');const postSchema = new mongoose.Schema({title: {type: String,required: true,trim: true},content: {type: String,required: true},author: {type: mongoose.Schema.Types.ObjectId,ref: 'User',required: true},tags: [{type: String,trim: true}],status: {type: String,enum: ['draft', 'published'],default: 'published'},views: {type: Number,default: 0}
}, {timestamps: true // 自动添加 createdAt 和 updatedAt 字段
});const Post = mongoose.model('Post', postSchema);module.exports = Post;
user.js
const bcrypt = require('bcryptjs');const mongoose = require('mongoose');const userSchema = new mongoose.Schema({username: {type: String,required: true,unique: true,trim: true,minlength: 3},email: {type: String,required: true,unique: true,trim: true,lowercase: true},password: {type: String,required: true,minlength: 6},avatar: {type: String,default: ''},role: {type: String,enum: ['user', 'admin'],default: 'user'}
}, {timestamps: true // 自动添加 createdAt 和 updatedAt 字段
});userSchema.pre('save', async function(next) {if (this.isModified('password')) {this.password = await bcrypt.hash(this.password, 10);}next();
});userSchema.methods.comparePassword = async function(candidatePassword) {return bcrypt.compare(candidatePassword, this.password);
};const User = mongoose.model('User', userSchema);module.exports = User;
postRoutes.js
const express = require('express');
const router = express.Router();
const postController = require('../controllers/postController');
const auth = require('../middleware/auth');// 公开路由
router.get('/tags', postController.getTags);
router.get('/', postController.getPosts);
router.get('/:id', postController.getPost);// 需要认证的路由
router.post('/', auth, postController.createPost);
router.put('/:id', auth, postController.updatePost);
router.delete('/:id', auth, postController.deletePost);module.exports = router;
userRoutes.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
const auth = require('../middleware/auth');// 公开路由
router.post('/register', userController.register);
router.post('/login', userController.login);// 需要认证的路由
router.get('/info', auth, userController.getUserInfo);
router.get('/', auth, userController.getUsers);
router.get('/:id', auth, userController.getUser);module.exports = router;
处理
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();// 修改这一行,使用正确的相对路径
const userRoutes = require('./routes/userRoutes.js'); // 确保加上 .js 扩展名
const postRoutes = require('./routes/postRoutes.js'); // 添加这行// 连接MongoDB数据库
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/blog').then(() => console.log('MongoDB 连接成功!')).catch(err => console.error('MongoDB 连接失败:', err));const app = express();// 更详细的 CORS 配置
app.use(cors({origin: 'http://localhost:5173', // 您的前端地址methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],allowedHeaders: ['Content-Type', 'Authorization'],credentials: true
}));app.use(express.json());// 测试路由
app.get('/api/test', (req, res) => {res.json({ message: '后端服务器运行正常!' });
});// 在其他路由之前添加
app.get('/api/health', (req, res) => {res.json({ status: 'ok', timestamp: new Date().toISOString() });
});// 使用路由
app.use('/api/users', userRoutes);
app.use('/api/posts', postRoutes); // 添加这行// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {console.log(`服务器运行在 http://localhost:${PORT}`);
});
相关文章:
React-nodejs 练习 个人博客
1.主要功能模块: 文章管理:CRUD操作 用户系统:注册、登录、权限控制评论系统:文章评论功能 2.技术栈: 前端:React Ant Design React Router后端:Express MongoDB 通信:RESTful…...
AI三大主义 和 深度学习三大主义
在人工智能(AI)研究领域,"三大主义"通常指三种核心方法论或思想流派,它们代表了不同的技术路径和哲学观点。以下是主流的划分方式: 1. 符号主义(Symbolicism) 核心思想:智…...
Linux实现生产者消费者模型(基于阻塞队列)
目录 概念及优势 代码实现 概念及优势 生产者消费者模型是一种用于线程同步的模型,在这个模型中有两种角色,生产者生产数据,消费者消费数据。有三种关系,生产者与生产者,消费者与消费者,生产者与消费者。…...
UE5 UE4 右键/最大化-菜单-不显示/闪/黑色/黑屏--修复方法
先关闭UE5 、UE4 编辑器。 进入这个网站:https://nvidia.custhelp.com/app/answers/detail/a_id/5157 网速慢,换成这个下载:https://download.csdn.net/download/qq_21153225/90546310 下载:mpo_disable.reg 的文件 只双击“mpo…...
[ C语言 ] | 从0到1?
目录 认识计算机语言 C语言 工欲善其事必先利其器 第一个C语言代码 这一些列 [ C语言 ] ,就来分享一下 C语言 相关的知识点~ 认识计算机语言 我们说到计算机语言,语言,就是用来沟通的工具,计算机语言呢?就是我们…...
4.训练篇2-毕设篇
resnet # 1. 从 torchvision 中加载预训练的 ResNet18 模型 # pretrainedTrue 表示使用在 ImageNet 上预训练过的参数,学习效果更好 base_model_resnet18 models.resnet18(pretrainedTrue)# 2. 获取 ResNet18 模型中全连接层(fc)的输入特征…...
Ubuntu 系统中,每日健康检查
一、手动检查命令(基础项) 1. 系统资源监控 内存使用: free -h # 查看内存和交换空间使用情况 cat /proc/meminfo | grep -i "memavailable" # 查看可用内存CPU 负载: top -n 1 -b | grep "load ave…...
#CX# UVM中的virtual sequence 和 virtual sequencer 的用途
在UVM中,Virtual Sequence(虚拟序列) 的核心用途是协调多个物理Sequencer上的Sequence执行,以实现跨接口、跨组件的复杂事务同步和场景控制。 1. 核心用途 多接口协同测试 当验证场景需要多个接口(如DUT的AXI、I2C、UART接口)同时或按特定顺序发送事务时,Virtual Seque…...
vue:突然发现onok无法使用
const that this;this.$confirm({title: "修改商品提示",content: "如果当前商品存在于商品活动库,则在商品活动库的状态会下架",onOk: function () {that.submitForm();}}); 突然发现 this.$confirm无法进入onok 最终发现是主题冲突&#x…...
开发过程中需要安装的浏览器插件
Vue.js devtools https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hlzh-CN&utm_sourceext_sidebar ModHeader https://chromewebstore.google.com/detail/modheader-modify-http-hea/idgpnmonknjnojddfkpgkljpfnnfcklj?utm…...
基于javaweb的SpringBoot房屋出租系统设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…...
小程序某点餐平台全自动化实现思路
某德基全自动化实现 一、实现思路二、具体步骤1、分析接口2、破解接口3、解决自动支付4、获取订单 三、其他 以下内容仅供学习交流使用 粗略讲解一下思路,要完全实现只靠以下这些内容还是不够的。 一、实现思路 全自动化实现用的是小程序版本。我们首先要分析整个…...
红宝书第二十讲:详解JavaScript的Proxy与Reflect
红宝书第二十讲:详解JavaScript的Proxy与Reflect 资料取自《JavaScript高级程序设计(第5版)》。 查看总目录:红宝书学习大纲 一、Proxy的作用:给对象戴上“监听耳机” Proxy(代理)允许你为对象…...
Contactile三轴触觉传感器:多维力感赋能机器人抓取
在非结构化环境中,机器人对物体的精准抓取与操作始终面临巨大挑战。传统传感器因无法全面感知触觉参数(如三维力、位移、摩擦),难以适应复杂多变的场景。Contactile推出的三轴触觉力传感器,通过仿生设计与创新光学技术…...
18-动规-子序列中的 k 种字母(中等)
题目 来源 28. 子序列中的 k 种字母(第一期模拟笔试) 思路 基本分析 子序列的定义 子序列是从原序列中选取部分元素,同时保持这些元素在原序列中的相对顺序所形成的新序列。也就是说,子序列中的元素不需要在原序列中连续出现…...
Lua 数组
Lua 数组 引言 Lua 是一种轻量级、高效的脚本语言,广泛应用于游戏开发、服务器端编程等领域。在 Lua 中,数组是一种非常重要的数据结构,它允许开发者以高效的方式存储和操作一系列数据。本文将详细介绍 Lua 数组的相关知识,包括…...
C 语言的未来:在变革中坚守核心价值
一、从 “古老” 到 “长青”:C 语言的不可替代性 诞生于 20 世纪 70 年代的 C 语言,历经半个世纪的技术浪潮,至今仍是编程世界的 “基石语言”。尽管 Python、Java 等高级语言在应用层开发中占据主流,但 C 语言在系统级编程和资…...
springboot3 基于 logback
1.配置 logging:level:root: ${ROOT_LOG_LEVEL:debug}web: ${ROOT_WEB_LEVEL:info}org.hibernate.orm.jdbc.bind: ${ROOT_SQL_PARAM_LEVEL:info} #打印SQL参数2.基于 logback-spring.xml配置 <?xml version="1.0" encoding="UTF-8"?> <configu…...
网络相关的知识总结1
1.设备可以通过以太网电缆(如双绞线)连接到交换机的端口,交换机也通过以太网电缆连接到路由器。但是如果距离过远,比如跨国路由器如何连接? 1.专用通信线路(如 MPLS、光纤专线):租用…...
Rust vs. Go: 性能测试(2025)
本内容是对知名性能评测博主 Anton Putra Rust vs. Go (Golang): Performance 2025 内容的翻译与整理, 有适当删减, 相关数据和结论以原作结论为准。 再次对比 Rust 和 Go,但这次我们使用的是最具性能优势的 HTTP 服务器库---Hyper,它基于 Tokio 异步运…...
如何使用postman调用多参数接口(包含文件上传)
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 如何使用postman调用包含文件参数的多参数接…...
无人机助力道路智能养护,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建无人机航拍道路交通场景下水泥路面缺陷智能检测识别系统
道路养护是保障交通命脉安全的隐形防线,其重要性不亚于道路建设本身。我国每年因道路病害引发的交通事故占比高达12%,及时修复1平方米的早期裂缝可避免后续数万元的修复成本。在这场与道路病害赛跑的战役中,传统养护模式正遭遇前所未有的挑战…...
28_跨域
目录 promise promise的基本语法 async await try catch promise 静态方法 跨域 跨域的解决方案 1-cors 编辑 2-jsonp方案 3-代理服务器 promise promise 是一个es6新增的语法 承诺的意思 作用:是专门用来解决回调地狱!!!! promise的基本语法 // 基本语法:// Pr…...
【C#.NET】VS2022创建Web API项目
C# Web API 是一种基于 .NET 平台(包括但不限于.NET Framework 和 .NET Core)构建 HTTP 服务的框架,用于创建 RESTful Web 服务。REST(Representational State Transfer)是一种软件架构风格,它利用HTTP协议…...
注意力蒸馏技术
文章目录 摘要abstract论文摘要简介方法预备知识注意力蒸馏损失注意力引导采样 实验结论总结参考文献 摘要 本周阅读了一篇25年二月份发表于CVPR 的论文《Attention Distillation: A Unified Approach to Visual Characteristics Transfer》,论文开发了Attention Distillation…...
Day17 -实例:利用不同语言不同框架的特征 进行识别
前置:我们所需的web站点,都可以利用fofa去搜索,例如:app"flask"这样的语句去找对应的站点,找到后,我们模拟不知道是什么框架,再根据特征去判断它的框架。 ***利用工具可以再去结合大…...
Centos7 安装 TDengine
Centos7 安装 TDengine 1、简介 官网: https://www.taosdata.com TDengine 是一款开源、高性能、云原生的时序数据库(Time Series Database, TSDB), 它专为物联网、车联网、工业互联网、金融、IT 运维等场景优化设计。同时它还带有内建的缓…...
DeepSeek调用API访问,使用AnythingLLM建立本地知识库后开放API调用,ApiFox/PostMan调用本地DeepSeek
上篇文章中我们使用AnythingLLM成功在本地部署了DeepSeek的本地知识库,并且上传了几个文件让DeepSeek学习,可点击查看: 本地部署DeepSeek并使用AnythingLLM建立本地知识库全流程,DeepSeek-R1:7b本地安装部署,DeepSeek-R1本地部署…...
SQL语句---DDL
文章目录 1、SQL语句2、DDL2.1 数据库的操作显示当前的数据库创建数据库指定编码删除数据库切换当前数据库 2.2 数据表的操作显示表创建表显示表结构修改表添加新的字段删除原有字段 修改原有字段删除数据表 2.3 Mysql数据库中常用的数据类型 1、SQL语句 结构化查询语句&#…...
Java实战:实现用户的登录注册功能
系列文章目录 Java文件 I/O流的操作实战和高级UI组件和事件监听的综合 文章目录 系列文章目录前言一、大致流程思路分析:二、定义用户类:三、服务层的实现: 1.保护用户数据功能的实现2.登录操作的实现 四、实现用户的注册界面: 大…...
用LLama factory时报类似Process 2504721 got signal: 1的解决方法
之前用nohup来远程跑LLama factory微调脚本,是没有问题的,但今天发现运行类似下面这个命令时, nohup llamafactory-cli train examples/train_qlora/qwen_lora.yaml 只要一关闭ssh session,就会终止训练,报类似&…...
WPF ContentTemplate
ContentTemplate 是一个非常重要的属性,用于定义 ContentPresenter 中内容的显示样式。通过设置 ContentTemplate,你可以控制 ContentPresenter 如何呈现其绑定的内容。 下面是对 ContentTemplate 的详细解释以及它的作用和用法。 1. ContentTemplate 的…...
powershell7.5.0不支持conda的问题
经历:这周手欠使用vscode的powershell时提示我更新,我就更新了,更新完激活不了conda环境了,查询了半天是powershell最新版7.5.0与目前conda25.1.1以前的版本不支持的问题。 问题环境:powershell版本>7.5.0ÿ…...
SSE SseEmitter.completeWithError(e) 触发的处理逻辑
在 Java 客户端使用 OkHttp 监听 SSE(Server-Sent Events) 的情况下,当服务端调用 SseEmitter.completeWithError(e),客户端会触发 EventSourceListener 的 onFailure() 方法(而不是 onError)。 1. 服务端&…...
JAVA反序列化深入学习(八):CommonsCollections6
与CC5相似: 在 CC5 中使用了 TiedMapEntry#toString 来触发 LazyMap#get在 CC6 中是通过 TiedMapEntry#hashCode 来触发 LazyMap#get 之前看到了 hashcode 方法也会调用 getValue() 方法然后调用到其中 map 的 get 方法触发 LazyMap,那重点就在于如何在反…...
科技快讯 | 韩国科学家研发出全球首款仿生液态机器人;OpenAI推出GPT-4o图像生成功能
韩国科学家研发出全球首款仿生液态机器人,自由变形穿越金属栅栏 韩国首尔大学等研究团队开发出一种基于液体的下一代软体机器人,具有细胞仿生特性,能自由变形、分裂融合,并执行物质运输任务。该机器人采用“颗粒装甲”设计&#x…...
[Vue2]v-model用于表单
之前我们讲到过v-model用于双向绑定一个数据,通常用于表单提交数据。而之前的演示里只演示了文本输入栏,这里详细演示一下其他表单输入时使用v-model。 文本输入 文本输入数据就是经典的type"text": <input type"text&qu…...
【机器学习】imagenet2012 数据预处理数据预处理
【机器学习】数据预处理 1. 下载/解压数据2. 数据预处理3. 加载以及训练代码3.1 使用PIL等加载代码3.2 使用OpenCV的方式来一张张加载代码3.3 h5的方式来加载大文件 最后总结 这个数据大约 140个G,128w的训练集 1. 下载/解压数据 首先需要下载数据: 数据最后处理…...
基于pycatia的CATIA零部件激活状态管理技术解析
一、问题背景:CATIA激活状态管理的痛点 在CATIA V5/V6的装配设计过程中,工程师经常使用激活状态控制(Activation)来管理大型装配体的显示性能。但实际使用中存在一个典型问题:当零部件被取消激活(Deac…...
基于javaweb的SpringBoot水果生鲜商城系统设计与实现(源码+文档+部署讲解)
技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…...
JVM 内存模型(JDK8+)
1. 内存模型结构图解 JVM 内存模型(JDK 8) ├── **线程私有区** │ ├── 程序计数器(Program Counter Register) │ ├── 虚拟机栈(VM Stack) │ │ └── 栈帧(局…...
基于飞腾FT2000/4的全国产标准6U VPX板卡,支持银河麒麟
1 功能 高可靠性的基于飞腾公司FT2000/4的处理器以及 X100 芯片组的标准6U VPX板卡,具有以太网、SATA、PCIE,以及显示等接口,产品功能框图如图1所示: 图 1 功能框图 2 技术指标 本产品功能和性能指标,见表 1。 表1 产品…...
【从零实现Json-Rpc框架】- 项目实现 - Dispatcher模块实现篇
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
WPS宏开发手册——JSA语法练习
目录 系列文章3、JSA语法练习3.1、运算练习3.2、比较练习3.3、if else练习3.4、for 练习3.5、字符串、数组方法练习3.6、语义转编程练习题 系列文章 使用、工程、模块介绍 JSA语法 JSA语法练习题 Excel常用Api 后续EXCEL实战、常见问题、颜色附录,持…...
【自学笔记】Go语言基础知识点总览-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1. Go 语言简介2. 基本语法变量声明与赋值常量数据类型运算符 3. 控制结构条件语句循环语句 4. 函数函数定义与调用多返回值匿名函数与闭包 5. 并发编程goroutinech…...
PyQt6实例_批量下载pdf工具_主线程停止线程池
目录 前置: 代码: 视频: 前置: 1 本系列将以 “PyQt6实例_批量下载pdf工具”开头,放在 【PyQt6实例】 专栏 2 本系列涉及到的PyQt6知识点: 线程池:QThreadPool,QRunnable; 信号与…...
在 Vue 项目中,登录成功后是否存储 token 与用户信息到本地
答案:不安全 举例:直接使用localStorage存储,本地存储可能会被 XSS 攻击窃取 localStorage.setItem(token, response.token)localStorage.setItem(userInfo, JSON.stringify({username: response.username,email: response.email})) 推荐方…...
【加密社】做一个展示币种价格的组件
具体的代码是以下,可以看到 <div id"crypto-price-widget"><p class"loading">Loading cryptocurrency prices... <span class"spinner"></span></p> </div><script> document.addEventListener(DOM…...
CANoe入门——CANoe的诊断模块,调用CAPL进行uds诊断
目录 一、诊断窗口介绍 二、诊断数据库文件管理 三、添加基础诊断描述文件(若没有CDD/ODX/PDX文件)并使用对应的诊断功能进行UDS诊断 3.1、添加基础诊断描述文件 3.2、基于基础诊断,使用诊断控制台进行UDS诊断 3.2.1、生成基础诊断 3.…...
AI日报 - 2025年3月30日
🌟 今日概览(60秒速览) ▎🤖 模型进展 | Qwen2.5-Omni多模态实时交互,Gemini 2.5 Pro/GPT-4o低调升级,Claude内部思考过程揭秘。 新模型和升级持续涌现,多模态与内部机制理解成焦点。 ▎&#x…...