美文网首页
[react]22、router

[react]22、router

作者: 史记_d5da | 来源:发表于2021-11-27 10:46 被阅读0次

    1、概念

    路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动.
    路由的概念出现最早是在后端路由中实现的,原因是web的发展主要经历了这样一些阶段:

    1.1、后端路由阶段;

    早期的网站开发整个HTML页面是由服务器来渲染的.
    服务器直接生产渲染好对应的HTML页面, 返回给客户端进行展示.

    1.1.1、服务器处理多个页面的步骤:

    1、一个页面有自己对应的网址, 也就是URL.
    2、URL会发送到服务器, 服务器会通过正则对该URL进行匹配, 并且最后交给一个Controller进行处理.
    3、Controller进行各种处理, 最终生成HTML或者数据, 返回给前端.
    4、这就完成了一个IO操作.

    1.1.2、上面的这种操作, 就是后端路由.

    1、当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户顿.
    2、这种情况下渲染好的页面, 不需要单独加载任何的js和css, 可以直接交给浏览器展示, 这样也有利于SEO的优化.

    1.1.3、后端路由的缺点:

    1、一种情况是整个页面的模块由后端人员来编写和维护的.
    2、另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码.
    3、而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情.

    1.2、前后端分离阶段

    分析:
    1、每次请求涉及到的静态资源都会从静态资源服务器获取, 这些资源包括HTML+CSS+JS,然后在前端对这些请求回来的资源进行渲染;
    2、客户端的每一次请求,都会从静态资源服务器请求文件;
    3、 同时可以看到,和之前的后端路由不同,这时后端只是负责提供API了;
    总结前后端分离思想:
    1、随着Ajax的出现, 有了前后端分离的开发模式,
    2、后端只提供API来返回数据,前端通过Ajax获取数据,并且可以通过JavaScript将数据渲染到页面中

    1.3、单页面富应用(SPA)

    单页面富应用的英文是single-page application,简称SPA,整个Web应用只有实际上只有一个页面,当URL发生改变时,并不会从服务器请求新的静态资源,而是通过JavaScript监听URL的改变,并且根据URL的不同去渲染新的页面。
    前端路由实现:
    1、前端路由维护着URL和渲染页面的映射关系
    2、路由可以根据不同的URL,最终让我们的框架(比如Vue、React、Angular)去渲染不同的组件
    3、最终我们在页面上看到的实际就是渲染的一个个组件页面;

    2、前端路由的实现

    2.1、前端路由的原理

    1、监听URL的改变
    1)、 通过URL的hash改变URL
    2)、可以通过直接赋值location.hash来改变href, 但是页面不发生刷新
    URL的hash也就是锚点(#), 本质上是改变window.location的href属性
    注意点:
    hash的优势就是兼容性更好,在老版IE中都可以运行、但是缺陷是有一个#,显得不像一个真实的路径;

    <body>
        <div id="app">
            <a href="#/home">首页</a>
            <a href="#/about">关于</a>
            <div class="router-view"></div>
        </div>
        <script>
            // 获取routerview docm
            const routerViewEl = document.getElementsByClassName("router-view")[0];
            // 监听url改变
            window.addEventListener("hashchange", () => {
                console.log(routerViewEl);
                switch(location.hash) {
                    case "#/home":
                    routerViewEl.innerHTML = "首页";
                    break;
                    case "#/about":
                    routerViewEl.innerHTML = "关于";
                    break;
                    default:
                    routerViewEl.innerHTML = "";
                    break;
                }
            })
        </script>
    </body>
    </html>
    

    2)、通过HTML5中的history模式修改URL,它有l六种模式改变URL而不刷新页面

    <body>
        <div id="app">
            <a href="/home">首页</a>
            <a href="/about">关于</a>
            <div class="router-view"></div>
        </div>
        <script>
            const routerViewEl = document.getElementsByClassName("router-view")[0];
            // 监听url改变
            const aEls = document.getElementsByTagName("a");
            for (let el of aEls) {
                el.addEventListener("click", e => {
                    e.preventDefault();
                    const href = el.getAttribute("href");
                    history.pushState({}, "", href);
                    urlChange()
                    // history.go()
                    // history.forward()
                })
            }
            window.addEventListener("popstate", urlChange);
            // window.addEventListener("go", urlChange);
            function urlChange() {
                switch (location.pathname) {
                    case "/home":
                    console.log("首页")
                        routerViewEl.innerHTML = "首页";
                        break;
                    case "/about":
                    console.log("关于")
                        routerViewEl.innerHTML = "关于";
                        break;
                    default:
                    console.log("default")
                    routerViewEl.innerHTML = "";
                }
            }
        </script>
    </body>
    

    2、当监听到URL发生变化时,我们可以通过自己判断当前的URL,决定到底渲染什么样的内容。

    3、react-router

    3.1、react-router介绍

    1、目前前端流行的三大框架, 都有自己的路由实现:
    Angular的ngRouter、React的react-router、Vue的vue-router
    2、react-router的代码组成

    • react-router是router的核心部分代码
    • react-router-dom是用于浏览器的
    • react-router-native 是用于原生应用的

    3、安装react-router:
    安装react-router-dom会自动帮助我们安装react-router的依赖;
    安装命令yarn add react-router-dom

    3.2、react-router基本使用

    react-router最主要的API是给我们提供的一些组件:
    1、BrowserRouter或HashRouter

    • Router中包含了对路径改变的监听,并且会将相应的路径传递给子组件
    • BrowserRouter使用history模式
    • HashRouter使用hash模式
    import React, { PureComponent } from 'react'
    import { BrowserRouter, Link, Route, NavLink, Switch, withRouter } from 'react-router-dom'
    import About from './pages/about'
    import Home from './pages/home'
    import Profile from './pages/profile'
    class App extends PureComponent {
        static propTypes = {}
        constructor(props) {
            super(props)
        }
    
        render() {
            return (
                <BrowserRouter>
                    <Link to="/">首页</Link>
                    <Link to="/about">关于</Link>
                    <Link to="/profile">我的</Link>
                    <Route exact={true} path="/" component={Home}></Route>
                    <Route path="/profile" component={Profile}></Route>
                    <Route path="/about" component={About}></Route>
                </BrowserRouter>
            )
        }
    }
    

    2、Link和NavLink:

    • 通常路径的跳转是使用Link组件,最终会被渲染成a元素
    • NavLink是在Link基础之上增加了一些样式属性(后续学习)
    • to属性:Link中最重要的属性,用于设置跳转到的路径

    3、Route

    • Route用于路径的匹配
    • path属性:用于设置匹配到的路径
    • component属性:设置匹配到路径后,渲染的组件
    • exact:精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件

    3.3、NavLink的使用

    NavLink可以改变选中时对应的a元素变为红色
    NavLink的三个属性:
    1、activeStyle:活跃时(匹配时)的样式、
    2、activeClassName:活跃时添加的class;(在默认匹配成功时,NavLink就会添加上一个动态的active class)
    3、exact:是否精准匹配;

    <NavLink exact to="/" activeClassName={"link-active"}>首页</NavLink>
    <NavLink to="/about" >关于</NavLink>
    <NavLink to="/profile" >我的</NavLink>
    

    对应的.css

    a.active {
        color: red;
        font-size: 30px;
        margin: 10px;
        text-decoration: line-through;
    }
    

    3.4、Switch使用

    当匹配到某一个路径时,会发现有一些问题,比如/about路径匹配到的同时,/:userid也被匹配到了,并且最后的一个NoMatch组件总是被匹配到
    原因是默认情况下,react-router中只要是路径被匹配到的Route对应的组件都会被渲染;
    如果希望只匹配到了第一个,后面的就不继续匹配了,可以使用Switch

    <Switch>
           <Route exact={true} path="/" component={Home}></Route>
           <Route path="/profile" component={Profile}></Route>
           <Route path="/about" component={About}></Route>
           <Route path="/user" component={User}></Route>
           <Route path="/login" component={Login}></Route>
           <Route path="/product" component={Product}></Route>
           <Route path="/detail/:id" component={Detail}></Route>
           <Route path="/detail2" component={Detail2}></Route>
           <Route path="/detail3" component={Detail3}></Route>
           <Route component={NoMatch}></Route>  
    </Switch>
    
    3.5、Redirect

    Redirect用于路由的重定向,当这个组件出现时,就会执行跳转到对应的to路径中

    import React, { PureComponent } from 'react'
    import PropTypes from 'prop-types'
    import { Redirect } from 'react-router-dom'
    class User extends PureComponent {
        static propTypes = {}
        constructor(props) {
            super(props)
            this.state = {
                isLogin: false
            }
        }
        render() {
            return (
                this.state.isLogin ? (<div>
                    <h2>Login</h2>
                    <h2>coder why</h2>
                </div>) : (<Redirect to="/login"/>)
            )
        }
    }
    export default User
    
    3.6、手动路由跳转

    通过JavaScript代码进行跳转有一个前提:必须获取到history对象,如果该组件是通过路由直接跳转过来的,那么可以直接获取history、location、match对象
    index.js文件

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

    App.js文件

    class App extends PureComponent {
        static propTypes = {}
        constructor(props) {
            super(props)
    
            this.state = {
                links: [
                    {to: "/", title: "首页"},
                    {to: "/about", title: "关于"},
                    {to: "/profile", title: "我的"}
                ]
            }
        }
    
        render() {
            const id = "abc"
    
            const info = {name: "codey", age: 18};
            return (
                <div>
                        {/* {
                            this.props.links.map((item, index) => {
                                return (
                                    <div key={item.title} className={classNames({"active": index === this.state.currentIndex})}>
                                        <Link to={item.to}>{item.title}</Link>
                                    </div>
                                )
                            })
                        } */}
                        <NavLink exact to="/" activeClassName={"link-active"}>首页</NavLink>
                        <NavLink to="/about" >关于</NavLink>
                        <NavLink to="/profile" >我的</NavLink>
                        <NavLink to="/abc" >abc</NavLink>
                        <NavLink to="/user" >user</NavLink>
                        <NavLink to="/login" >login</NavLink>
                        <NavLink to={`/detail/${id}`} activeClassName={"link-active"}>详情</NavLink>
                        <NavLink to={`/detail2?name=cody&age=10`} activeClassName={"link-active"}>详情2</NavLink>
                        <NavLink to={{
                            pathname: "/detail3",
                            search: "?names=abc",
                            state: info
                        }} activeClassName={"link-active"}>详情3</NavLink>
                        <button onClick={e => this.jumpProduct()}>商品列表</button>
    
    {/*switch的作用*/}
                       <Switch>
                            <Route exact={true} path="/" component={Home}></Route>
                            <Route path="/profile" component={Profile}></Route>
                            <Route path="/about" component={About}></Route>
                            <Route path="/user" component={User}></Route>
                            <Route path="/login" component={Login}></Route>
                            <Route path="/product" component={Product}></Route>
                            <Route path="/detail/:id" component={Detail}></Route>
                            <Route path="/detail2" component={Detail2}></Route>
                            <Route path="/detail3" component={Detail3}></Route>
                            <Route component={NoMatch}></Route>  
                        </Switch> 
                        { (routes)}
                </div>
            )
        }
    
        // 跳转商品列表
        jumpProduct() {
            this.props.history.push("/product")
        }
    }
    
    export default withRouter(App)
    
    3.7、参数传递

    参数传递方式:1、search传递参数,2、Link中to可以直接传入一个对象

    <NavLink to={`/detail2?name=cody&age=10`} activeClassName={"link-active"}>详情2</NavLink>
    <NavLink to={{
                            pathname: "/detail3",
                            search: "?names=abc",
                            state: info
                        }} activeClassName={"link-active"}>详情3</NavLink>
    

    参数接收

    <h2>Detail2 : {this.props.location.search}</h2>
    

    4、react-router-config

    安装react-router-configyarn add react-router-config
    配置路由映射的关系数组

    const routes = [
        {
            path: "/",
            exact: true,
            component: Home
        },
        {
            path: "/about",
            component: About,
            routes: [
                {
                    path: "/about",
                    exact: true,
                    component: AboutHistory
                }
            ]
        },
        {
            path: "/profile",
            component: Profile
        },
        {
            path: "/",
            component: User
        }
    ]
    export default routes;
    

    renderRoutes源码分析

    function renderRoutes(routes, extraProps, switchProps) {
      if (extraProps === void 0) {
        extraProps = {};
      }
    
      if (switchProps === void 0) {
        switchProps = {};
      }
    
      return routes ? React.createElement(reactRouter.Switch, switchProps, routes.map(function (route, i) {
        return React.createElement(reactRouter.Route, {
          key: route.key || i,
          path: route.path,
          exact: route.exact,
          strict: route.strict,
          render: function render(props) {
            return route.render ? route.render(_extends({}, props, {}, extraProps, {
              route: route
            })) : React.createElement(route.component, _extends({}, props, extraProps, {
              route: route
            }));
          }
        });
      })) : null;
    }
    

    相关文章

      网友评论

          本文标题:[react]22、router

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