【Vue】重新理解Vue-Router中的两种路由模式

历史小剧场

唐代实在太高太强了,他们忽忘了民族界限,他们不懂害怕外国人,不懂提防外国人,大量使用外国人当兵作将,结果才弄得不可收拾。于是唐代的府兵一变而成为“藩镇”,军阀割据,胡族临制。----《中国历代政治得失》

location 和 history接口

window.location

说到location对象,它有很多的属性。我们可以通过改变其属性值修改页面的url。我们在单页应用中需要做到的是改变url不刷新页面,location接口提供以下两种方式可以做到:

  1. locaiton.href 赋值时只改变url的hash
  2. 直接赋值location.hash
    在这里插入图片描述

除此之后,还有一个常用方法:location.search(): 会直接刷新页面。

window.history

history 接口 是 HTML5 新增的,它有5个方法可以改变url而不刷新页面。

  1. history.pushSate()
    在这里插入图片描述
  2. history.replaceState()
    在这里插入图片描述
  3. history.forward(). 等价于 history.go(1): 页面前进
  4. history.back() 等价于 history.go(-1): 页面后退

如何监听url的变化

hashchange

hashchange 事件能监听 url hash 的改变

window.addEventListener('hashchange', function(e) {
  console.log(e)
})
popstate

popstate 事件能监听history.forward() 和 history.back() 方法导致url的变化

window.addEventListener('popstate', (e) => {
  console.log("state => ", e.state.page)
   loadHTML(e.state.page, content)
})

hash模式和history模式

前端路由

前端路由是后来发展到SPA(但页面应用)时才出现的概念。SPA就是一个WEB项目只有一个HTML页面,一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转。

优点:前后端的彻底分离,不刷新页面,用户体验较好,页面持久性较好。

