前端路由hash && history

前端路由,是指前端应用中管理页面导航和url的机制。前端路由使得单页面应用能够在用户交互时动态的加载不同的视图,而不需要每次都重新加载整个页面,即刷新页面。

单页面和多页面的区别:
1. 单页面就是只有一个主页面的应用,只需要在第一次时请求一次js,css,html等相关资源,页面的切换时不需要重新加载整个页面,而是通过ajax和javascript动态的更新DOM

2. 多页面就是有多个独立页面的应用,每个页面都是独立的html文件,每个页面都需要重复请求js,css,html等相关资源,页面切换时需要重新加载整个页面,需要再次请求相关资源

在这里插入图片描述
在这里插入图片描述

vue-router,React Router等都属于前端路由库,这些库提供了一组api和组件,用于定义路由规则、处理导航事件和渲染相应的视图。

前端路由实现原理

路由这个词最早是来自服务器,路由是服务端用来描述路径的,换句话说就是URL和文件的映射关系。

后来前端借鉴了路由的概念,用于描述URL和组件的映射关系。浏览器的URL变成了需要映射到页面的某个组件,URL变成了需要展示某个组件。/Home和Home.vue,/about和About.vue就是一一映射关系。

区别

hash

  • 原理

在 url 中的 # 之后对应的是 hash 值, 其原理是通过hashChange() 事件监听hash值的变化, 根据路由表对应的hash值来判断加载对应的路由加载对应的组件

  • 优点
    • 只需要前端配置路由表, 不需要后端的参与
    • 兼容性好, 浏览器都能支持
    • hash值改变不会向后端发送请求, 完全属于前端路由
  • 缺点
    • hash值前面需要加#, 不符合url规范,也不美观
  • 分析

当 URL 改变时,页面不会重新加载。

hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中也不会不包括#;

同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据

history

  • 优点
    • 符合url地址规范, 不需要#, 使用起来比较美观
  • 缺点
    • 在用户手动输入地址或刷新页面时会发起url请求, 后端需要配置index.html页面用户匹配不到静态资源的情况, 否则会出现404错误
不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问 http://oursite.com/user/id 就会返回 404,这就不好看了。

所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面

但是这样又会有一个问题:这样做了以后,服务器就不再返回404错误页面了,因为对于所有路径都会返回index.html文件。为了避免这种情况,应该在Vue引用里面覆盖所有的路由情况,然后再给出一个404页面:

