手把手教你实现react路由配置

前言

基本上我们在写 React 项目的时候,路由切换是我们碰到的最棘手问题之一。小编最近在写极客时间项目时,也遇到了路由配置的难题,想了三天三夜都没得出个毛线,而当品读了神三元的小册之后,小编不得不给他点赞,吾尝终日而思矣,不如须臾之所学也。同样的,老铁们读完这篇文章,拿下路由不在话下。

本文梳理了 4 个知识点,帮助大家提高路由的使用技巧

如何配置哈希路由

路由,简单点说就是我们如何从一个页面跳转到另一个页面。光是跳转页面,我们想到的解决办法可能有如下几种:window.location.href = "https://www.baidu.com/"<a href="https://www.baidu.com/">跳转</>, self.location = "https://www.baidu.com/",你能想到这几种代表你的基础知识已经很扎实,接下来我们将再深入了解点有内涵的东西 - 「路由」。

一个demo

假如我们要用 React 开发一个手机端 App,手机底部导航栏有五个不同的按钮,每当我点击一个按钮时 App 都会自动切换到一个页面,并且底部导航栏一直固定保持不变。(效果如下)你会怎么做?
在这里插入图片描述

首先,我们来剖析一下完成这道题的基本思路。

  1. 我们需要一个空白的页面,由于我们刚进 App 时需要定位到一个页面,因此我们要重定向到 Find 页面。除此之外,我们的五个页面都依赖着底部导航栏,因此,底部导航栏是第二个需要加载在空白页面上的内容
  2. 当固定好底部导航栏之后,其他五个页面都是底部导航栏的子路由,因此这些子路由都需要嵌套在前路由里,五个子路由之间的关系是平行关系,而它们和底部导航栏的关系是父子关系
  • 第一步:搭好空白页面,空白页面可以说是一个入门的页面,里面什么内容都没有
import React from 'react';
import { renderRoutes } from 'react-router-config';

const Layout = ({ route }) => <>{renderRoutes(route.routes)}</>
export default Layout;
  • 第二步:写好底部导航栏,每个按钮添加了一个「Navlink」用于跳转
import React from 'react';
import { NavLink } from 'react-router-dom';
import { renderRoutes } from 'react-router-config';
function Foot({ route }) {
    return (
        <div>
            {renderRoutes(route.routes)}
            <div className="gk-foot">
                <NavLink
                    to="/find"
                    className="gk-foot--item gk-foot--item__on"
                    activeClassName="selected">
                    <span className="gk-foot--icon iconfont">&#xe60e;</span>
                    <p className="gk-foot--label">发现</p>
                </NavLink>

                <NavLink to="/lecture" className="gk-foot--item" activeClassName="selected">
                    <span className="gk-foot--icon iconfont">&#xe726;</span>
                    <p className="gk-foot--label">讲堂</p>
                </NavLink>

                <NavLink to="/horde" className="gk-foot--item" activeClassName="selected">
                    <span className="gk-foot--icon iconfont">&#xe637;</span>
                    <p className="gk-foot--label">部落</p>
                </NavLink>

                <NavLink to="/study" className="gk-foot--item" activeClassName="selected">
                    <span className="gk-foot--icon iconfont">&#xe625;</span>
                    <p className="gk-foot--label">学习</p>
                </NavLink>

                <NavLink to="/user" className="gk-foot--item" activeClassName="selected">
                    <span className="gk-foot--icon iconfont">&#xe617;</span>
                    <p className="gk-foot--label">我的</p>
                </NavLink>
            </div>
        </div>
    );
}


export default Foot;
  • 第三步:写好五个页面,五个页面都很简洁,只需要渲染一句话
import React from 'react';

function Find() {
    return (
        <div>
            Find
        </div>
    )
}

export default Find;
import React from "react";

function Lessons() {
    return (
        <div>
            Lessons
        </div>
    )
}
export default Lessons;
import React from 'react';

function Horde() {
    return (
        <div>
            Horde
        </div>
    )
}

export default Horde;
import React from 'react';

function Study() {
    return (
        <div>
            Study
        </div>
    )
}

export default Study;
import React from 'react';

function HomePage() {
    return (
        <div>
            My
        </div>
    )
}

