在前端技术日新月异的时代,建设一个完整的博客 SPA 应用已经不再是一件难事。本文将介绍如何通过使用 React 和 Antd4 来创建一个完整的博客 SPA 应用。
前置条件
在继续之前,需要确保您已经具备以下的前置条件:
- 了解 React 的基本概念和语法
- 熟悉 Antd4 组件库的使用方法
- 了解前端路由的概念和使用
如果您对以上任何一个条件不熟悉,可以先通过相关文档和教程进行学习。
目标功能
在本篇文章中,我们将完成以下几个目标功能:
- 博客文章列表的展示
- 博客文章的详情页展示
- 博客文章的发布和编辑功能
- 博客文章的分类和标签筛选功能
- 登录和注册功能
技术选型
在开始项目建设前,我们需要确定本项目的技术选型。在本文中,我们将使用以下技术:
- React:一个用于构建用户界面的 JavaScript 库
- Antd4:一个企业级 UI 设计语言和 React 组件库
以下是 Antd4 相关的 npm 包:
- antd:Antd4 的组件库
- less:Antd4 使用 less 预处理器
- babel-plugin-import:Antd4 支持按需加载,需要这个插件
- @ant-design/icons:Antd4 的图标库
在您的项目中使用 React 和 Antd4,可以通过以下的命令安装相关依赖:
npm install --save react antd less babel-plugin-import @ant-design/icons
创建项目
在开始创建项目前,确保您已经在本地安装了 Node.js 和 npm。接着,我们需要通过以下命令创建一个新的 React 项目:
npx create-react-app blog-spa
以上命令将创建一个名为 "blog-spa" 的新 React 项目,并自动安装相关依赖。
配置 Antd4
我们需要通过以下步骤来配置 Antd4:
- 修改项目中的 .env 文件
我们需要在项目中的 .env 文件中添加以下内容:
GENERATE_SOURCEMAP=false
通过添加以上内容,将会关闭生产环境下的 source map,从而提升项目的性能。
- 添加 Antd4 相关依赖
通过以下命令安装 Antd4 相关的依赖:
npm install antd less babel-plugin-import @ant-design/icons --save
- 修改项目的配置文件
在项目的根目录下,我们需要新建 "config-overrides.js" 文件,并添加以下代码:
// javascriptcn.com 代码示例 const { override, fixBabelImports, addLessLoader } = require('customize-cra'); module.exports = override( fixBabelImports('import', { libraryName: 'antd', libraryDirectory: 'es', style: true, }), addLessLoader({ lessOptions: { javascriptEnabled: true, }, }), );
以上代码将会使用 customize-cra 库来覆盖 Create React App 的默认配置。具体来说,这份配置将会进行以下操作:
- 使用 babel-plugin-import 插件来实现按需加载,减小打包后的文件大小
- 使用 less-loader 来解析 Antd4 中的 less 样式文件
修改 "package.json" 中的 "scripts",并添加以下内容:
"scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "start-server": "json-server --watch db.json --port 3001", "dev": "concurrently \"npm start\" \"npm run start-server\"" },
以上代码将会启动 json-server 来模拟后端数据的 API,而 "dev" 命令将会同时启动前后端服务。
- 引入 Antd4 样式和图标
在项目的 "src/index.js" 文件中添加以下代码:
import 'antd/dist/antd.css'; import '@ant-design/icons/dist/antd.css';
以上代码将会引入 Antd4 中的样式表和图标。
开发流程
在配置 Antd4 完毕后,接下来将介绍我们所需的开发流程:
1. 定义路由
在项目根目录下,新建 "src/router.js" 文件,并添加以下代码:
// javascriptcn.com 代码示例 import React from 'react'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import BlogList from './views/BlogList'; import BlogDetail from './views/BlogDetail'; import Login from './views/Login'; import Register from './views/Register'; import BlogEdit from './views/BlogEdit'; const Router = () => { return ( <BrowserRouter> <Switch> <Route path="/" exact component={BlogList} /> <Route path="/blogs/:id" exact component={BlogDetail} /> <Route path="/login" exact component={Login} /> <Route path="/register" exact component={Register} /> <Route path="/blogs/:id/edit" exact component={BlogEdit} /> </Switch> </BrowserRouter> ); }; export default Router;
以上代码将会定义我们所需的路由,其中:
"/"
路径将会渲染BlogList
页面组件"/blogs/:id"
路径将会渲染BlogDetail
页面组件"/login"
路径将会渲染Login
页面组件"/register"
路径将会渲染Register
页面组件"/blogs/:id/edit"
路径将会渲染BlogEdit
页面组件
2. 编写页面组件
通过以上的路由定义,我们需要实现以下几个页面组件:
BlogList 组件
// javascriptcn.com 代码示例 import React, { useState, useEffect } from 'react'; import { PageHeader, Button, message, Empty, Card } from 'antd'; import { Link } from 'react-router-dom'; import { getBlogs } from '../api/blog'; const BlogList = () => { const [blogs, setBlogs] = useState([]); useEffect(() => { getBlogList(); }, []); const getBlogList = async () => { try { const { data } = await getBlogs(); setBlogs(data); } catch (error) { message.error(error.message); } }; if (blogs.length === 0) { return ( <div> <PageHeader title="Blog List" extra={[<Link to="/blogs/create"><Button key="1" type="primary">Create</Button></Link>]} /> <Empty /> </div> ); } return ( <div> <PageHeader title="Blog List" extra={[<Link to="/blogs/create"><Button key="1" type="primary">Create</Button></Link>]} /> {blogs.map(blog => ( <Card title={blog.title} key={blog.id}> {blog.content} <br /> <br /> <Link to={`/blogs/${blog.id}`}>查看详情</Link> </Card> ))} </div> ); }; export default BlogList;
以上代码将会渲染博客文章列表页面,并提供创建新博客文章的入口。我们使用 Antd4 的 PageHeader 和 Card 组件来实现页面布局,使用 Link 组件来实现路由跳转。
BlogDetail 组件
// javascriptcn.com 代码示例 import React, { useState, useEffect } from 'react'; import { PageHeader, message } from 'antd'; import { useParams, Link, useHistory } from 'react-router-dom'; import { getBlogById } from '../api/blog'; const BlogDetail = () => { const [blog, setBlog] = useState({}); const { id } = useParams(); const history = useHistory(); useEffect(() => { getBlogDetail(id); }, [id]); const getBlogDetail = async (id) => { try { const { data } = await getBlogById(id); setBlog(data); } catch (error) { message.error(error.message); } }; const handleEdit = () => { history.push(`/blogs/${id}/edit`); }; return ( <div> <PageHeader title={blog.title} onBack={() => history.goBack()} extra={[<Link key="1" to={`/blogs/${id}/edit`}>Edit</Link>]} /> {blog.content} </div> ); }; export default BlogDetail;
以上代码将会渲染博客文章详情页面,提供编辑博客文章的入口。我们使用 Antd4 的 PageHeader 组件来实现页面布局,使用 useParams 和 useHistory 来获取路由参数和进行路由跳转。
Login 组件
// javascriptcn.com 代码示例 import React, { useState } from 'react'; import { PageHeader, Form, Input, Button, message } from 'antd'; import { useHistory } from 'react-router-dom'; import { login } from '../api/user'; const Login = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const history = useHistory(); const handleSubmit = async () => { try { await login({ username, password }); message.success('登录成功!'); history.push('/'); } catch (error) { message.error(error.message); } }; return ( <div> <PageHeader title="Login" /> <Form name="login-form" onFinish={handleSubmit}> <Form.Item name="username" label="Username" rules={[{ required: true }]}> <Input value={username} onChange={(e) => setUsername(e.target.value)} /> </Form.Item> <Form.Item name="password" label="Password" rules={[{ required: true }]}> <Input type="password" value={password} onChange={(e) => setPassword(e.target.value)} /> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit">Login</Button> </Form.Item> </Form> </div> ); }; export default Login;
以上代码将会渲染登录页面,并使用 Antd4 的 Form、Input 和 Button 组件来实现表单提交和页面布局,使用 useHistory 来进行路由跳转,使用 login API 来进行登录认证。
Register 组件
// javascriptcn.com 代码示例 import React, { useState } from 'react'; import { PageHeader, Form, Input, Button, message } from 'antd'; import { useHistory } from 'react-router-dom'; import { register } from '../api/user'; const Register = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const history = useHistory(); const handleSubmit = async () => { try { if (password !== confirmPassword) { throw new Error('两次输入的密码不一致!'); } await register({ username, password }); message.success('注册成功!'); history.push('/login'); } catch (error) { message.error(error.message); } }; return ( <div> <PageHeader title="Register" /> <Form name="register-form" onFinish={handleSubmit}> <Form.Item name="username" label="Username" rules={[{ required: true }]}> <Input value={username} onChange={(e) => setUsername(e.target.value)} /> </Form.Item> <Form.Item name="password" label="Password" rules={[{ required: true }]}> <Input type="password" value={password} onChange={(e) => setPassword(e.target.value)} /> </Form.Item> <Form.Item name="confirmPassword" label="Confirm Password" rules={[{ required: true }]}> <Input type="password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} /> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit">Register</Button> </Form.Item> </Form> </div> ); }; export default Register;
以上代码将会渲染注册页面,并使用 Antd4 的 Form、Input 和 Button 组件来实现表单提交和页面布局,使用 useHistory 来进行路由跳转,使用 register API 来进行用户注册。
BlogEdit 组件
// javascriptcn.com 代码示例 import React, { useState, useEffect } from 'react'; import { PageHeader, Form, Input, Button, message } from 'antd'; import { useParams, useHistory } from 'react-router-dom'; import { getBlogById, updateBlog } from '../api/blog'; const BlogEdit = () => { const [blog, setBlog] = useState({}); const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const { id } = useParams(); const history = useHistory(); useEffect(() => { getBlogDetail(id); }, [id]); const getBlogDetail = async (id) => { try { const { data } = await getBlogById(id); setBlog(data); setTitle(data.title); setContent(data.content); } catch (error) { message.error(error.message); } }; const handleSubmit = async () => { const payload = { title, content }; try { await updateBlog(id, payload); message.success('更新成功!'); history.push(`/blogs/${id}`); } catch (error) { message.error(error.message); } }; return ( <div> <PageHeader title={title} onBack={() => history.goBack()} /> <Form name="blog-edit-form" onFinish={handleSubmit}> <Form.Item name="title" label="Title" rules={[{ required: true }]}> <Input value={title} onChange={(e) => setTitle(e.target.value)} /> </Form.Item> <Form.Item name="content" label="Content" rules={[{ required: true }]}> <Input.TextArea value={content} onChange={(e) => setContent(e.target.value)} /> </Form.Item> <Form.Item> <Button type="primary" htmlType="submit">Publish</Button> </Form.Item> </Form> </div> ); }; export default BlogEdit;
以上代码将会渲染博客文章编辑页面,并使用 Antd4 的 Form、Input 和 Button 组件来实现表单提交和页面布局,使用 useParams 和 useHistory 来获取路由参数和进行路由跳转,使用 getBlogById 和 updateBlog API 来进行数据请求和更新。
3. 编写 API 接口
以上实现中,我们所需连接的后端数据 API 并没有被说明。我们需要通过以下步骤来实现这些 API 的编写:
- 新建 "api" 文件夹,并添加 "blog.js"、"user.js" 和 "index.js" 三个文件
- 在 "blog.js" 文件中,添加以下代码:
// javascriptcn.com 代码示例 import axios from 'axios'; const URL = 'http://localhost:3001/blogs'; export const getBlogs = () => { return axios.get(URL); }; export const getBlogById = (id) => { return axios.get(`${URL}/${id}`); }; export const createBlog = (payload) => { return axios.post(URL, payload); }; export const updateBlog = (id, payload) => { return axios.put(`${URL}/${id}`, payload); }; export const deleteBlog = (id) => { return axios.delete(`${URL}/${id}`); };
以上代码将会定义我们所需的博客文章 API 接口。
- 在 "user.js" 文件中,添加以下代码:
// javascriptcn.com 代码示例 import axios from 'axios'; const URL = 'http://localhost:3001'; export const login = (payload) => { return axios.post(`${URL}/login`, payload); }; export const register = (payload) => { return axios.post(`${URL}/users`, payload); };
以上代码将会定义我们所需的用户登录和注册 API 接口。
- 在 "index.js" 文件中,添加以下代码:
export * from './blog'; export * from './user';
以上代码将会导出所有的 API 接口函数。
4. 启动项目
完成以上所有步骤后,我们可以通过以下命令启动项目:
npm run dev
以上命令将会同时启动前端 React 服务和后端 json-server 数据服务,从而使得我们的前端页面可以通过后端数据 API 进行数据获取和展示。
总结
通过本文的介绍,我们了解到如何使用 React 和 Antd4 来实现一个完整的博客 SPA 应用,包括页面路由的定义、页面组件的编写、API 接口的定义,以及前后端服务的启动等等。这让我们更加深入地了解了前端的开发流程和技术栈,对于提升我们的前端技术水平具有极大的帮助。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/654debc67d4982a6eb7481a2