博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
让react用起来更得心应手——(react-router原理简析)
阅读量:5812 次
发布时间:2019-06-18

本文共 10262 字,大约阅读时间需要 34 分钟。

让react用起来更得心应手系列文章:

前端路由和后台路由

在刚入行的时候一直明白什么单页面应用是什么,说白了就是混淆了前台路由和后台路由,现在来缕缕它们:

  1. 前台路由:页面的显示由前台js控制,在url的路径中输入哈希值是不会往后台发送请求的,所以前台可以通过将哈希和页面进行映射从而控制渲染显示哪个页面。
  2. 后台路由:页面的显示由后台根据url进行处理然后返回给浏览器,非哈希url都会往服务器发送请求(historyAPI也不会发送请求,后面会介绍)

如果还不理解,那么可以用express搭建本地服务器看看效果(ps:为什么用express,因为懒,koa的话还得下载koa-router插件):

var express = require('express');var app = express();app.get('/', function (req, res) {    res.send('welcome to home'); }) app.get('/a', function (req, res) {   res.send('welcome to a');})var server = app.listen(8081)复制代码

在浏览器中输入localhost:8081/

在浏览器中输入localhost:8081/#a
在浏览器中输入localhost:8081/a
结合图片和上面的陈述应该知道前端路由和后台路由的区别,除了hash路由还有一种方法可以修改url并且不向后台发送请求,它是history.pushState(),注意兼容处理:
但是这种方法有一个问题,如果再按一次回车键,它是会向后台发送请求的,如果后台路由没有相应的匹配,那么会报404的错误,一般需要后台做处理。

var express = require('express');var app = express();app.get('/', function (req, res) {    res.send('welcome to home'); }) var server = app.listen(8081)复制代码

router的核心

hash路由

主要是监听hashchange事件,然后再获取数据重新渲染页面

    
Document pageALink pageBLink 复制代码

history.push实现路由

    
Document pageALink pageBLink 复制代码

react-router中原理分析

react-router的基础构成

  1. BrowserRouter或hashRouter用来渲染Router所代表的组件
  2. Route用来匹配组件路径并且筛选需要渲染的组件
  3. Switch用来筛选需要渲染的唯一组件
  4. Link直接渲染某个页面组件
  5. Redirect类似于Link,在没有Route匹配成功时触发
import BrowserRouter from './BrowserRouter';import Route from './Route';import Link from './Link';import Switch from './Switch';import Redirect from './Redirect';export {  BrowserRouter,  Route,  Link,  Switch,  Redirect}复制代码

BrowserRouter

import React from 'react';import ReactDOM,{render} from 'react-dom';import Home from './components/Home.js';import User from './components/User.js';import {BrowserRouter as Router,Route} from './react-router-dom'render(
,window.root);复制代码

从上面的用法,可以知道BrowserRouter其实是一个组件,它有以下功能:

  1. 保存当前的访问的路径,当路径变化时会重新渲染Route所代表的组件
  2. 监听popstate,路径变化时会修改state保存新的访问路径,从而重新渲染Route代表的组件
  3. 提供修改url和state的方法,供内部嵌入的组件使用,从而触发页面重新渲染
import React from 'react';import {Provider} from './context';// // 想染路径变化 刷新组件 路径定义在状态中 路径变化就更新状态export default class BrowserRouter extends React.Component{  state = {    // 获取打开网页时的默认路径    location:{      pathname: window.location.pathname || '/',    }  }      componentWillMount(){    window.addEventListener('popstate',()=>{      let pathname = window.location.pathname;      this.handleChangeState(pathname);    },false);  }    //当浏览器的路由改变时触发,改变state从而重新渲染组件  handleChangeState(pathname){    this.setState({      location:{        ...this.state.location,        pathname      }    })  }    // 渲染Route,  render(){     let that = this;    let value = {      ...this.state,      history:{        push(pathname){          // 这个方法主要是提供给Link使用的          // 当点击Link时,会改变浏览器url并且重新渲染组件          window.history.pushState({},null,pathname);          that.handleChangeState(pathname);        }      }    }    return(     
{this.props.children} //嵌入的Route组件
) }}复制代码