export default HomePage;
项目结构
├─demo                         # 项目根目录
│  ├─src                       # 组件根目录
│    ├─assets                  # 静态文件svg
│    ├─components              # 共用组件
│      ├─Foot.js
│    ├─layouts                 # 页面目录
│      ├─Find.js               # 发现页面
│      ├─Lessons.js            # 课程页面
│      ├─Horde.js              # 部落页面
│      ├─Study.js              # 学习页面
│      ├─HomePage.js           # 主页
│    ├─routes                  # 路由
│      ├─index.js              # 路由配置页面
│      ├─BlankLayout.js        # 空白页面
├─README.md                    # README

最后,在 index.js 文件中,配置路径时我们这样写

import React from 'react';
import BlankLayout from './BlankLayout';
import { Redirect } from 'react-router-dom';
import FootLayout from '../layouts/Foot';
import FindLayout from '../layouts/Find';
import LessonsLayout from '../layouts/Lessons';
import HordeLayout from '../layouts/Horde';
import StudyLayout from '../layouts/Study';
import HomePageLayout from '../layouts/HomePage';

export default [
  {
    component: BlankLayout, //最初的空白页面
    routes: [
      {
        path: "/",
        component: FootLayout,  //固定住底部导航栏-“父路由”
        routes: [
          {
            path: "/",
            exact: true,
            render: () => <Redirect to={"/find"} />,  //重定向到发现页面
          },
          {
            path: "/find",
            component: FindLayout,
          },
          {
            path: "/lecture",
            component: LessonsLayout,
          },
          {
            path: "/horde",
            component: HordeLayout,
          },
          {
            path: "/study",
            component: StudyLayout,
          },
          {
            path: "/homepage",
            component: HomePageLayout,
          },
        ],
      },
    ],
  },
];

到这里为止,凡是我们配置了路径的页面都能拿到路径的信息了,我们可以从页面的props中解构出route来看一下路径

这些路径我们需要从父路由里面结构出来,然后在父组件中使用「NavLink」标签,紧接着当我们点击「NavLink」标签时,我们需要重新渲染一下路径,因此在父组件中{renderRoutes(route.routes)}是必不可少的,如果你没加这句话你会看到路径虽然发生了变化,但是页面是完全没有变化的,现在你可以动手尝试一下啦!

多层路由嵌套怎么解决

万变不离其中的固定原理: 既然我们上面已经配置了两层路由,顺水推舟,多层路由嵌套也就是再在子路由里嵌套子路由罢了

demo

现在我的 App 已经有了底部导航栏并且可以切换到不同的页面,现在我需要头部导航栏让我可以进入更详细的页面。(效果如下),你会怎么做?
在这里插入图片描述

根据上道题的思路,我们可以在 index.js 文件的 Lesson.js 页面里更深层次的嵌套路径,原因有以下三点:

  1. 底部导航栏是要固定不变的,说明Camp, Daily页面是底部导航栏的子路由
  2. 头部导航栏在Lesson页面是固定不变的,说明头部导航栏要是底部导航栏的子路由
  3. 由于头部导航栏切换的时候,它是固定存在的,说明Camp, Daily页面还是头部导航栏的子路由

我们用一张图来观察一下它们的关系

丢一下刚刚的代码

import React from 'react';
import BlankLayout from './BlankLayout';
import { Redirect } from 'react-router-dom';
import FootLayout from '../layouts/Foot';
import FindLayout from '../layouts/Find';
import LessonsLayout from '../layouts/Lessons';
import HordeLayout from '../layouts/Horde';
import StudyLayout from '../layouts/Study';
import HomePageLayout from '../layouts/HomePage';
import CampLayout from '../layouts/Lessons/Camp';
import DailyLayout from '../layouts/Lessons/Daily';

export default [
  {
    component: BlankLayout, //最初的空白页面
    routes: [
      {
        path: "/",
        component: FootLayout,  //固定住底部导航栏-“父路由”
        routes: [
          {
            path: "/",
            exact: true,
            render: () => <Redirect to={"/find"} />,  //重定向到发现页面
          },
          {
            path: "/find",
            component: FindLayout,
          },
          {  
                path: "/lecture",
                component: Header,
                routes: [
                  {
                    path: "/lecture",
                    exact: true,
                    render: () => <Redirect to={"/lecture/lessons"} />
                  },
                  {
                    path: '/lecture/lessons',
                    component: LessonsLayout 
                  },
                  {
                    path: '/lecture/camp',
                    component: CampLayout
                  },
                  {
                    path: '/lecture/daily',
                    component: DailyLayout
                  },
                ] 
              },
          {
            path: "/horde",
            component: HordeLayout,
          },
          {
            path: "/study",
            component: StudyLayout,
          },
          {
            path: "/homepage",
            component: HomePageLayout,
          },
        ],
      },
    ],
  },
];

