美文网首页
前端路由原理和React Router

前端路由原理和React Router

作者: forever_提拉米苏 | 来源:发表于2021-06-16 17:24 被阅读0次

    前端路由原理

    前端三大框架 Angular、React、Vue ,它们的路由解决方案 angular/router、react-router、vue-router 都是基于前端路由原理进行封装实现的,因此将前端路由原理进行了解和掌握是很有必要的。

    路由的概念起源于服务端,在以前前后端不分离的时候,由后端来控制路由。但由于后端路由还存在着自己的不足,前端路由才有了发展空间。路由的映射函数通常是进行一些 DOM 的显示和隐藏操作,当访问不同的路径的时候,会显示不同的页面组件。前端路由主要有两种实现方案:Hash和History。

    Hash模式

    hash模式就是基于 location.hash 来实现的,原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如https://www.xxxxxx.com#search的 location.hash 的值为 '#search'

    hash 有下面几个特性:

    • URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送。
    • hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换。
    • 我们可以使用 hashchange 事件来监听 hash 的变化。

    那么如何来触发hash改变(hashchange 事件)呢?

    1. 通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 就会发生改变,也就会触发 hashchange 事件了:
    <a href="#search">search</a>
    
    1. 直接使用 JavaScript来对 loaction.hash 进行赋值,从而改变 URL,触发 hashchange 事件:
    location.hash="#search"
    

    History模式

    由于hash模式下使用时都需要加上 #,并不是很美观。到了 HTML5,又提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState()history.repalceState()。这两个 API可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录。代码格式如下:

    window.history.pushState(null, null, path);
    window.history.replaceState(null, null, path);
    

    history 有下面几个特性:

    • pushState 和 repalceState 的标题(title):一般浏览器会忽略,最好传入 null ;
    • 我们可以使用 popstate 事件来监听 url 的变化;
    • history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面渲染;

    在对前端路由原理有了基本的掌握后,就可以尝试去阅读 vue-router 和 react-router 的源码实现。下面来看下react-router。


    React Router

    React Router 中的组件主要分为三类:

    • 路由组件,例如 BrowserRouter和 HashRouter
    • 路由匹配器,例如 Route 和 Switch
    • 导航,例如 Link,NavLink 和 Redirect

    下面逐一展开介绍:

    路由组件

    路由组件分为两种:BrowserRouter(对应前端路由history 模式) 和 HashRouter(对应前端路由hash 模式),用法一样,只是 url 展示不同,其中 hash 模式带有 # 号符。

    BrowserRouter:http://localhost:3000/page2
    HashRouter:http://localhost:3000/#/page2

    // BrowserRouter:
    <BrowserRouter>
        <Route path="/page1" exact component={Page1}></Route>
        <Route path="/page2/:id" exact component={Page2}></Route>
    </BrowserRouter>
    // HashRouter:
    <HashRouter>
        <Route path="/page1" exact component={Page1}></Route>
        <Route path="/page2/:id" exact component={Page2}></Route>
    </HashRouter>
    

    路由匹配器

    1. Route
      Route 是用于声明路由映射到应用程序的组件层。
      路由Route的参数介绍如下:
    • path
      string 类型,用来指定路由跳转路径,根据 path 和 url 匹配到对应的页面;如下所示
    // 路由配置
    <Route path="/page1" exact component={Page1}></Route>
    // 页面访问 http://localhost:3000/page1
    
    • exact
      boolean 类型,用来精确匹配路由,如果为 true 则精确匹配,否则为正常匹配;如下所示
    // exact = true 时
    <Route path="/page1" exact component={Page1}></Route>
    // 浏览器通过 http://localhost:3000/page1/one 访问不到 page1 页面
    
    // exact = fasle 时
    <Route path="/page1" exact={false} component={Page1}></Route>
    // 浏览器通过 http://localhost:3000/page1/one 可以访问到 page1 页面
    
    • sensitive
      boolean 类型,用来设置是否区分路由大小写,如果为 true 则区分大小写,否则不区分;如下所示
    // sensitive = true 时
    <Route path="/page1" sensitive component={Page1}></Route>
    // 浏览器通过 http://localhost:3000/PAGE1 访问不到 page1 页面
    
    // sensitive = fasle 时
    <Route path="/page1" sensitive={false} component={Page1}></Route>
    // 浏览器通过 http://localhost:3000/PAGE1 可以访问到 page1 页面
    
    • strict
      boolean 类型,对路径末尾斜杠的匹配。如果为 true,path 为 '/page1/' 将不能匹配 '/page1' 但可以匹配 '/page1/one'。;如下所示
    // strict = true 时
    <Route path="/page1/" strict component={Page1}></Route>
    // 浏览器通过以下 url 访问不到 page1 页面
    http://localhost:3000/page1
    // 浏览器通过以下 url 可以访问到 page1 页面
    http://localhost:3000/page1/one
    
    // strict = fasle 时
    <Route path="/page1/" strict={false} component={Page1}></Route>
    // 浏览器通过以下 url 可以访问到 page1 页面
    http://localhost:3000/page1
    http://localhost:3000/page1/one
    
    • component
      设置路由对应渲染的组件,如下所示Page1为要渲染的组件
    <Route path="/page1/" exact component={Page1}></Route>
    
    • render
      通过render函数返回路由对应渲染的组件或者dom,好处是不仅可以通过 render 方法传递 props 属性,并且可以传递自定义属性。如下所示
    <Route path='/about' exact render={(props) => {
        return <Page1 {...props} name={'name1'} />
    }}></Route>
    
    class Page1 extends React.Component {
      componentDidMount() {
        console.log(this.props) 
        // this.props:
        // history: {length: 9, action: "POP", location: {…}, createHref: ƒ, push: ƒ, …}
        // location: {pathname: "/home", search: "", hash: "", state: undefined, key: "ad7bco"}
        // match: {path: "/home", url: "/home", isExact: true, params: {…}}
        // name: "name1"
      }
    }
    
    1. Switch
      如果路由 Route 外部包裹 Switch 时,路由匹配到对应的组件后,就不会继续渲染其他组件了。但是如果外部不包裹 Switch 时,所有路由组件会先渲染一遍,然后选择所有匹配的路由进行显示。
    // 当没有使用 Switch 时
    <BrowserRouter>
      <Route path="/page1" component={Page1}></Route>
      <Route path="/" component={Page2}></Route>
      <Route path="/page3/:id" exact component={Page3}></Route>
    </BrowserRouter>
    // 当面访问 http://localhost:3000/page1 时,浏览器会同时显示 page1 和 page2 页面的内容 
    
    // 当使用 Switch 时
    <BrowserRouter>
      <Switch>
        <Route path="/page1" component={Page1}></Route>
        <Route path="/" component={Page2}></Route>
        <Route path="/page3/:id" exact component={Page3}></Route>
      </Switch>
    </BrowserRouter>
    // 当面访问 http://localhost:3000/page1 时,浏览器只会显示 page1 页面的内容 
    

    导航

    1. Link
      用来指定路由跳转,Link的参数介绍如下:
    • to
      可以通过字符串执行路由跳转,也可以通过对象指定路由跳转,如下所示
    // 通过字符串
    <Link to='/page2'>
      <span>跳转到 page2</span>
    </Link>  
    
    // 通过对象
    <Link to={{
        pathname: '/page2',
        search: '?name=name1',
        hash: '#someHash',
        state: { age: 11 }
      }}>
      <span>跳转到 page2</span>
    </Link>
    

    通过对象指定路由跳转时,各字段的含义如下:
    pathname: 表示跳转的页面路由 path
    search: 表示查询参数的字符串形式,即等同于 location 中的 search
    hash: 放入网址的 hash,即等同于 location 中的 hash
    state: 可以通过这个属性,向新的页面隐式传参,在上面的例子中page2 中可以通过 this.props.location.state 可以拿到 age: 11;

    • replace
      如果设置 replace 为 true 时,表示用新地址替换掉上一次访问的地址;
    1. NavLink
      这是 Link 的特殊版,顾名思义这就是为页面导航准备的,因为导航需要有 “激活状态”。除Link的固定参数外,还有如下可选参数:
    • activeClassName: string,导航选中激活时候应用的样式名
    • activeStyle: object,如果不想使用样式名就直接写 style,
    • exact: bool,若为 true,只有当访问地址严格匹配时激活样式才会应用
    • strict: bool,若为 true,只有当访问地址后缀斜杠严格匹配(有或无)时激活样式才会应用
    • isActive: func,决定导航是否激活,或者在导航激活时候做点别的事情。不管怎样,它不能决定对应页面是否可以渲染。
    1. Redirect
      Redirect用于路由的重定向,它会执行跳转到对应的to路径中,其中参数to的规则同Link一样。
    <Redirect to='/' />
    

    withRouter

    withRouter 可以将一个非路由组件包裹为路由组件,使这个非路由组件也能访问到当前路由的 match, location, history对象。

    class Component1 extends React.Component<any> {
      handleClick () {
        this.props.history.push('/page2');
      }
      render () {
        return <div onClick={() => this.handleClick()}>this is component1</div>;
      }
    }
    
    export default withRouter(Component1);
    

    参数传递

    传递参数有三种方式:

    1. 动态路由的方式
      假如/detail的路径对应 一个组件Detail,若将路径中的Route匹配时写成/detail/:id,那么/detail/123,/detail/xyz都可以匹配到该Route并进行显示
    // 路由配置
    <Route path="/detail/:id" exact component={Detail}></Route>
    // 路由跳转
    this.props.history.push('/detail/1000');
    // 参数获取
    this.props.match.params;  // {id: "1000"}
    
    1. search传递参数
      根据参数to的不同书写格式,search传参可以分为两种形式:
    <Link to="/detail2?name=boge&age=20">详情2</Link>
    <Link to={{ pathname: '/detail2', search: '?name=boge&age=20'}}>详情2</Link>
    
    // 参数获取
    this.props.location.state;  // {name:'boge', age:20}
    
    1. 隐式传参
      所传递的参数不可见
    //数据定义
    const data = {id:3,name:sam,age:36};
    const path = {
        pathname: '/user',
        query: data,
    }
    this.props.history.push(path);
    
    // 参数获取
    this.props.location.query;   // {id:3,name:sam,age:36};
    

    嵌套路由

    嵌套路由就是在子页面中再设置一层新的路由导航规则。重点在于不能在父级加 exact(精准匹配),因为先要匹配父级然后才能匹配子集。

    // appRouter.js
    <BrowserRouter>
        <Route path="/" exact component={Index} />
        <Route path="/video/" component={Video} />
        <Route path="/workplace/" component={Workplace} />
    </BrowserRouter>
    
    // video.js
    <div className="videoContent">
        <h3>视频教程</h3>
        <Route path="/video/react/" component={React} />
        <Route path="/video/vue/" component={Vue} />
    </div>
    

    为什么我能够看得更远,那是因为我站在巨人的肩上,以上内容来源:
    深度剖析:前端路由原理
    一文搞定 React 路由

    相关文章

      网友评论

          本文标题:前端路由原理和React Router

          本文链接:https://www.haomeiwen.com/subject/jkizeltx.html