# 为什么要使用路由
越来越多的应用使用 Ajax 请求数据,浏览器 URL 不会发生任何变化。同时,浏览的页面内容在用户下次使用 URL 访问时将无法重新呈现,使用路由可以很好地解决这个问题。
单页面利用了 JavaScript 动态变换网页内容,避免了页面重新加载;路由这提供了浏览器地址变化,网页内容页跟随变化,两个结合提供了体验良好的 单页面应用。
# 前端路由实现方法
路由需要实现以下功能:
- 当浏览器地址变化时,切换页面;
- 点击浏览器【后退】、【前进】按钮时,网页内容跟随变化;
- 刷新浏览器,网页加载当前路由对应的内容。
在单页面 web 网页中,单纯的浏览器地址改变,网页不会重载,如单纯的 hash 网址改变网页不会变化,因此路由主要通过监听事件,并利用 JavaScript 实现动态改变网页内容,有以下实现方法:
- hash 模式: 监听浏览器地址 hash 值变化,执行相应的 JavaScript 切换网页
- history 模式:利用 history API 实现 URL 变化,网页内容跟随变化
二者的区别是 hash 会在浏览器地址后面增加 #
,而 history 可以自定义地址。
# hash 模式
使用 window.location.hash
属性及窗口的 onhashchange
事件 (opens new window),可以实现监听浏览器地址 hash 值变化,执行相应的 JavaScript 切换网页。一些需要注意的地方:
- hash 指地址中
#
以及后面的字符,也叫散列值- 也叫 锚点,本身是用来做页面跳转定位的,如
https://cellinlab.xyz/#/home
的 hash 即#/home
- 也叫 锚点,本身是用来做页面跳转定位的,如
- 散列值不会随请求发送到服务器端,所以改变 hash,不会重新加载页面
- 监听
window
的hashchange
事件,当散列值改变时,可以通过location.hash
来获取和设置 hash 值 location.hash
值的变化会直接反应到浏览器的地址栏
# 触发 hashchange 的情况
- 浏览器地址散列值的变化(包括浏览器的前进、后退)会触发
window.location.hash
值的变化,从而触发onhashchange
事件 - 当浏览器地址栏中 URL 包含 哈希 如
https://cellinlab.xyz/#/home
,此时按下回车,浏览器发送https://cellinlab.xyz/
请求到服务器,请求完毕之后设置散列值为#/home
,此时触发onhashchange
事件 - 当值改变浏览器地址栏 URL 的哈希部分,按下回车,浏览器不会发送任何请求到服务器,只是设置散列值修改,并触发
onhashchange
事件 html
中<a>
标签的属性href
可以设置为页面的元素 ID 如#top
,当点击链接时页面跳转到该 ID 元素所在区域,同时浏览器自动设置window.location.hash
属性,地址栏中的哈希值也会发生变化,并触发onhashchange
事件
window.location.hash = 'home';
let hash = window.location.hash; // '#home'
window.addEventListener('hashchange', function () {
console.log('hashchange');
});
# history 模式
# 概述
window.history
属性指向 History 对象 (opens new window),它表示当前窗口的浏览历史,当发生改变时,只会改变页面的路径,不会刷新页面- History 对象保存了当前窗口访问过的所有页面网址,可以通过
history.length
获知当前窗口访问过的页面数量 - 由于安全原因,浏览器不允许脚本读取这些地址,但允许在地址之间跳转
- 浏览器工具栏的 “前进” 和 “后退” 按钮,其实就是对 History 对象进行操作
# 属性
History 对象主要有两个属性:
History.length
当前窗口访问过的页面数量(含当前页面)History.state
History 堆栈最上层的状态值
history.length; // 1
history.state; // undefined
# 方法
History.back()
移动到上一个网址,相当于点击浏览器后退键,该方法对于第一个访问的页面无效- 注意移动到以前访问的页面时,页面通常是从浏览器缓存中加载,而不是重新请求服务器发送新的网页
History.forward()
移动到下一个网址,相当于点击浏览器前进键,该方法对于最后一个访问的页面无效History.go()
接收一个整数作为参数,以当前网址为基准,移动到参数指定的网址- 如果参数超过实际存在的网址范围,该方法无效果
- 如果不指定参数,默认参数为 0,相当于刷新当前
history.back();
history.forward();
history.go(1); // 相当于 history.forward()
history.go(-1); // 相当于 history.back()
history.go(0); // 相当于刷新当前页面
History.pushState()
用于在历史中添加一条记录
pushState()
不会触发页面刷新,只是导致 History 对象发生变化,地址栏会有变化history.pushState(obj, title, url)
obj
一个对象,通过pushState
可以将该对象内容传递到新页面中,不需要是可以填null
title
指标题,但多数浏览器不支持,建议传空字符串url
新的网址,必须与当前页面处于同一个域,不指定的话则为当前路径,如果设置一个跨域网址会报错
const data = { foo: 'bar' };
history.pushState(data, '', '/about.html');
console.log(history.state); // { foo: 'bar' }
- 注意:如果
pushState
的 URL 参数设置了一个新的锚点值(即 hash),并不会触发hashchange
事件。相反,如果 URL 的锚点值变了,会在 History 对象创建一条浏览记录。 - 如果
pushState()
方法设置了一个跨域网址,会报错
History.replaceState()
用来修改 History 对象的当前记录,用法与 pushState
相同
popstate()
每当 History 对象出现变化时,就会触发 popstate
事件
- 仅仅调用
pushState()
方法或replaceState()
方法,并不会触发该事件 - 只有用户点击浏览器倒退按钮或前进按钮,或者使用 JavaScript 调用
history.back()
、history.forward()
、history.go()
方法,才会触发该事件 - 该事件只针对同一个文档,如果浏览历史的切换导致加载不同的文档,该事件不会触发
- 页面第一次加载时,浏览器不会触发
popstate
事件