Vue3基础:带你手搓简易vue-router理解实现原理

路由

在传统的多页面应用(MPA) 中,服务端路由是非常常见的。当用户在浏览器中输入 URL 或点击链接时,浏览器会向服务器发送请求,服务器端的路由系统会解析请求的 URL,并确定应该返回哪个HTML页面。每个URL对应服务器上的一个独立的资源或页面。

单页面应用(SPA) (vue就是单页面应用)中,情况有所不同。SPA是一种Web应用程序架构,其核心思想是在加载应用程序时只加载一个HTML页面,并通过JavaScript动态地更新该页面,而不需要每次交互都向服务器请求新的HTML页面。

单页应用只有一个html页面,页面切换url不变,想保持原来服务端路由的特点,来维持url和组件的映射关系,这就是前端路由要解决的问题。

实现前端路由需要解决的问题

单页应用在一般页面变化时url并不会直接改变,但是我们想模拟传统多页应用中路由显示特点,希望页面切换url对应也变化,于是我们需要考虑两个核心问题

  1. 如何修改url,还不引起页面的刷新(跳转)
  2. 如何知道url变化了

我们可以通过js实现路由的hash模式和history模式来解决

hash

浏览器中url后面拼接 #xxx 会被认为是hash值,而hash值的变更,是不会引起浏览器页面的刷新。我们就能实现页面切换同步url变化,但是页面不跳转。

特点

在网址后加'/'回车后网页刷新

在网址后加其它内容回车后网页刷新

在网址后面加'#'后接任意内容回车不刷新

我们编写一个页面来实现hash模式下的前端路由

代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        ul {
            display: flex;
        }

        li {
            display: inline-block;
        }
    </style>
</head>

<body>
    <ul>
        <li><a href="#/home">首页</a></li>
        <li><a href="#/about">关于</a></li>
    </ul>
    <div id="routerView">
        内容显示区
    </div>

    <script>
        const routes = [
            {
                path: '#/home',
                component: '首页页面内容'
            },
            {
                path: '#/about',
                component: '关于页面内容'
            }
        ]
        const routerView = document.getElementById('routerView')
        window.addEventListener('hashchange', onHashChange);
        function onHashChange(event) {
            // 处理URL变化的逻辑
            //console.log(window.location.hash);//直接打印location也是一样的效果
            routes.forEach((item, index) => {
                if (item.path === location.hash) {
                    routerView.innerHTML =
                        item.component
                }
            });
        }
        //解决只能点击才会触发onHashChange事件
        window.addEventListener('DOMContentLoaded',onHashChange)
    </script>
</body>

</html>

代码解释

  • 一般修改url的主要方式:

    1. a标签
    2. 浏览器的前进后退
    3. window.location的前进后退
  • 这里我们通过a标签中跳转的路径开头设置为'#',这样点击a标签后,页面url后面添加上a标签中设置的路径,因为是变更了url的哈希部分,所以页面并没有刷新

  • 在script标签中定义一个对象数组,每个对象有跳转路径属性path,以及跳转之后的要展示的内容component

  • 设置一个监听事件,监听hashchange(更改当前页面url中的哈希部分),当url改变就会执行函数onHashChange

  • onHashChange会读取当前的window对象的location.hash属性也就是url中的哈希部分,再遍历对象数组,如果该对象路径path和当前url的哈希部分匹配就将其component拿到页面中展示

效果

history

因为前端路由的hash模式增加'#'号,有点不好看,当然也有去掉'#'更简洁的history模式

原理:

  • 解决页面刷新问题 : history模式下,更改url之后页面会被刷新,但是我们想更改url又不想刷新页面,js中提供了一个pushState方法,可以修改url且不会引起页面刷新;

  • 解决组件和url的映射问题 : 那我们也要完成url与组件的映射关系可以通过location.pathname 属性获取URL中的路径部分,之后通过匹配对应的url展示相应的页面内容,通过js提供的popState事件,仅当浏览器前进后退时该事件生效,来实现页面通过浏览器前进后退时url与组件的正确映射

代码:

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        ul {
            display: flex;
        }

        li {
            display: inline-block;
        }
    </style>
</head>