hash模式
  • 把前端的路由用#拼接到真实url后面的模式,但是会覆盖锚点定位元素的功能,通过监听URL的哈希部分变化,响应地更新页面的内容
  • 前端路由的处理完全在客户端进行,在路由发生变化时,只会改变URL中的哈希部分(#后面的路径),且不会向服务器发送新的请求,而是触发 hashchange 事件。
  • hash值的改变,都会在浏览器的访问历史中增加一个记录,所以可以通过浏览器的回退、前进按钮控制hash的切换。
  • hash路由不会造成404页面的问题,因为所有路由信息都在客户端进行解析和处理,服务端只负责提供应用的初始HTML页面和静态资源,不需要关心路由的匹配问题。
  • 通过location.hash 修改hash值,触发更新。
  • 通过监听hashchange事件监听浏览器前进或者后退,触发更新

实现

这里有三个文件:一个主页面 hash.html,两个子页面:Home.html 和 About.html

Home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home</title>
</head>
<body>
    Home
</body>
</html>

About.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>About</title>
</head>
<body>
    About
</body>
</html>

hash.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>哈希路由</title>
</head>
<body>
    <ul>
        <li>
            <a href="Home.html">Home</a>
        </li>
        <li>
            <a href="About.html">About</a>
        </li>
    </ul>
    <div id="content"></div>
    <script>
        const contentEle = document.querySelector("#content");
        const ulEle = document.querySelector("ul")

        const loadHTML = (url, element) => {
            fetch(url).then(response => {
                if (response.ok) {
                    return response.text()
                }
                throw new Error('Network response was not ok.');
            }).then(html => {
                element.innerHTML = html;
            }).catch(error => {
                console.error("error => ", error)
            })
        }

        // 页面刷新时,需要重新获取hash
        window.addEventListener('load', () => {
            // 获取当前的hash值
            const currentHash = window.location.hash;
            // 如果存在hash值,执行相应的逻辑
            if (currentHash) {
                // 这里可以添加处理hash的逻辑
                console.log('current hash: ', currentHash.slice(1))
                loadHTML(currentHash.slice(1), contentEle)
            }
        })
        
        ulEle.addEventListener('click', (e) => {
            // 不跳转
            e.preventDefault()
            const page = e.target.getAttribute('href')
            loadHTML(page, contentEle)
            location.hash = `#${page}`
        })
       
    </script>
</body>
</html>
history 模式
  • 在使用history模式时,需要通过服务端支持允许地址可访问,如果没有设置,就很容易导致404的局面;
  • 改变url:history 提供了 pushState 和 replaceState 两个方法来记录路由状态,这两个方法只改变URL不会引起页面刷新;
  • 监听url变化:通过 popstate 事件监听history 变化,在点击浏览器的前进或者后端功能时触发,在 popstate 事件中根据状态信息加载对应的页面内容

实现

这里有五个文件:主页面一个 history.html; 子页面4个:test1.html、test2.html、test3.html、test4.html

test1.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>
</head>
<body>
    11111
</body>
</html>

test2.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>
</head>
<body>
    2222
</body>
</html>

test3.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>
</head>
<body>
    33333
</body>
</html>

test4.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>
</head>
<body>
    4444
</body>
</html>

history.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>
</head>
<body>
    <button id="pushStateBtn">pushState压栈</button>
    <button id="getLenBtn">getLen</button>
    <button id="replaceStateBtn">replaceState替换</button>
    <button id="forward">前进</button>
    <button id="back">后退</button>
    <div>栈:<span id="size"></span></div>
    <hr>
    <div id="content"></div>
    <script>

        const loadHTML = (url, element) => {    
            fetch(url).then(response => {
                if (response.ok) {
                    return response.text()
                }
                throw new Error('Network response was not ok.');
            }).then(html => {
                element.innerHTML = html;
            }).catch(error => {
                console.error("error => ", error)
            })
        }

        const pushStateBtn = document.getElementById('pushStateBtn');
        const getLenBtn = document.getElementById('getLenBtn');
        const size = document.getElementById('size');
        const replaceStateBtn = document.getElementById('replaceStateBtn');
        const forward = document.getElementById('forward');
        const back = document.getElementById('back');
        const content = document.getElementById('content');

        let i = 1;
        size.textContent = `当前历史记录栈总数:${history.length}`;

        pushStateBtn.addEventListener('click', () => {
            const page = `test${i}.html`
            history.pushState({page: page}, "", page)
            loadHTML(page, content)
            i++;
        }, false)

        getLenBtn.addEventListener('click', () => {
            size.textContent = `当前历史记录栈总数:${history.length}`;
        })

        forward.addEventListener('click', () => history.forward())
        back.addEventListener('click', () => history.back())

        window.addEventListener('popstate', (e) => {
            console.log("state => ", e.state.page)
            loadHTML(e.state.page, content)
        })
    </script>
</body>
</html>

相关推荐

  1. vue-router

    2024-07-21 10:16:01       25 阅读
  2. Vue hash和history区别

    2024-07-21 10:16:01       44 阅读
  3. vue-router(守卫)

    2024-07-21 10:16:01       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 10:16:01       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 10:16:01       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 10:16:01       45 阅读
  4. Python语言-面向对象

    2024-07-21 10:16:01       55 阅读

热门阅读

  1. 【音视频】音频重采样

    2024-07-21 10:16:01       17 阅读
  2. IEEE论文发布

    2024-07-21 10:16:01       20 阅读
  3. 【CSS】基本用法

    2024-07-21 10:16:01       16 阅读
  4. 塔子哥的循环序号-美团2023笔试(codefun2000)

    2024-07-21 10:16:01       16 阅读
  5. sqlalchemy事件监听

    2024-07-21 10:16:01       14 阅读
  6. Nuxt.js与Serverless:构建无服务器应用

    2024-07-21 10:16:01       15 阅读
  7. 九、运算符重载

    2024-07-21 10:16:01       14 阅读
  8. SpringBoot集成MyBatis的步骤是什么?

    2024-07-21 10:16:01       17 阅读
  9. 【阿里OSS文件上传】SpringBoot实现阿里OSS对象上传

    2024-07-21 10:16:01       17 阅读