【React】react-router-dom中的HashRouter和BrowserRouter实现原理

1. 前言

        在之前整理BOM的五个对象时,提到:

  • location.hash发生改变后,会触发hashchange事件,且history栈中会增加一条记录,但页面不会重新加载——实现HashRouter的关键
  • history.pushState(state, '', URL)执行后,history栈中会增加一条记录,但页面不会重新加载,回退(history.back()或history.go(-1))会触发popstate事件,location.pathname发生改变——实现BrowserRouter的关键

        本篇通过举例方式详细描述实现的过程。

2. HashRouter的原理
2.1 原理要点
  • 通过<a>元素href属性修改URL的hash值(或location.hash='#/xxx'修改,或前进后退修改);
  • location.hash发生改变,页面不会重新加载;
  • location.hash发生改变,URL也会改变(hash是URL的一部分),新URL被推入history栈中;
  • location.hash发生改变,会触发window的hashchange事件;
  • 通过给window.onhashchange绑定事件处理函数,监听hash变化,根据hash值选择渲染的组件。

注:根据hash值变化渲染组件,不会影响hash原有作为锚点的功能(锚点功能:根据#后面字符串滚动到对应id的元素 )

2.2 示例源码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Hash Router</title>
    </head>
    <body>
        <a href="#/user">Go user</a>
        <a href="#/info">Go info</a>
        <div id="root"></div>
        <!-- <div style="height: 600px;"></div>
        <div id="/info"></div> -->
        <script>
            let root = document.getElementById('root');
            window.onhashchange = function(event) {
                if(window.location.hash === '#/user') {
                    root.innerHTML = `
                        <label>
                            用户名:
                            <input value="小明" placeholder="user name"/>
                        </label>
                    `;
                } else {
                    root.innerHTML = `
                        <label>
                            年龄:13岁
                        </label>
                    `;
                }
            }
        </script>
    </body>
</html>
3. BrowserRouter的原理
3.1 原理要点
  • window不支持onpushstate事件,需要为window构造onpushstate事件,并绑定事件处理函数,重写history.pushState,保证执行history.pushState就会触发onpushstate事件;
  • 可以为菜单项绑定click事件处理函数,执行重写的history.pushState(state,title,url)修改URL;
  • 执行重写的history.pushState,页面不会重新加载,但location.pathname发生改变,并触发window的onpushstate事件;
  • 通过window.onpushstate的事件处理函数,监听pathname变化,根据pathname值选择渲染的组件;
  • 前进和后退会改变URL,页面不会重新加载,location.pathname发生改变,并触发window的onpopstate事件;
  • 通过window.onpopstate绑定的事件处理函数,监听pathname变化,根据pathname值选择渲染的组件。
3.2 示例源码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Hash Router</title>
    </head>
    <body>
        <div id="root"></div>

        <script>
            let root = document.getElementById('root');
            
            window.onpushstate = function(state, title, url) {
                console.dir({
                    type: 'onpushstate', 
                    state, 
                    pathname: url
                });
                render(url);
            }
            // 修改history.pushState方法,将window.onpushstate事件绑入
            bindPushstate(window.history);

            function bindPushstate(history) {
                let pushState = history.pushState;
                history.pushState = function(state, title, url) {
                    if(typeof window.onpushstate === 'function') {
                        window.onpushstate(state, title, url);
                    }
                    return pushState.apply(history, arguments);
                }
            }            
            
            window.onpopstate = function(event) {
                const pathname = window.location.pathname;
                console.dir({
                    type: event.type, 
                    state: event.state,
                    pathname
                });
                
                render(pathname);
            }

            function render(pathname) {
                if(pathname === '/user') {
                    root.innerHTML = `
                        <label>
                            用户名:
                            <input value="小明" placeholder="user name"/>
                        </label>
                    `;
                } else if(pathname === '/info') {
                    root.innerHTML = `
                        <label>
                            年龄:13岁
                        </label>
                    `;
                } else {
                    root.innerHTML = `
                        <label>
                            需要填写个人信息
                        </label>
                    `;
                }
            }

            setTimeout(() => {
                window.history.pushState({ page: 1 }, 'user', '/user');
            }, 2000);

            setTimeout(() => {
                window.history.pushState({ page: 2 }, 'info', '/info');
            }, 4000);

            setTimeout(() => {
                window.history.pushState({ page: 3 }, 'help', '/help');
            }, 6000);

        </script>
    </body>
</html>
4. 总结

        再牛的建筑都离不开一块一块砖瓦,学到很多高大上的框架和上层API时,再回过头来看基础,会更加深对框架和上层API的理解。

注:以上,如有不合理之处,还请帮忙指出,大家一起交流学习~  

  • 28
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Router是一个用于构建单页面应用程序的库。它提供了一种在React应用程序管理路由的方式。React Router DOM是React Router的一个扩展,用于在Web应用程序进行路由。 React Router DOM 5和React Router DOM 6之间有几个重要的区别: 1. 安装方式:React Router DOM 5使用npm包管理器进行安装,命令为`npm install react-router-dom`。而React Router DOM 6使用yarn进行安装,命令为`yarn add react-router-dom@next`。 2. 路由组件:在React Router DOM 5,使用`<Route>`组件来定义路由。而在React Router DOM 6,使用`<Route>`组件的替代方案`<Routes>`来定义路由。 3. 路由匹配方式:React Router DOM 5使用基于路径的匹配方式来确定哪个路由应该被渲染。而React Router DOM 6引入了新的匹配方式,称为元素匹配(element matching),它可以根据组件的类型来匹配路由。 4. 嵌套路由:在React Router DOM 5,嵌套路由需要使用嵌套的`<Route>`组件来定义。而在React Router DOM 6,可以使用嵌套的`<Routes>`组件来定义嵌套路由。 5. 动态路由:在React Router DOM 5,可以通过在路径使用参数来定义动态路由。而在React Router DOM 6,可以使用`<Route>`组件的新属性`element`来定义动态路由。 6. 错误处理:React Router DOM 5使用`<Switch>`组件来处理路由匹配错误。而React Router DOM 6使用`<Routes>`组件的新属性`fallback`来处理路由匹配错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值