<body>
    <ul>
        <li><a href="/home">首页</a></li>
        <li><a href="/about">关于</a></li>
    </ul>
    <div id="routerView">
        放一个代码片段
    </div>

    <script>
        const routerView = document.getElementById("routerView");
        const routes = [
            {
                path: '/home',
                component: '首页页面内容'
            },
            {
                path: '/about',
                component: '关于页面内容'
            }
        ]
        const links = document.querySelectorAll('li a')
        // console.log(links);
        links.forEach(a => {
            a.addEventListener('click', (e) => {
                // console.log(e);
                //阻止了a标签的默认跳转行为
                e.preventDefault();
                //添加一种可以修改url又不造成页面刷新
                history.pushState(null, '', a.getAttribute('href'));
                //映射对应的DOM
                onPopState()
            })
        })
        function onPopState() {
            // console.log(location.pathname);
            routes.forEach((item) => {
                if (item.path == location.pathname) {
                    routerView.innerHTML = item.component
                }
            });
        }
        function onLoad() {
            onPopState()
            const links = document.querySelectorAll('li a')
            links.forEach(a => {
                //为每个a标签添加点击事件
                a.addEventListener('click', (e) => {
                    e.preventDefault()  // 阻止了a标签的默认跳转行为
                    // 添加一个可以修改url又不造成页面刷新
                    history.pushState(null, '', a.getAttribute('href'))
                    // 映射对应的dom
                    onPopState()
                })
            })
        }

        window.addEventListener('DOMContentLoaded', onLoad)
        //监听页面因为浏览器前进后退导致的url变化事件
        window.addEventListener('popstate', onPopState)
    </script>
</body>

</html>

代码解释

  • 因为点击a标签会更改url,而history模式下,更改url会导致页面刷新,这不是我们想要的效果,所以我们使用a标签的自带函数preventDefault(),阻止了a标签的默认跳转行为
  • 那么此时a标签就不能修改url,当然js中有我们需要的修改url但是不引起页面刷新的方法pushState,我们给a标签绑定点击事件,点击事件执行自定义函数onPopState,在onPopState中使用pushState方法来更改url
  • 自定义函数onPopState,函数通过读取当前页面的url的路径部分location.pathname,再遍历路径对象,如果路径参数path匹配就将该对象的component展示到页面上
  • 在浏览器ui界面进行前进后退,改变url的话并没有触发点击事件,所以我们需要为这个情景补充一个监听事件window.addEventListener('popstate', onPopState),popstate事件,仅在浏览器ui界面进行前进后退,改变url触发

效果:

总结

1. 背景

在传统多页面应用(MPA)中,服务端路由常见,而单页面应用(SPA)通过前端路由实现页面切换和导航,从而提升用户体验。本文讨论了前端路由的简易实现,重点介绍了 hash 模式和 history 模式。

2. hash 模式

  • 特点: 在 URL 后面加 #,不会引起页面刷新。
  • 解决问题: 实现页面切换同步 URL 变化,但不进行页面跳转。
  • 实现原理: 利用 hashchange 事件监听 URL 变化,通过对象数组映射 URL 和组件关系,实现页面内容的动态切换。

3. history 模式

  • 特点: 去除 #,更简洁。
  • 解决问题: 使用 pushState 方法修改 URL 且不引起页面刷新,解决页面刷新问题;通过 popState 事件,和a标签的点击事件实现 URL 与组件的映射关系。
  • 实现原理: 利用 popstate 事件监听浏览器前进后退,通过 location.pathname 获取 URL 路径部分,映射到相应组件。
相关推荐
3Katrina几秒前
前端面试之防抖节流(二)
前端·javascript·面试
前端进阶者7 分钟前
天地图编辑支持删除编辑点
前端·javascript
江号软件分享15 分钟前
无接触服务的关键:二维码生成识别技术详解
前端
江号软件分享16 分钟前
如何利用取色器实现跨平台色彩一致性
前端
灰海20 分钟前
封装WebSocket
前端·网络·websocket·网络协议·vue
前端小巷子30 分钟前
深入理解TCP协议
前端·javascript·面试
万少32 分钟前
鸿蒙外包的十大生存法则
前端·后端·面试
顽疲1 小时前
从零用java实现 小红书 springboot vue uniapp(13)模仿抖音视频切换
java·vue.js·spring boot
开开心心就好1 小时前
电脑息屏工具,一键黑屏超方便
开发语言·javascript·电脑·scala·erlang·perl
江号软件分享1 小时前
有效保障隐私,如何安全地擦除电脑上的敏感数据
前端