ReactJS101 项目实战:使用 React + Redux + Node 开发同构食谱分享网站
前言
在本教程中,我们将基于 ReactJS101 项目的知识体系,带领大家开发一个完整的同构(Isomorphic)JavaScript 应用——食谱分享网站。这个项目将整合 React、Redux、React Router 和 Node.js 等技术栈,实现前后端同构渲染、用户认证、CRUD 操作等核心功能。
项目概述
功能需求
我们将开发一个具有以下功能的食谱分享社区网站:
- 用户认证(登录/登出)
- 食谱的创建、读取、更新和删除(CRUD)
- 食谱列表展示
- 响应式设计
技术栈
-
前端:
- React:构建用户界面
- Redux:状态管理
- React Router:路由控制
- Immutable.js:不可变数据结构
- Axios:HTTP 请求
-
后端:
- Node.js + Express:服务器框架
- MongoDB + Mongoose:数据库存储
- JSON Web Token (JWT):用户认证
-
构建工具:
- Webpack:模块打包
- Babel:ES6/JSX 转译
项目结构设计
项目采用模块化设计,主要目录结构如下:
src/
├── client/ # 客户端代码
├── common/ # 共享代码(Redux、路由等)
├── server/ # 服务器端代码
│ ├── config/ # 配置文件
│ ├── controllers/ # API控制器
│ ├── models/ # 数据库模型
核心实现
1. 同构渲染架构
同构(Isomorphic)JavaScript 是指同一套代码可以在客户端和服务器端运行。我们的实现方案包括:
服务器端渲染流程:
- 接收请求
- 获取初始数据
- 创建 Redux store
- 渲染 React 组件为字符串
- 注入初始状态到 HTML
- 发送完整 HTML 到客户端
关键代码实现:
// 服务器端渲染处理函数
const handleRender = (req, res) => {
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
// 获取初始数据
fetchComponentData(req.cookies.token).then((response) => {
const initialState = fromJS({
recipe: { /* 初始食谱数据 */ },
user: { /* 用户认证状态 */ }
});
// 创建 Redux store
const store = configureStore(initialState);
// 渲染组件为字符串
const initView = renderToString(
<Provider store={store}>
<RouterContext {...renderProps} />
</Provider>
);
// 注入初始状态到 HTML
const page = renderFullPage(initView, store.getState());
res.status(200).send(page);
});
});
};
2. 用户认证系统
我们采用 JWT(JSON Web Token)实现用户认证:
-
登录流程:
- 用户提交邮箱和密码
- 服务器验证并生成 JWT
- 将 token 返回给客户端存储
-
认证中间件:
- 对需要认证的 API 请求验证 token
- 验证通过后允许访问受保护资源
关键代码实现:
// 登录 API
apiRoutes.post('/login', (req, res) => {
User.findOne({ email: req.body.email }, (err, user) => {
if (!user || user.password !== req.body.password) {
return res.json({ success: false, message: '认证失败' });
}
// 生成 JWT
const token = jwt.sign(
{ email: user.email },
app.get('superSecret'),
{ expiresIn: '24h' }
);
res.json({ success: true, token });
});
});
// 认证中间件
apiRoutes.use((req, res, next) => {
const token = req.body.token || req.query.token;
if (!token) {
return res.status(403).send({ success: false, message: '未提供 token' });
}
jwt.verify(token, app.get('superSecret'), (err, decoded) => {
if (err) return res.json({ success: false, message: 'token 验证失败' });
req.decoded = decoded;
next();
});
});
3. Redux 状态管理
我们使用 Redux 管理应用状态,主要包含以下部分:
-
Reducer 设计:
recipeReducer
:管理食谱相关状态userReducer
:管理用户认证状态uiReducer
:管理 UI 相关状态
-
异步 Action:
- 使用
redux-promise
中间件处理异步 API 调用 - 通过
redux-actions
创建标准的 Flux 动作
- 使用
关键代码实现:
// 异步获取食谱列表
export const getRecipes = createAction('GET_RECIPES', WebAPI.getRecipes);
// 添加新食谱
export const addRecipe = createAction('ADD_RECIPE', WebAPI.addRecipe);
// 更新食谱
export const updateRecipe = createAction('UPDATE_RECIPE', WebAPI.updateRecipe);
// 删除食谱
export const deleteRecipe = createAction('DELETE_RECIPE', WebAPI.deleteRecipe);
4. 路由与权限控制
我们使用 React Router 结合高阶组件实现路由权限控制:
-
路由配置:
- 主路由包含首页、登录页和分享页
- 使用
IndexRoute
设置默认路由
-
权限控制:
- 已登录用户不能访问登录页
- 未登录用户不能访问分享页
关键代码实现:
// 高阶组件实现权限控制
const CheckAuth = (Component, type) => {
return class extends React.Component {
componentWillMount() {
this.props.checkAuth();
}
render() {
const { isAuthorized } = this.props.user;
if ((type === 'guest' && isAuthorized) || (type === 'auth' && !isAuthorized)) {
return <Redirect to={type === 'guest' ? '/' : '/login'} />;
}
return <Component {...this.props} />;
}
};
};
// 路由配置
export default (
<Route path='/' component={Main}>
<IndexRoute component={HomePageContainer} />
<Route path="/login" component={CheckAuth(LoginPageContainer, 'guest')}/>
<Route path="/share" component={CheckAuth(SharePageContainer, 'auth')}/>
</Route>
);
开发环境配置
1. 基础配置
-
Babel 配置 (
.babelrc
):{ "presets": ["es2015", "react"] }
-
ESLint 配置 (
.eslintrc
):{ "extends": "airbnb", "rules": { "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }] }, "env": { "browser": true } }
2. Webpack 配置
module.exports = {
entry: ['./src/client/index.js'],
output: {
path: `${__dirname}/dist`,
filename: 'bundle.js',
publicPath: '/static/'
},
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015', 'react']
}
}]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin()
]
};
数据库设计
我们使用 MongoDB 存储数据,通过 Mongoose 定义数据模型:
1. 用户模型
import mongoose, { Schema } from 'mongoose';
export default mongoose.model('User', new Schema({
id: Number,
username: String,
email: String,
password: String,
admin: Boolean
}));
2. 食谱模型
import mongoose, { Schema } from 'mongoose';
export default mongoose.model('Recipe', new Schema({
id: String,
name: String,
description: String,
imagePath: String,
steps: Array,
updatedAt: Date
}));
总结
通过本教程,我们实现了一个完整的同构 JavaScript 应用,涵盖了以下关键技术点:
- React + Redux 的前端架构
- Node.js + Express 的后端服务
- MongoDB 数据存储
- JWT 用户认证
- 同构渲染实现
- 路由权限控制
这个项目展示了如何将现代前端技术栈整合到一个实际应用中,为开发者提供了一个很好的学习范例。通过实践这个项目,你可以深入理解 React 生态系统中的各种技术和它们之间的协作方式。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考