Route

Route主要将所代表组件的path和当前的url(state.pathname)进行匹配,如果匹配成功则返回其代表的组件,那么就会渲染其代表的组件,否则返回null。

import React from 'react';import ReactDOM,{render} from 'react-dom';import Home from './components/Home.js';import User from './components/User.js';import {BrowserRouter as Router,Route} from './react-router-dom'render(
首页 /*由于Link不会有点击后的样式变化,所以通常使用下面这用方法自定义link*/
{ return
  • 用户
  • } }
    /*采用render参数会执行对应的函数*/
    { return
    }}/>
    ,window.root);复制代码
    import React from 'react';import {Consumer} from './context';// 路径转化成正则,在另一篇文章【koa会用也会写——(koa-router)】可以找到其原理import pathToRegExp from 'path-to-regexp';// 不是通过Route渲染出来的组件没有match、location、history三个属性export default class Route extends React.Component{  render(){    return 
    {(value)=>{ // BrowserRouter中state.pathname和浏览器url一致 let {pathname} = value.location; // Route组件上的参数 let {path='/',component:Component,render,children,exact=false} = this.props; //用来保存匹配路径的参数键值 /user/:name/:id => [name,id] let keys = []; //将Route的path参数转化为正则表达式 let reg = pathToRegExp(path,keys,{end:exact}); if(reg.test(pathname)){ let result = pathname.match(reg); let match = {} // 将获取路径参数exp:{id:xxx,name:xxx} if(result){ let [,...arr] = result; match.params = keys.reduce((memo,next,idx)=>{ memo[keys[idx].name]=arr[idx] return memo; },{}); } // 将匹配路径的参数和原来的参数合并传给Route代表的组件 let props = { ...value,match } // component直接渲染组件 // render执行render(props) // children不管是否匹配都会执行children(props) if(Component){ return
    }else if(render){ return render(props); }else if(children){ return render(props); } }else{ // children 不管是否匹配到都会 if(children){ return render(props); } return null //Route的路径不匹配返回null,不渲染Route代表的组件 } }}
    }}复制代码

    Switch

    Switch组件其实就是包装在Route外面的一层组件,它会对Route进行筛选后返回唯一Route,如果 没有Switch的话,可以渲染多个Route代表的组件

    import React from 'react';import ReactDOM,{render} from 'react-dom';import Home from './components/Home.js';import User from './components/User.js';import Article from './components/Article';import {BrowserRouter as Router,Route,Switch} from './react-router-dom'render(
    ,window.root);复制代码
    import React from 'react';import {Consumer} from './context';import pathToRegExp from 'path-to-regexp';export default class Switch extends React.Component{  render(){    return 
    {(value)=>{ // BrowserRouter中state.pathname和浏览器url一致 let pathname = value.location.pathname; // 将Route的path对url进行匹配,匹配成功返回唯一的Route React.Children.forEach(this.props.children,(child)=>{ let {path='/',exact=false} = child.props; let reg = pathToRegExp(path,[],{end:exact}); if(reg.test(pathname)){ return child } }) }}
    }}复制代码

    Redirect

    对于没有匹配到的Route会默认重定向渲染Redirect,其实就是直接改变url和BrowserRouter中state.pathname导致重新渲染组件

    import React from 'react';import ReactDOM,{render} from 'react-dom';import Home from './components/Home.js';import User from './components/User.js';import Article from './components/Article';import {BrowserRouter as Router,Route,Link,Switch,Redirect} from './react-router-dom'render(
    ,window.root);复制代码
    import React from 'react';import {Consumer} from './context';export default class Redirect extends React.Component{  render(){    return 
    {({
    history})=>{ //修改url,重新渲染组件 history.push(this.props.to); return null }}
    }}复制代码

    Link

    和Redirect组件类似,区别在于Redirect直接调用context上面的方法修改url,而Link需要点击触发调用context上面的方法

    import React from 'react';import ReactDOM,{render} from 'react-dom';import Home from './components/Home.js';import User from './components/User.js';import Article from './components/Article';import {BrowserRouter as Router,Route,Link,Switch,Redirect} from './react-router-dom'render(
    首页
    用户
    ,window.root);复制代码
    import React from 'react';import {Consumer} from './context';export default class Link extends React.Component{  render(){    return 
    {({
    history})=>{ //点击触发回调用,修改url,重新渲染组件 return
    { history.push(this.props.to) }}>{this.props.children} }}
    }}复制代码

    withRoute

    不是通过Route渲染出来的组件没有match、location、history三个属性,但是又想要使用这三个属性,那该怎么办呢,所以可以在外面套一层Route组件,从而得到这三个属性,这种做法叫高阶组件。

    import React from 'react';import Route from './Route'let withRouter = (Component) =>{  return ()=>{    return 
    }}export default withRouter;复制代码
    import React, { Component } from 'react';import {withRouter} from 'react-router-dom';class withRouterLink extends Component {  change = ()=>{   this.props.history.push('/withRouterLink') // url变化,组件的跳转  }  render() {    return (      
    withRouter
    ) }}// 高阶组件export default withRouter(Logo)复制代码
    import React from 'react';import ReactDOM,{render} from 'react-dom';import Home from './components/Home.js';import User from './components/User.js';import Article from './components/Article.js';import withRouterLink from './components/withRouterLink.js';import {BrowserRouter as Router,Route,Link,Switch,Redirect} from './react-router-dom'render(
    首页
    用户
    ,window.root);复制代码

    登陆拦截和登陆重回

    一般网页都会有登陆注册功能,如果没有登陆,很多页面是访问受限的,登陆之后又会跳转到原页面。

    import Index from './pages/index.js';import Protected from './pages/Protected'export default class App extends Component {  render() {    return (      
    ) }}复制代码
    import React, { Component } from 'react'import {Route,Redirect} from 'react-router-dom'export default class Protected extends Component {  render() {       let login = localStorage.getItem('login');        // this.props里面有 path 有component    //如果用户没有登录重定向到登录页    return login?
    :
    }}复制代码
    import React, { Component } from 'react'export default class Login extends Component {  render() {    console.log(this.props)    return (      
    ) }}复制代码

    结语

    个人使用一种框架时总有一种想知道为啥这样用的强迫症,不然用框架用的不舒服,不要求从源码上知道其原理,但是必须得从心理上说服自己。

    转载地址:http://nitbx.baihongyu.com/

    你可能感兴趣的文章
    +++++++子域授权与编译安装(一)
    查看>>
    编程修养(七)
    查看>>
    asp.net怎样在URL中使用中文、空格、特殊字符
    查看>>
    ISA2006实战系列之二:实战ISA三种客户端部署方案(下)
    查看>>
    Linux后门入侵检测工具,附bash漏洞最终解决方法
    查看>>
    ASA5585-S20测试方案
    查看>>
    利用for循环打印实心棱形和空心棱形
    查看>>
    路由器发布服务器
    查看>>
    实现跨交换机VLAN间的通信
    查看>>
    oracle import & export 操作相关脚本
    查看>>
    LVS集群的体系结构,构建强壮的体系结构里负载均衡层、真实服务器层、后端共享存储层都是相辅相成...
    查看>>
    将用户资料传至海外,俄罗斯封锁LinkedIn
    查看>>
    为了保护隐私 你发布图片的人脸可能被打马赛克
    查看>>
    近六成用户不知免费WiFi有风险
    查看>>
    6Gbps!无线网络传输速度再破纪录
    查看>>
    OA未来情归何处?还能否一统江湖?
    查看>>
    评论:提高违法成本保护信息安全
    查看>>
    IBM发布全新相变存储技术:跟卡顿永别
    查看>>
    2016~2045新科技趋势报告发布 十大技术值得ICT产业关注
    查看>>
    开启视频会议享受快乐的商务办公
    查看>>