项目结构

├─demo                         # 项目根目录
│  ├─src                       # 组件根目录
│    ├─assets                  # 静态文件svg
│    ├─components              # 共用组件
│      ├─Foot.js
│    ├─layouts                 # 页面目录
│      ├─Find.js               # 发现页面
│      ├─Lessons               # 课程目录
│        ├─camp                # 训练营文件
│          ├─index.js          # 训练营页面
│        ├─daily               # 每日一课文件
│          ├─index.js          # 每日一课页面
│        ├─lessons             # 课程文件
│          ├─index.js          # 课程页面
│      ├─Horde.js              # 部落页面
│      ├─Study.js              # 学习页面
│      ├─HomePage.js           # 主页
│    ├─routes                  # 路由
│      ├─index.js              # 路由配置页面
│      ├─BlankLayout.js        # 空白页面
├─README.md                    # README

这样我们就很巧妙的解决了多层路由嵌套问题,在最外层的路由里一层又一层的嵌套新的路由,一般一个页面最多嵌套3-4层路由,如果你的项目路由嵌套过多,想想是否可以稍微改进一下呢?「此外牢记无论嵌套了多少层路由,为了显示目前这个页面,我们需要找到它的父路由,然后在它的父路由里面解构出route,最后渲染一下页面{renderRoutes(route.routes)},页面就显示出来了,不然看到的就一直是空白页面哦」

如何实现懒加载功能

懒加载功能可以很好的提升用户的体验感,懒加载是指只有屏幕可视区的内容被加载,其它不可视区的内容等待加载,当用户滑动到页面底部时会发送一个请求请求更多资源,它又被称为分页功能,这样极大的节省了为了一次性加载全部内容和图片的超出时间

案例如下:
在这里插入图片描述

「这里为了方便大家观察懒加载功能,特意调成了3G网,大家实际生活中几乎都是4G或5G网,因此加载的时间并不会这么长,顶多一眨眼的时间或者你根本观察不到变化」

实现懒加载功能的基本思路是当页面滑动到最底端时,我需要等待一会把后面的内容加载出来,它一般用于数据内容比较多时,也称分页功能。首先你需要装一个插件,在vscode里你可以执行下面命令行

npm i react-lazyload -S

我们需要加上共用组件,当你需要懒加载功能时,直接复制下面代码即可

import React from 'react';
import styled, { keyframes } from 'styled-components';
import style from '../../asset/global-style/global-style';

const loading = keyframes`
  0%, 100% {
    transform: scale(0.0);
  }
  50% {
    transform: scale(1.0);
  }
`
const LoadingWrapper = styled.div`
    >div {
      position: absolute;
      top: 0; left: 0; right: 0; bottom: 0;
      margin: auto;
      width: 60px;
      height: 60px;
      opacity: 0.6;
      border-radius: 50%;
      background-color: ${style["theme-color"]};
      animation: ${loading} 1.4s infinite ease-in;
    }
    >div:nth-child(2) {
      animation-delay: -0.7s;
    }
`

function Loading()  {
  return (
    <LoadingWrapper>
      <div></div>
      <div></div>
    </LoadingWrapper>
  );
}
 
export default React.memo(Loading);

这里我使用了 styled-components 在js文件里导出css样式,不了解的同学可以直接复制,也可以把它们写在css文件中,效果是一样的

这是上面所需要的global-style文件

export default {
    "theme-color": "#d44439",
    "font-size-ss": "10px",
    "font-size-s": "12px",
    "font-size-m": "14px",
    "font-size-l": "16px",
    "font-size-ll": "18px",
}

由于懒加载功能是伴随着滚动产生的,因此我们还需要添加手机端滚动组件,由于它也是一个共用组件,我们把它放在baseUI文件夹中,每个页面都可以使用它

import React, { forwardRef, useState,useEffect, useRef, useImperativeHandle, useMemo } from "react"
import PropTypes from "prop-types"
import BScroll from "better-scroll"
import styled from 'styled-components';
import Loading from '../../baseUI/loading/index'; 
import { debounce } from "../../api/utils";

const ScrollContainer = styled.div`
  width: 100%;
  height: 100%;
  overflow: hidden;
`

