react-router
现在的网站一般来讲很少只有单个“页面”,对于我们的博客来说,除了文章列表的界面,起码还得得有个文章详情页才行。
单页应用(SPA):可能你在官方介绍create-react-app
这个脚手架时已经看到了这个名词(注意不是做按摩的那个spa啊),但千万不要误以为单页面的意思是没有“可以点击的链接”的。在这里所说的单页应用实际上就是:既然我们将一个网页应用看作一堆组件的组合,那么动态的页面其实只需要动态更新显示部分组件就行,而不是像传统做法那样,把整个页面都刷新,所有资源都重新加载。
好了,看到这里你应该明白,create-react-app
是一个适于构建单页应用的脚手架,但不意味着想要做一个文章详情页就要再次yarn create react-app
新建一个项目了吧。
好了,说了这么多,开始写代码吧。这里我们需要学习一个新东西:react-router-dom
。首先进入我们的react_drf/frontend
目录,终端运行yarn add react-router-dom
来安装依赖。
函数组件
之前我们已经讲过了类组件,在React
中我们也可以创建函数形式的组件,函数组件又称为无状态组件,它可以接收一个props
作为参数,但是不可以使用state
,它没有状态,也没有生命周期函数(在本教程介绍React Hooks
之前这句话是正确的)。
为了介绍函数组件,这里先拆分一下组件,从ArticleList
拆出一个ArticleItem
。
const ArticleItem = props => (
// 这一部分从ArticleList复制过来
{props.item.title}
{props.item.body}
创建时间:{moment(props.item.created).fromNow()}
{' '}
更新时间:{moment(props.item.updated).fromNow()}
)
这里使用了箭头函数,要了解这些基础知识的细节,推荐去看MDN
、阮一峰的ES6入门教程
或者现代JavaScript教程
。
ArticleItem
组件的内容是从ArticleList
复制过来的,那么现在去修改ArticleList
的内容:
......
{articleList.map((item) =>
)}
......
我们将aritcleList
中的元素当作ArticleItem
的props
传递下去。可以看到这里的ArticleItem
组件就是一个函数,它的返回值就是要渲染的内容。
路由
在开始写代码之前,让我们先来构思一下路由划分。
首先我们看到,三张图中都有的共同点是都有最外层的矩形框以及导航栏这个矩形框。当点击home
时url
为/
,此时显示第一张图的样式,点击about
跳转到/about
,显示第二张图的样式,如果点击了某个ArticleItem
的标题,则进入/articles/articleId
,显示第三张图的样式。
所以,我们需要一个最外层的组件,可以利用之前的App
,App
中固定包含导航栏,当然这也可以作为Nav
组件被拆分出来,接着是包含ArticleList
和ArticleItem
组件的Home
组件,另外还有About
组件以及ArticleDetail
组件。
首先改写App.js
:
import React from 'react';
import './App.css';
import ArticleList from "./ArticleList";
import {
Switch,
Link,
Route
} from "react-router-dom";
import About from "./About";
import ArticleDetail from "./ArticleDetail";
const Home = () =>
function App() {
return (
);
}
export default App;
首先这里引入了react-router-dom
中的三个组件,Link
组件用于创建链接,Switch
和Route
搭配使用,Switch
会搜索子元素Route
,当找到其路径与当前url
相匹配的Route
时,则渲染此Route
内容,并忽略其它的Route
。例如当前url
为根路径/
,那么就会渲染这里最后一个Route
中的Home
,这里由于Home
中暂时没有其它元素,我直接将它定义在了App.js
。
注意到这里有个/articles/:articleId
的路由,这是到文章详情页的链接,这里引入的ArticleDetail
和About
两个组件我们稍后完成。
别忘了在之前的index.js
中我们渲染的是ArticleList
,现在去更改它:
......
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
,
document.getElementById('root')
);
要使用react-router-dom
的路由,还需要在App
外套上BrowserRouter
。
现在去修改ArticleList.js
,让其根据文章ID创建不同的Link
:
......
import { Link } from "react-router-dom";
......
const ArticleItem = props => (
{props.item.title}
......
)
class ArticleList extends Component {
......
}
我们使用ES6
语法的模板字符串,注意/articles/${props.item.id}
}>
里的不是单引号,而是键盘左上角esc键下面那个反引号。这和Python
中的f
字符串有些类似,都允许在字符串中嵌入变量,但是ES6
的写起来有点麻烦。JSX
的实现也离不开模板字符串哦。
OK,现在让我们在src
目录下新建两个文件ArticleDetail.js
和About.js
:
// ArticleDetail.js
import React from "react";
import { useParams } from 'react-router-dom';
const ArticleDetail = () => {
// 取出url中的参数
const { articleId } = useParams();
return (
article {articleId}
)
}
export default ArticleDetail
// About.js
import React from "react";
const About = () =>
这是我的博客
export default About
现在使用yarn start
启动应用,别忘了Django
应用也要启动哦,否则看不到文章啦。现在在网页上点击文章标题或者导航栏的链接试试看吧。
练习
现在我们的文章详情组件只是简单地显示了article + id
,请你尝试重写组件以显示真正的文章详情,这篇文章不讲Hooks
(其实我们已经不知不觉中使用过了),所以你可能要将ArticleDetail
改写为类组件,并通过props
传递id
并在componentDidMount
中请求API。
欢迎关注我的公众号“公子政的宅日常”,原创技术文章第一时间推送。