history
为了可以方便地操作、访问浏览器的历史,浏览器提供了window.history
对象,通过调用go
、back
、forward
等方法实现浏览器的前进后退。
HTML5新增内容
在最新的HTML5标准中,可以操作history栈中的内容。
pushState
在传统的web访问体验中,点击一个新链接会表现为页面的全量加载。但是在HTML5的加持下,可以利用pushState
实现只修改URL而不全量加载(例如各种各样的单页应用SPA—Single Page Application),局部更新页面dom加快了访问速度、改善了web体验。
pushState
使用如下,data
为存放到history栈的数据,title
是网页的标题,url
是浏览器地址栏需要展示的地址。使用pushState
跳转的页面会添加历史。
history.pushState(data, title, url)
注意:
replaceState
修改当前历史,不添加新历史。使用同pushState
。
popstate
当浏览历史发生改变时,会触发popstate
事件。如果是pushState
/replaceState
创建的历史,popstate
触发时可以获取到pushState
/replaceState
调用时的第一个参数data
的数据拷贝。下面是一个简单的例子,模拟了pushState
和popstate
的使用。
<!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="forward">前进</button>
<button id="backward">后退</button>
<script>
let count = 1;
document.getElementById('forward').addEventListener('click', () => {
history.pushState({ count }, 'count: ' + count, '?count=' + count)
count += 1
})
document.getElementById('backward').addEventListener('click', () => {
if (count === 1) return;
count -= 1;
history.back()
})
window.addEventListener('popstate', (event) => {
console.log(event)
})
</script>
</body>
</html>
popstate
的数据可以直接从回调的事件中获取event.state
。当然,也可以直接调用history.state
获取当前历史中存放的数据。
location
谈到history
必然离不开location
,这边简单介绍一下location。
类型说明
// location的TS定义
interface Location {
/**
* Returns a DOMStringList object listing the origins of the ancestor browsing contexts, from the parent browsing context to the top-level browsing context.
*/
readonly ancestorOrigins: DOMStringList;
/**
* Returns the Location object's URL's fragment (includes leading "#" if non-empty).
*
* Can be set, to navigate to the same URL with a changed fragment (ignores leading "#").
*/
hash: string;
/**
* Returns the Location object's URL's host and port (if different from the default port for the scheme).
*
* Can be set, to navigate to the same URL with a changed host and port.
*/
host: string;
/**
* Returns the Location object's URL's host.
*
* Can be set, to navigate to the same URL with a changed host.
*/
hostname: string;
/**
* Returns the Location object's URL.
*
* Can be set, to navigate to the given URL.
*/
href: string;
toString(): string;
/**
* Returns the Location object's URL's origin.
*/
readonly origin: string;
/**
* Returns the Location object's URL's path.
*
* Can be set, to navigate to the same URL with a changed path.
*/
pathname: string;
/**
* Returns the Location object's URL's port.
*
* Can be set, to navigate to the same URL with a changed port.
*/
port: string;
/**
* Returns the Location object's URL's scheme.
*
* Can be set, to navigate to the same URL with a changed scheme.
*/
protocol: string;
/**
* Returns the Location object's URL's query (includes leading "?" if non-empty).
*
* Can be set, to navigate to the same URL with a changed query (ignores leading "?").
*/
search: string;
/**
* Navigates to the given URL.
*/
assign(url: string): void;
/**
* Reloads the current page.
*/
reload(): void;
/** @deprecated */
reload(forcedReload: boolean): void;
/**
* Removes the current page from the session history and navigates to the given URL.
*/
replace(url: string): void;
}
location中属性说明
下面举MDN上面的例子介绍location
的属性。
http://example.com:8888/foo/bar?q=baz#bang
对应location中的各个部分。
属性 | 内容 | 说明 |
---|---|---|
href |
http://example.com:8888/foo/bar?q=baz#bang |
|
origin |
http://example.com:8888 |
protocal + hostname + port |
protocal |
http: |
|
host |
example.com:8888 |
|
hostname |
example.com |
|
port |
8888 |
|
pathname |
/foo/bar |
|
search |
?q=baz |
|
hash |
#bang |
location
中还有一个只读属性ancestorOrigins
,在iframe中可获取外部父页面的域名信息(可用来判断当前文档在iframe中)。
location中方法使用
如果不需要HTML5 History存储对象,直接使用
location
的API也可实现页面历史操作。并且,
location
使用时没有同域的限制。
-
assign
location.assign(url)
作用等同于location = url
(也可以location.href = url
、location.hash = xxx
等直接赋值)。 -
reload
页面刷新。
-
replace
当前页面地址重定向访问。
Vue-router
最近在看Vue-router
的实现代码,文章最后就简单介绍一下路由导航的实现。
初始化
vue项目使用Vue-router
时,都会使用new VueRouter({ routes })
的形式构建出可供Vue实例读取的Router对象。
new
操作相应的源码就是Link。
路由模式
Vue-router
有三种路由模式: history
、hash
、abstract
。
-
history
这个模式使用
history.pushState
作为历史切换的方式,URL比较正常例如http://example.com/dir/subdir
。对应源码是HTML5History类
但是使用时需要注意的是,因为浏览器url访问的规则,例如
http://example.com/dir/subdir
会访问服务器路径/dir/subdir
的文件(subdir.html
、subdir/index.html
或同路径服务等等),对于单页应用来说这个路径并没有相应的文件。所以需要在服务端配置路径访问404时返回单页应用的index.html
文件。 -
hash
这个模式使用
location
的hash
作为单页应用各个路由的访问路径(例如http://example.com/dir/#/spaRoute
)。比较简单,不需要服务端额外的404配置。对应的源码是HashHistory类。浏览器路由事件的监听使用了
popstate
或hashchange
(支持HTML5 History就用popstate
,否则使用hashchange
)。 -
abstract
这个模式通常给
nodejs
服务端使用(比如服务端渲染SSR
),可以模拟浏览器端的历史切换。如果Vue-router
运行时的环境不支持浏览器的API,会直接设置成abstract
模式。对应的源码是AbstractHistory类
路由动作
在上面的三种模式中,路由对象都继承了基类History并模拟实现了类似浏览器location API的go
、push
、replace
方法。
同时,history
和hash
模式下监听了浏览器的popstate
、hashchange
等事件,在浏览器前进/后退操作时更新SPA。