const PullUpLoading = styled.div`
  position: absolute;
  left:0; right:0;
  bottom: 5px;
  width: 60px;
  height: 60px;
  margin: auto;
  z-index: 100;
`

export const PullDownLoading = styled.div`
  position: absolute;
  left:0; right:0;
  top: 0px;
  height: 30px;
  margin: auto;
  z-index: 100;
` 
const Scroll = forwardRef((props, ref) => {
  const [bScroll, setBScroll] = useState();

  const scrollContaninerRef = useRef();

  const { direction, click, refresh, pullUpLoading, pullDownLoading, bounceTop, bounceBottom } = props;

  const { pullUp, pullDown, onScroll } = props;

  let pullUpDebounce = useMemo(() => {
    return debounce(pullUp, 500)
  }, [pullUp]);

  let pullDownDebounce = useMemo(() => {
    return debounce(pullDown, 500)
  }, [pullDown]);

  useEffect(() => {
    const scroll = new BScroll(scrollContaninerRef.current, {
      scrollX: direction === "horizental",
      scrollY: direction === "vertical",
      probeType: 3,
      click: click,
      bounce:{
        top: bounceTop,
        bottom: bounceBottom
      }
    });
    setBScroll(scroll);
    return () => {
      setBScroll(null);
    }
    // eslint-disable-next-line
  }, []);

  useEffect(() => {
    if(!bScroll || !onScroll) return;
    bScroll.on('scroll', onScroll)
    return () => {
      bScroll.off('scroll', onScroll);
    }
  }, [onScroll, bScroll]);

  useEffect(() => {
    if(!bScroll || !pullUp) return;
    const handlePullUp = () => {
      //判断是否滑动到了底部
      if(bScroll.y <= bScroll.maxScrollY + 100){
        pullUpDebounce();
      }
    };
    bScroll.on('scrollEnd', handlePullUp);
    return () => {
      bScroll.off('scrollEnd', handlePullUp);
    }
  }, [pullUp, pullUpDebounce, bScroll]);

  useEffect(() => {
    if(!bScroll || !pullDown) return;
    const handlePullDown = (pos) => {
      //判断用户的下拉动作
      if(pos.y > 50) {
        pullDownDebounce();
      }
    };
    bScroll.on('touchEnd', handlePullDown);
    return () => {
      bScroll.off('touchEnd', handlePullDown);
    }
  }, [pullDown, pullDownDebounce, bScroll]);


  useEffect(() => {
    if(refresh && bScroll){
      bScroll.refresh();
    }
  });

  useImperativeHandle(ref, () => ({
    refresh() {
      if(bScroll) {
        bScroll.refresh();
        bScroll.scrollTo(0, 0);
      }
    },
    getBScroll() {
      if(bScroll) {
        return bScroll;
      }
    }
  }));

  const PullUpdisplayStyle = pullUpLoading ? { display: "" } : { display: "none" };
  const PullDowndisplayStyle = pullDownLoading ? { display: "" } : { display: "none" };
  return (
    <ScrollContainer ref={scrollContaninerRef}>
      {props.children}
      {/* 滑到底部加载动画 */}
      <PullUpLoading style={ PullUpdisplayStyle }><Loading></Loading></PullUpLoading>
      {/* 顶部下拉刷新动画 */}
      <PullDownLoading style={ PullDowndisplayStyle }><Loading></Loading></PullDownLoading>
    </ScrollContainer>
  );
})

Scroll.defaultProps = {
  direction: "vertical",
  click: true,
  refresh: true,
  onScroll:null,
  pullUpLoading: false,
  pullDownLoading: false,
  pullUp: null,
  pullDown: null,
  bounceTop: true,
  bounceBottom: true
};

Scroll.propTypes = {
  direction: PropTypes.oneOf(['vertical', 'horizental']),
  refresh: PropTypes.bool,
  onScroll: PropTypes.func,
  pullUp: PropTypes.func,
  pullDown: PropTypes.func,
  pullUpLoading: PropTypes.bool,
  pullDownLoading: PropTypes.bool,
  bounceTop: PropTypes.bool,//是否支持向上吸顶
  bounceBottom: PropTypes.bool//是否支持向下吸顶
};

export default Scroll;

你需要执行以下命令行:

npm i prop-types better-scroll styled-components -S

完整代码

