前端路由的实现原理

430 阅读1分钟

首先:

路由是什么?

描述的是URL和处理函数之间的一种映射关系,在前端单页应用中路由描述的是URL和UI的映射关系

路由实现的基本思路?

1. 改变URL,页面不刷新

2. 如何检测URL变化?

众所周知,前端路由有两种模式,分别是 hash 和 history

1.hash 模式的简单实现

首先我们构建一个最简单的HTML页面

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div class="pages">
        <ul>
            <li><a href="/home">首页</a></li>
            <li><a href="/about">关于</a></li>
        </ul>
    </div>
    <div id="routerView"></div>
</body>

现在我们往这个HTML中添加JavaScript代码, 首先先拿到routerViewd的HTML结构, 然后给全局添加一个加载完成事件

window.addEventListener('DOMContentLoaded',onHashChange)// DOMContentLoaded 监听页面初次渲染完成 执行onHashChange

    window.addEventListener('DOMContentLoaded',onHashChange)// DOMContentLoaded 监听页面初次渲染完成  执行onHashChange
    let routerView = document.querySelectorAll('#routerView')[0]
    //监听路由的变化
    window.addEventListener('hashchange',onHashChange) //hashchange <-- JS已有的事件
    function onHashChange(){
        console.log(location.hash,'---');
        switch(location.hash){  
            case '#/home':
            routerView.innerHTML = '这里是Home页面'
            return
            case '#/about':
            routerView.innerHTML = '这里是About页面'
            return
            default: return
        }           
    }

这就是hash的简单实现。

总结: 因为hash的改变天生就不会引起页面的刷新,所以在URL后边拼接hash值,再通过监听hashchange事件检测到URL的变化,从而去显示不同的DEMO结构

2.history模式的简单实现(记得开一个live server去看效果,用vscode的可以直接下一个插件名字就叫Live Server)

先简单说一下,实现原理也比较简单,就是劫持了点击事件然后利用了history的pushState来改变后缀,废话不多说,进入主题。

HTML代码和上边的一模一样

现在我们往这个HTML中添加JavaScript代码, 首先先拿到routerViewd的HTML结构, 然后给全局添加一个加载完成事件

window.addEventListener('DOMContentLoaded', onload)

等到加载完成则触发onload事件

function onload() {
    let linkList = document.querySelectorAll('.pages a[href]')
    console.log(linkList);
    console.log(location.pathname);
    linkList.forEach(el => {
        el.addEventListener('click', function (e) {
            e.preventDefault() // 阻止掉href的默认跳转事件
            //手动让url发生变化
            let home = el.getAttribute('href')
            console.log(el.getAttribute('href'));
            history.pushState(null, '',home)
            onPopState()
        })
    })
}

在这我们先去拿到所有的a节点,然后再手动给他添加一个点击事件,再给他的点击事件的默认行为给阻止掉,相信看到这应该也能明白个七七八八了,接下来就是用上我们的history.pushState事件。接下来只需要调用onPopState函数

function onpopstate(){
    switch(location.pathname){
        case '/Home':
        routerView.innerHTML = `<h2>Home页面</h2>`;
        return
        case '/About':
        routerView.innerHTML = `<h2>About页面</h2>`;
        return
        case '/Detail':
        routerView.innerHTML = `<h2>Detail页面</h2>`;
        return
        default:return
    }
`}`

然后就算是完成了history的简陋实现了。

总结: html 提供了一个history对象,该对象提供了pushState 和 replaceState两个方法,history提供了类似于hashchange的事件popState,同时通过a标签和pushState和replaceState改变url是不会触发popState,只有通过浏览器的前进(go)和后退(back)会触发popState