```js
const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '*', component: NotFoundComponent }
  ]
})

解释一下,*这个通配符用于匹配所有未匹配到的路径,即表示没有与之匹配的路径。因此,当用户访问的路径没有在路由表中找到匹配项时,会匹配到这个路由通配符路由。可以使得在应用中处理所有未知的路径,并为他们提供自定义的处理方式,例如显示一个404页面。


- 兼容性比较差, 是利用了 HTML5 History对象中新增的 pushState() 和 replaceState() 方法,需要特定浏览器的支持.
  • 分析

利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。

pushState()方法可以改变URL地址且不会发送请求;

replaceState()方法可以读取历史记录栈,还可以对浏览器记录进行修改。

这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。

只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。

上面有些问题是与URL有关的:

一般URI有一个通用的结构描述:

scheme:[//[user:password@]host[:port]][/]path[?query][#fragment]

在这里插入图片描述

主要资源是由URI进行标识,URI中的fragment用来标识次级资源。换言之,fragment就是用来标识uri所标识资源里的某个资源。

在URI的末尾通过hash mark(#)作为fragment的开头,其中#不属于fragment的值。

fragment的特点:

  • #有别于?,?后面的查询字符串会被网络请求带上服务器,而fragment不会被发送的服务器
  • fragment的改变不会触发浏览器刷新页面,但是会生成浏览记录
  • frament会被浏览器根据文件媒体类型进行对应处理。
具体来说,浏览器在处理HTML文档时,会根据文档的媒体类型(通常是**`text/html`**)和fragment的内容来决定如何处理它。主要有以下几种情况:

1. **HTML文档**:
    - 如果浏览器加载的文档是HTML文档,并且fragment指定了一个存在于文档中的元素的ID,则浏览器会自动滚动到该ID所对应的元素处。这被称为页面内链接(page anchor),浏览器会自动将视图滚动到指定ID的元素位置,使用户能够方便地查看页面的特定部分。

2. **其他媒体类型**:
    - 如果浏览器加载的文档不是HTML文档,而是其他类型的文档(例如图片、视频、音频等),或者是未知类型的文档,则fragment通常被忽略,浏览器会简单地加载整个文档。在这种情况下,fragment不会影响文档的加载或显示。
  • Google的搜索引擎会忽略#及器后面的字符串

frament的应用:

  1. 单页面路由。JavaScript提供了location.hash 来操作当前URI的fragment/读取当前URI的fragment,同时提供了HashChange事件监听fragment的变化。结合这两个api在结合上述特点1,2就可以实现一个简单前端路由:
    在这里插入图片描述

修改location.hash的值,触发HashChange 事件,js处理对应的逻辑,改变页面ui实现页面的跳转,并在浏览器中产生历史记录

  1. HTML锚点。在HTML中比较常见的一个应用-页面内定位。在页面中通过设置标签的id属性来定义锚点,从而实现锚点定位。实际上锚点定位的实现依赖了fragment的特点3。例如这个URI:https://domain/index.html#L18 ,假设返回的文件类型是text/html,则浏览器会读取URI的fragment,然后在页面中寻找#L8这个锚点,并将页面滚动到该锚点的位置。
因此我们当点击 <a href="#top">top</a>时,实际上处理过程是 URI 的 hash 发生变化,然后浏览器读取新的 fragment,并寻找 DOM 中是否存在对应的锚点,将该锚点显示到页面中。在 MIME Type 为 HTML 或 XML 时,如https://domain/index.html#这个 URI 中是空的 fragment,则浏览器默认显示页面的最顶端。
  1. 特点4 其实是针对 hash 模式前端路由来说的一个缺点。因为 fragment 会被 Google 搜索引擎忽略掉,因此对于用 hash 模式前端路由的应用的 SEO 来说是很不友好的。不过 Google 给了一个方案,就是在 # 紧跟一个 ! ,这样Google 搜索引擎就会将这个 URI 进行转换,如 https://domain/index.html#!L18转换后就成为了 https://domain/index.html?_escaped_fragment_=L18。这样搜索引擎就会携带上 URI’s fragment 直接去访问这个 URI,开发者可以利用这个 trick 优化网站的 SEO。

实现路由

实现路由需要实现:

  1. 如何修改URL还不引起页面的刷新
  2. 如何知道URL变化了

哈希Hash

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
        <ul>
            <li><a href="#/home">首页</a></li>
            <li><a href="#/about">关于</a></li>
        </ul>

        <div id="routeView"></div>
    </body>

    <script>
        const routes = [
            {
                path: '#/home',
                component: '首页内容',
            },
            {
                path: '#/about',
                component: '关于页面内容',
            },
        ];

        // hashchange事件监听fragment的变化
        window.addEventListener('hashchange', onHashChange);

        // 想要hashchangge在页面初次加载时就触发一次,就监听一次dom结构的事件,dom一出来就执行一次
        window.addEventListener('DOMContentLoaded', onHashChange);

        function onHashChange() {
            // 获取当前URL中的fragment
            const hash = location.hash;
            routes.forEach((item, index) => {
                if (hash === item.path) {
                    document.querySelector('#routeView').innerHTML = item.component;
                }
            });
        }
    </script>
</html>

History

浏览器中有个会话历史栈,它可以维护你的访问路径,有了这个你返回就可以按照栈的顺序进行前进回退。

pushState提到了popState,他是靠popState监听url的改变的,并且仅当浏览器前进后退时生效

pushState 可以修改URL并且不引起页面的刷新。

pushState()popstate 之间存在着一种关系,它们共同组成了 HTML5 History API 的一部分,用于在浏览器历史记录中进行导航和管理。

  • pushState(): pushState() 方法用于向浏览器的历史记录栈中添加一个新的状态。它接受三个参数:状态对象(state object)、标题(title)和 URL。当调用 pushState() 方法时,浏览器不会立即加载新的页面,而是将当前的状态推入历史记录栈,并更新当前的 URL。这意味着你可以使用 pushState() 方法来改变 URL,而不会导致页面的重新加载。这个方法通常与前端路由配合使用,用于在单页面应用中切换视图而不刷新页面。
  • popstate 事件: popstate 事件是在用户点击浏览器的前进或后退按钮时触发的事件。当历史记录发生改变时(例如通过调用 pushState() 方法添加新的状态或通过浏览器导航按钮进行导航),浏览器会触发 popstate 事件。通过监听 popstate 事件,你可以在历史记录发生改变时执行相应的操作,例如更新页面内容以反映当前的状态。

因此,pushState() 用于向历史记录栈中添加新的状态,而 popstate 事件则用于在历史记录发生改变时通知页面,并且在这种情况下可以执行相应的操作。这两者结合使用可以实现在单页面应用中进行导航、路由和状态管理的功能。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
        <ul>
            <li><a href="/home">首页</a></li>
            <li><a href="/about">关于</a></li>
        </ul>

        <div id="routeView"></div>

        <script>
            const routes = [
                {
                    path: '/home',
                    component: '首页内容',
                },
                {
                    path: '/about',
                    component: '关于页面内容',
                },
            ];

            const routeView = document.getElementById('routeView');

            window.addEventListener('DOMContentLoaded', onLoad);
            window.addEventListener('popstate', onPopState);

            function onLoad() {
                const links = document.querySelectorAll('li a'); // 获取所有的li下的a标签
                // console.log(links)
                links.forEach((a) => {
                    // 禁用a标签的默认跳转行为
                    a.addEventListener('click', (e) => {
                        console.log(e);
                        e.preventDefault(); // 阻止a的跳转行为
                        // pushState可以修改URL,但不引起页面的刷新
                        // pushState有三个参数:
                        // 1. null
                        // 2. 空字符串
                        // 3. 新的URL
                        // 新的URL肯定是点了什么就放什么URL,所以需要读取到a标签的href值
                        history.pushState(null, '', a.getAttribute('href')); // 核心方法  a.getAttribute('href')获取a标签下的href属性
                        // 映射对应的dom
                        onPopState();
                    });
                });
            }

            function onPopState() {
                routes.forEach((item) => {
                    if (item.path === location.pathname) {
                        routeView.innerHTML = item.component;
                    }
                });
            }
        </script>
    </body>
</html>
  • 28
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃炫迈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值