import React, { useEffect } from "react";
import Path from "./learn-path/Path";
import Direction from "./lesson-direction/Direction";
import AllLessons from "./allLessons/allLessons";
import { Tab, Content, EnterLoading } from "./index.style";
import { connect } from "react-redux";
import * as actionTypes from "./store/actions";
import { renderRoutes } from "react-router-config";
import { forceCheck } from "react-lazyload";
import Scroll from "../../../components/scroll/Scroll";
import Loading from "../../../baseUI/loading/index";

function Lessons(props) {
    const {
        route,
        studyPath,
        lessonsDirection,
        allLessons,
        getLessonsListDataDispatch,
    } = props;
    const {
        enterLoading,
        pullUpLoading,
        pullDownLoading,
        pullUpRefresh,
        pageCount,
        pullDownRefresh, 
    } = props;

    useEffect(() => {
        if (!studyPath.length) {
            getLessonsListDataDispatch();
        }
    }, []);
    const handlePullUp = () => {
        pullUpRefresh(allLessons, pageCount);
    };

    const handlePullDown = () => {
        pullDownRefresh(allLessons, pageCount);
    }; 
    return (
        <>
            {renderRoutes(route.routes)}
            <Content>
                <Scroll
                    onScroll={forceCheck}
                    pullUp={handlePullUp}
                    pullDown={handlePullDown}
                    pullUpLoading={pullUpLoading}
                    pullDownLoading={pullDownLoading}
                >
                    <Tab>
                        <Path data={studyPath} />
                        <Direction data={lessonsDirection} />
                        <AllLessons data={allLessons} path={route} />
                    </Tab>
                    {/* 入场加载动画 */}
                    {enterLoading ? (
                        <EnterLoading>
                            <Loading></Loading>
                        </EnterLoading>
                    ) : null}
                </Scroll>
            </Content>
        </>
    );
}

export default connect(
    function mapStateToProps(state) {
        return {
            studyPath: state.lessons.studyPath,
            lessonsDirection: state.lessons.lessonsDirection,
            allLessons: state.lessons.allLessons,
            enterLoading: state.lessons.enterLoading,
            pullUpLoading: state.lessons.pullUpLoading,
            pageCount: state.lessons.listOffset,
            pullDownLoading: state.lessons.pullDownLoading,
        };
    },
    function mapDispatchToProps(dispatch) {
        return {
            getLessonsListDataDispatch() {
                dispatch(actionTypes.getLessonsList());
            },
            pullUpRefresh() {
                dispatch(actionTypes.changePullUpLoading(true));
                dispatch(actionTypes.refreshMoreLessonsInfoRequest());
            },
            //顶部下拉刷新
            pullDownRefresh() {
                dispatch(actionTypes.getLessonsList());
                dispatch(actionTypes.changePullDownLoading(true));
            },
        };
    }
)(Lessons);

你可能会好奇,Content 组件是什么?在这里我要强调一下,better-scroll 的原理并不复杂,就是在容器元素高度固定,当子元素高度超过容器元素高度时,通过 transfrom 动画产生滑动效果,因此它的使用原则就是外部容器必须是固定高度,不然没法滚动。而 Content 就是这个外部容器。
因此我们在style.js文件中添加以下代码

import styled from'styled-components';

export const Content = styled.div`
  position: fixed;
  top: 90px;
  bottom: 0;
  width: 100%;
`

CSSTransition 动画的实现

CSSTransition 效果可以说给我们做的项目锦上添花了,为了使我们做的App受大众喜爱,不来点动感怎么行呢

话不多说,我们直接上效果图
在这里插入图片描述

不知道老铁们有没有发现,每当我切换到下一个页面时,页面似乎是“弯着腰”进来的,在这里我们使用的就是CSSTransition效果了,相信当老铁们学会了CSSTransition效果后还能做出更花里胡哨的页面hhhhh

插件安装

npm i react-transition-group -S

使用

import React, { useState, useEffect } from "react";
import { NavLink } from "react-router-dom";
import { Container, Head, Content, Background } from "./style";
import Scroll from "../../../../components/scroll/Scroll";
import TabHead from "./head/Head";
import Nav from "./Nav/index";
import { renderRoutes } from "react-router-config";
import { CSSTransition } from "react-transition-group";
import Foot from "./foot/index";

