致虚极 守静笃
路由
2020-04-21发布 375

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复制过来
  <div key={props.item.id}>
    <h4 style={{ color: "red" }}>{props.item.title}</h4>
    <p>
      <strong>{props.item.body}</strong>
      <br/>
      <em>创建时间:{moment(props.item.created).fromNow()}</em>
      {' '}
      <em>更新时间:{moment(props.item.updated).fromNow()}</em>
    </p>
  </div>
)

这里使用了箭头函数,要了解这些基础知识的细节,推荐去看MDN、阮一峰的ES6入门教程或者现代JavaScript教程

ArticleItem组件的内容是从ArticleList复制过来的,那么现在去修改ArticleList的内容:

     ......
      <div className="ArticleList">
        {articleList.map((item) =>
          <ArticleItem item={item} />
        )}
      </div>
      ......

我们将aritcleList中的元素当作ArticleItemprops传递下去。可以看到这里的ArticleItem组件就是一个函数,它的返回值就是要渲染的内容。

路由

在开始写代码之前,让我们先来构思一下路由划分。

Layout

首先我们看到,三张图中都有的共同点是都有最外层的矩形框以及导航栏这个矩形框。当点击homeurl/,此时显示第一张图的样式,点击about跳转到/about,显示第二张图的样式,如果点击了某个ArticleItem的标题,则进入/articles/articleId,显示第三张图的样式。

所以,我们需要一个最外层的组件,可以利用之前的AppApp中固定包含导航栏,当然这也可以作为Nav组件被拆分出来,接着是包含ArticleListArticleItem组件的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 = () =>
  <ArticleList />

function App() {
  return (
    <div className="App">
      <header>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
          </ul>
        </nav>
      </header>

      <hr />

      <Switch>
        <Route path="/about">
          <About />
        </Route>
        <Route path="/articles/:articleId">
          <ArticleDetail />
        </Route>
        <Route exact path="/">
          <Home />
        </Route>
      </Switch>
    </div>
  );
}

export default App;

首先这里引入了react-router-dom中的三个组件,Link组件用于创建链接,SwitchRoute搭配使用,Switch会搜索子元素Route,当找到其路径与当前url相匹配的Route时,则渲染此Route内容,并忽略其它的Route。例如当前url为根路径/,那么就会渲染这里最后一个Route中的Home,这里由于Home中暂时没有其它元素,我直接将它定义在了App.js

注意到这里有个/articles/:articleId的路由,这是到文章详情页的链接,这里引入的ArticleDetailAbout两个组件我们稍后完成。

别忘了在之前的index.js中我们渲染的是ArticleList,现在去更改它:

......
import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

要使用react-router-dom的路由,还需要在App外套上BrowserRouter

现在去修改ArticleList.js,让其根据文章ID创建不同的Link

......
import { Link } from "react-router-dom";

......

const ArticleItem = props => (
  <div key={props.item.id}>
    <Link to={`/articles/${props.item.id}`}>
      <h4 style={{ color: "red" }}>{props.item.title}</h4>
    </Link>
    ......
  </div>
)

class ArticleList extends Component {
......
}

我们使用ES6语法的模板字符串,注意<Link to={/articles/${props.item.id}}>里的不是单引号,而是键盘左上角esc键下面那个反引号。这和Python中的f字符串有些类似,都允许在字符串中嵌入变量,但是ES6的写起来有点麻烦。JSX的实现也离不开模板字符串哦。

OK,现在让我们在src目录下新建两个文件ArticleDetail.jsAbout.js

// ArticleDetail.js
import React from "react";
import { useParams } from 'react-router-dom';

const ArticleDetail = () => {
// 取出url中的参数
  const { articleId } = useParams();

  return (
    <div>
      article {articleId}
    </div>
  )
}

export default ArticleDetail
// About.js
import React from "react";

const About = () =>
  <div>
    这是我的博客
  </div>

export default About

现在使用yarn start启动应用,别忘了Django应用也要启动哦,否则看不到文章啦。现在在网页上点击文章标题或者导航栏的链接试试看吧。

练习

现在我们的文章详情组件只是简单地显示了article + id,请你尝试重写组件以显示真正的文章详情,这篇文章不讲Hooks(其实我们已经不知不觉中使用过了),所以你可能要将ArticleDetail改写为类组件,并通过props传递id并在componentDidMount中请求API。

版权声明