07-06-React-Router的单页应用

课程目标

  • 理解前端路由的作用;
  • 掌握 React-Router 各 API 使用细节;
  • 可根据项目需求,在 React 项目中,组织合理的路由方案。

课程内容

路由

  • 路由:根据不同的 url 规则,给用户展示不同的视图(页面);
  • 当应用变得复杂的时候,就需要分块的进行处理和展示,传统模式下,我们是把整个应用分成了多个页面,然后通过 url 进行连接。但是这种方式也有一些问题,每次切换页面都需要重新发送所有请求和渲染整个页面,不止性能上会有影响,同时也会导致整个 JavaScript 重新执行,丢失状态。

SPA

  • Single Page Application:单页面应用,整个应用只加载一个页面(入口页面),后续在与用户的交互过程中,通过 DOM 操作在这个单页上动态生成结构与内容。
优点
  • 有更好的用户体验(减少请求、渲染和页面跳转产生的等待与空白),页面切换快;
  • 重前端,数据和页面内容由异步请求(AJAX)+ DOM 操作来完成,前端处理更多的业务逻辑。
缺点
  • 首次进入慢;
  • 不利于 SEO。

SPA 的页面切换机制

  • 虽然 SPA 的内容都是在一个页面通过 JavaScript 动态处理的,但是还是需要根据需求在不同的情况下区分内容展示,如果仅仅只是依靠 JavaScript 内部机制去判断,逻辑会变得过于复杂,通过把 JavaScript 与 URL 进行结合的方式:JavaScript 根据 URL 的变化,来处理不同的逻辑,交互过程中只需要改变 URL 即可。这样把不同的 URL 与 JavaScript 对应的逻辑进行关联的方式就是路由,其本质上与后端路由的思想是一样的。

前端路由

  • 前端路由只是改变了 URL 或 URL 中的某一部分,但一定不会直接发送请求,可以认为仅仅只是改变了浏览器地址栏上的 URL 而已,JavaScript 通过各种手段处理这种 URL 的变化,然后通过 DOM 操作来动态的改变当前页面的结构;
  • URL 的变化不会直接发送 HTTP 请求;
  • 业务逻辑由前端 JavaScript 来完成。
目前前端路由的主要模式
  • 基于 URL Hash 的路由;
  • 基于 HTML5 History API 的路由。

React Router

  • 理解了路由的基本机制以后,也不需要重复造轮子,我们可以直接使用 React Router 库;
  • React Router 提供了多种不同环境下的路由库:web、native;
  • 官网:https://reactrouter.com/。

基于 Web 的 React Router

  • 基于 web 的 React Router 为:react-router-dom;
  • 安装:npm i -S react-router-dom。

路由模式

BrowserRouter 组件 -- history
  • 基于 HTML5 History API 的路由组件。
HashRouter 组件 -- hash
  • 基于 URL Hash 的路由组件。
