# 前端路由模式

单页应用的url改变,不会向服务器发送请求。

那么如何实现那? 有2种方式 hash模式 H5 history 模式

# hash模式原理

基于

  • 改变location.hash不会刷新浏览器
  • 改变location.hash 的值会在浏览器历史中增加一条新记录。
  • 改变location.hash会触发 hashchange 事件

所以 hash模式通过监听hashchange事件来局部更新页面

就是对每个路由配置项添加回调函数,当hash匹配到对应的路由项,更新页面。

// hash 变化,包括:
// a. JS 修改 url
// b. 手动修改 url 的 hash
// c. 浏览器前进、后退
// d. <a href="#/hash">page1</a>

window.onhashchange = (event) => {
    console.log('old url', event.oldURL)
    console.log('new url', event.newURL)

    console.log('hash:', location.hash)
}

// 页面初次加载,获取 hash
document.addEventListener('DOMContentLoaded', () => {
    console.log('hash:', location.hash)
    // 执行显示首页的回调
})

// JS 修改 url
document.getElementById('btn1').addEventListener('click', () => {
    location.href = '#/user'
    // 执行显示/user的回调
})

# location 对象

location 对象不仅保存着当前加载文 档的信息,也保存着把 URL 解析为离散片段后能够通过属性访问的信息。 这些解析后的属性在下表中 有详细说明(location 前缀是必需的)。 假设浏览器当前加载的 URL 是 http://foouser:barpassword@www.wrox.com:80/WileyCDA/?q=javascript#contents location 对象的内容如下表所示。

属 性 说 明
location.hash "#contents" URL 散列值(井号后跟零或多个字符),如果没有则 为空字符串
location.host "www.wrox.com:80" 服务器名及端口号
location.hostname "www.wrox.com" 服务器名
location.href "http://www.wrox.com:80/WileyCDA/?q=javascript#contents" 当前加载页面的完整 URL。location 的 toString()方法返回这个值
location.pathname "/WileyCDA/" URL 中的路径和(或)文件名
location.port "80" 请求的端口。如果 URL中没有端口,则返回空字符串
location.protocol "http:" 页面使用的协议。通常是"http:"或"https:"
location.search "?q=javascript" URL 的查询字符串。这个字符串以问号开头
location.username "foouser" 域名前指定的用户名
location.password "barpassword" 域名前指定的密码
location.origin "http://www.wrox.com" URL 的源地址。只读

# H5 history 模式

基于

  • history.pushState(state, title, url) 不会刷新浏览器
  • history.pushState(state, title, url) 浏览器会添加到历史记录
  • history.pushState会触发popstate事件

所以 H5 history 模式过监听popstate事件来局部更新页面

// 页面初次加载,获取 path
document.addEventListener('DOMContentLoaded', () => {
    console.log('load', location.pathname)
})

// 打开一个新的路由
// 【注意】用 pushState 方式,浏览器不会刷新页面
document.getElementById('btn1').addEventListener('click', () => {
    const state = { name: 'page1' }
    console.log('切换路由到', 'page1')
    history.pushState(state, '', 'page1') // 重要!!
    // 执行显示/page1的回调
})

// 监听浏览器前进、后退
window.onpopstate = (event) => { // 重要!!
    console.log('onpopstate', event.state, location.pathname)
    // 执行显示对应路由的回调
}

Note: 使用H5 history模式,必须要后端支持。就是当手动刷新页面时,后端统一返回index.html 由前端去接管路由。否则会返回404错误。

# history 对象

history 对象表示当前窗口首次使用以来用户的导航历史记录。

// 后退一页
history.go(-1);
// 前进一页
history.go(1);
// 后退一页
history.back();
// 前进一页
history.forward();

let stateObject = {foo:"bar"};
history.pushState(stateObject, "My title", "baz.html");

history.replaceState({newFoo: "newBar"}, "New title");

# 如何选择?

hash 模式相比于 history 模式的优点:

  • 兼容性更好,可以兼容到IE8
  • 无需服务端配合处理非单页的url地址

hash 模式相比于 history 模式的缺点:

  • 看起来更丑。
  • 会导致锚点功能失效。
  • 相同 hash 值不会触发动作将记录加入到历史栈中,而 pushState 则可以。

综上所述,当我们不需要兼容老版本IE浏览器,并且可以控制服务端覆盖所有情况的候选资源时,我们可以愉快的使用 history 模式了。

# 具体实现

链接:https://juejin.cn/post/6844903890278694919