function Page({ route }) {
  const [showMessage, setShowMessage] = useState(false);
  useEffect(() => {
    setShowMessage(true);
  }, []);
  return (
    <CSSTransition
      in={showMessage}
      timeout={300}
      classNames="fly"
      unmountOnExit
    >
      <Background>
        {renderRoutes(route.routes)}
        <Head>
          <NavLink to="/lecture/lessons">
            <svg width="42" height="42" style={{ marginLeft: "-2vw" }}>
              <polyline
                points="25, 13 16, 21 25, 29"
                stroke="#ccc"
                strokeWidth="2"
                fill="none"
              />
            </svg>
          </NavLink>
          <img src="/asserts/star.png" className="star" />
          <img src="asserts/send.png" />
        </Head>

        <Content>
          <Scroll>
            <Container>
              <TabHead />
              <Nav route={route} />
            </Container>
          </Scroll>
        </Content>
        <Foot />
      </Background>
    </CSSTransition>
  );
}
export default Page;

对应的style.js

import styled from "styled-components";

export const Container = styled.div`
  background-color: rgb(245, 245, 245);
  transform-origin: right bottom;
  font-family: PingFang SC, Lantinghei SC, Microsoft Yahei, Hiragino Sans GB,
    Microsoft Sans Serif, WenQuanYi Micro Hei, Helvetica, sans-serif;
`;
export const Background = styled.div`
  position: fixed;
  top: 0;
  z-index: 200;
  width: 100vw;
  background-color: red;
  z-index: 22;
  height: 100vh;
  &.fly-enter,
  &.fly-appear {
    /* transform-origin: 100% 100%;
    transform: rotateZ(90deg);  */
    transform: rotateZ(30deg) translate3d(100%, 0, 0);
  }
  &.fly-enter-active,
  &.fly-appear-active {
    /* transform-origin: 100% 100%; 
    transition: all .3s;
    transform: rotateZ(0deg);  */
    transition: transform .3s;
    transform: rotateZ(0deg) translate3d(0, 0, 0);
  }
  &.fly-exit {
    /* transform-origin: 100% 100%; 
    transform: rotateZ(0deg);  */
    transform: rotateZ(0deg) translate3d(0, 0, 0);
  }
  &.fly-exit-active {
    /* transform-origin: 100% 100%; 
    transition: all .3s;
    transform: rotateZ(90deg); */
    transition: transform .3s;
    transform: rotateZ(30deg) translate3d(100%, 0, 0);
    
  }
`;
export const Head = styled.div`
  position: fixed;
  z-index: 100;
  top: 0;
  left: 0;
  width: 100%;
  padding: 0 15px;
  display: flex;
  align-items: center;
  background: #fff;
  & > img {
    width: 8vw;
    height: 4vh;
    margin-top: 1vh;
    margin-right: 2vw;
  }
  & > .star {
    margin-left: 65vw;
  }
`;
export const Content = styled.div`
  position: fixed;
  top: 48px;
  bottom: 15px;
  width: 100%;
  z-index: 100;
`;

在这里我们需要注意以下几点:

  1. 下一个页面默认是不出现的state = false,为了动态改变状态,我使用了React最近新出的技术栈「React hooks」,有了React hooks,从此我们在函数是组件里面也能拥有生命周期啦。好了回归正题,我们把默认状态储存起来后,只有当我点击上一个页面时(上一个页面肯定需要一个NavLink),setState(true)状态改变,CSSTransition效果就出现了
  2. 我们想让哪个页面有过渡效果就把过渡样式赋给那个页面
  3. CSSTransition 内部 in 行内样式值对应的是true or false,表示CSSTransition效果添加还是不添加,并且timeout时间要和在style.js里面写的时间相同
  4. CSSTransition 的类名可以随便取,但是样式文件里书写样式的时候 enter, appear,enter-active, appear-active…类名是固定不变的
  5. 初次使用CSSTransition效果时可能会出现,我明明都已经配置好了,为什么它会报下面的错?

    原因是在CSSTransition组件内部我们只能有一个Children,也就是说我们需要用一个大的div把里面的所有组件都包起来,在楼上代码中我们把注释掉就会报这个错

以上就是CSSTransition组件的基本使用方法,老铁们想要了解更多内容可以直接查看文档,链接参上http://reactcommunity.org/react-transition-group/css-transition

结语

「哈希路由配置到此就阐述完毕啦,另外我们还介绍了其他有点内涵的东西,小编目前仍在完善极客时间App,有兴趣的同学可以关注https://github.com/Siwen19
需要完整代码的同学也可以查看https://github.com/2941187216/Geek

致读者

  1. 读到这里首先要感谢大家,同时希望大家点个赞哦 : ),有👍 有动力

  2. 代码都收录在 Github 感谢Star✨

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值