功能组件
  • Route 组件:
    • 匹配规则(v6 之前):
      • 默认模糊匹配,当前 URL 以该 path 为开始时,则匹配成功;
      • exact:精确匹配,URL===path || URL===path/;
      • strict:严格匹配,URL===path,要注意 strict 必须与精确匹配一起使用才生效;
      • 多路径匹配:通过数组实现;
      • 动态路由,见最下面。
    • 渲染视图:
      • component;
      • render。
    import { Route } from 'react-router-dom';
    import About from './views/About';
    import Hire from './views/Hire';
    import Home from './views/Home';
    /**
     * 匹配规则:
     * 1、默认情况-——模糊匹配,当前 URL 以该 path 为开始时,就能匹配:/index、/index/、/index/xxx;
     * 2、exact——精确匹配,URL===path || URL===path/;
     * 3、strict——严格匹配,URL===path,但是需要与 exact 一起使用才生效;
     * 4、多路径匹配——通过数组匹配。
     */
    
    function App() {
      // 当有值需要传递给对应路由组件时,可以通过 render 属性实现。
      const user = {
        name: 'yjw',
        age: 18
      }
      return (  
        <>
          
    React Router Page
    { return }} /> ); } export default App;
    // react-router 6 之后的使用方式
    import { Routes, Route } from 'react-router-dom';
    import About from './views/About';
    import Hire from './views/Hire';
    import Home from './views/Home';
    
    function App() {
      return (  
        <>
          
            {/* 默认 精确匹配,严格匹配 */}
            } /> 
            } />
            } />
          
        
      );
    }
    
    export default App;
    
  • 链接组件:
    • Link;
    • NavLink:
      • activeClassName;
      • activeStyle;
      • isActive: function。
    // 可以使用 a 标签实现,但是页面每次都会刷新
    export default () => {
      return <>
        首页
         | 
        关于
         | 
        加入
      
    }
    
    import { Link, NavLink } from "react-router-dom"
    /**
     * 应用内链接:Link 或者 NavLink
     * 应用外链接:a 标签
     */
    /**
     * NavLink 用于导航的链接制作
     * - 当当前的 url 和 NavLink 的 to 属性匹配后,则会给当前的标签加一个选中状态,注:NavLink 默认情况下也是模糊匹配;
     * - activeClassName:当前项被选中后的 className,默认为 active;
     * - activeStyle:当前项被选中后的 style;
     * - isActive:function,返回一个 Boolean 值,表示该标签的 class、style 始终是 active 状态或者 非active 状态。
     */
    export default () => {
      return 
    首页 | 关于 | {return true}} activeStyle={{color: 'yellow'}}>加入 | 百度
    }
  • Switch 组件:只匹配一个路径;
  • Redirect 组件:当输入的 url 不合法时,可以重定向到 404:
    • form 属性;
    • to 属性。
    import { Route, Switch, Redirect } from 'react-router-dom';
    import Nav from './Nav';
    import About from './views/About';
    import Hire from './views/Hire';
    import Home from './views/Home';
    import View404 from './views/404.js';
    
    function App() {
      // 当有值需要传递给对应路由组件时,可以通过 render 属性实现。
      const user = {
        name: 'yjw',
        age: 18
      }
      return (  
        <>
          
    React Router Page

路由参数

  • 通过 props,可以获取 history、location、match 和 staticContext。
history
  • action:“PUSH” || “POP” || “REPLACE”;
    • “PUSH”:应用内通过连接跳转到当前视图的,或者通过 push 方法跳转到当前视图;
    • “POP”:直接输入地址跳转到当前应用,或者从外部链接跳转进来的;
    • “REPLACE”:通过重定向跳转或者通过 replace 方法跳转;
  • go:function,go(n)——跳转历史记录n步;
  • goBack:function,goBack()——返回历史记录上一步;
  • goForward:function,goForward()——前进历史记录下一步;
  • length:当前源在历史记录中记录的条目数;
  • push:function,push(path[,state])——跳转视图,向历史记录中,添加新的一条记录,从而影响视图;state 的值其实就是修改 location 下面的 state 的值,同 replace 方法中的 state;
  • replace:function,replace(path[,state])——跳转视图,替换掉历史记录中当前这条;
  • block:当离开当前组件时,会弹窗提示;全局函数,如果只需要在当前组件进行弹窗提示,在当前组件即将卸载时,调用该方法的返回值进行移除;
  • createHref:function,createHref(location),当 url 比较复杂时,比如含有参数和hash,这时可以使用该方法,返回一个 url 地址,但是需要再调用 push、replace 来进行跳转;
  • listen:监听 url 跳转,如果跳转了会打印 location和action,同样是全局函数。
location:
  • hash:hash 值,url 中 # 后面的内容;
  • pathname:当前的 url,不包含参数和hash;
  • search:当前的 search 值,? 后面的内容;
  • state:undefined、push 或 replace 传递的信息。
match:匹配信息
  • isExact:boolean,和 Route 中配置没有关系,取决于当前 path 和 url 是否能精确匹配;
  • params:{} 动态路由的参数;
  • path:和 pathname 不是一个概念,这里的 path 是当前 route 的 path 值;
  • url:当前 url 中,被当前 path 匹配成功的部分。

路由信息获取

高阶路由 - withRouter
  • 非路由组件获取路由信息
    import { NavLink, withRouter } from "react-router-dom";
    
    function SubNav(props) {
      console.log('SubNav: ', props);
      return (  
        
    { console.log('arg: ', args) return true; }} to='/list/all' >全部 精华 分享 问答
    ); } const Nav = withRouter(SubNav); export default Nav;
路由 Hooks,v5 版本之后引入了 Hooks。
  • useLocation();
  • useHistory();
  • useRouteMatch();
  • useParams():获取动态路由的参数信息。
  • 以下为完整练习代码:
    // App.js
    import { Route, Switch, Redirect } from 'react-router-dom';
    import Nav from './Nav';
    import About from './views/About';
    import Hire from './views/Hire';
    import Home from './views/Home';
    import View404 from './views/404.js';
    import List from './views/list/List';
    import './index.css';
    
    const types = ['good', 'good', 'share', 'ask'];
    
    function App() {
      // 当有值需要传递给对应路由组件时,可以通过 render 属性实现。
      const user = {
        name: 'yjw',
        age: 18
      }
      return (  
        
    React Router Page
    ); } /** * list 的路径问题: * - /list * - /list/分类 * - list/分类/页码 */ export default App;
    // Nav.js
    import { Link, NavLink } from "react-router-dom"
    /**
     * 应用内链接:Link 或者 NavLink
     * 应用外链接:a 标签
     */
    /**
     * NavLink 用于导航的链接制作
     * - 当当前的 url 和 NavLink 的 to 属性匹配后,则会给当前的标签加一个选中状态,注:NavLink 默认情况下也是模糊匹配;
     * - activeClassName:当前项被选中后的 className,默认为 active;
     * - activeStyle:当前项被选中后的 style;
     * - isActive:function,返回一个 Boolean 值,表示该标签的 class、style 始终是 active 状态或者 非active 状态。
     */
    export default () => {
      return 
    首页 | 关于 | 加入 | 产品列表
    }
    // List.js
    import ListList from "./ListList";
    import Pagination from "./Pagination";
    import SubNav from "./SubNav";
    
    function List(props) {
      return (  
        <>
          

    List

    ); } export default List;
    // SubNav.js
    import { NavLink, useParams } from "react-router-dom";
    
    function SubNav(props) {
      const { type='good' } = useParams();
      return (  
        
    精华 分享 问答
    ); } export default SubNav;
    // Pagination.js
    import { useParams } from "react-router";
    import { Link } from "react-router-dom";
    import data from './data';
    const limit = 6;
    
    function Pagination() {
      const { type='good', page='1' } = useParams();
      const nowData = data[type];
      const pageLen = Math.ceil(nowData.length/limit);
    
      const renderPage = () => {
        const inner = []
        for (let i = 1; i <= pageLen; i++) {
          if(pageLen === Number(page)) {
            inner.push({i})
          } else {
            inner.push({i})
          }
        }
        return inner;
      }
      return (  
        
    {renderPage()}
    ); } export default Pagination;
    // ListList.js
    import { useParams } from "react-router";
    import data from './data';
    const limit = 6;
    
    /**
     * 求页数
     * 每页的第一条:(page-1) * 6
     */
    function ListList() {
      const { type='good', page='1' } = useParams();
      const nowPage = Number(page);
      const start = (nowPage - 1) * limit;
      const end = nowPage * limit;
      const nowData = data[type]?.filter((item, index)=>(index>=start && index<=end));
      
      return ( 
        
      {nowData?.map(d => { return
    • {d.title}
    • })}
    ); } export default ListList;

你可能感兴趣的:(07-06-React-Router的单页应用)