📕
余烬的小册
数据结构与算法GitHub
  • 总述
  • 经验记录
    • 经验总结
      • web component
      • 前端性能优化总结与分析
      • 我的长列表优化方案
      • 双向通讯解决方案
      • 🔧基于istanbul实现代码测试覆盖率工具
      • 表单系统(低代码表单)
      • 跨端小程序
      • 设计一个即时聊天功能
      • 跨页面通讯 3658699fe4cb4d0bbe22b0881390bacd
    • 踩坑记录
      • HTML踩坑记录
      • Flutter踩坑记录
      • CSS踩坑记录
  • 源码解析
    • Vue源码解析
      • Vue2源码解析系列-响应式原理
      • Vue2源码解析系列-模板编译
      • Vue2源码解析系列-渲染系统(待更新)
        • Patch
      • Vue2源码解析系列-调度系统(todo)
      • Vue2组件更新流程(todo)
      • 如何学习Vue源码
      • Vue3源码解析系列-响应系统
      • Vue3源码解析系列-渲染系统
      • Vue3源码解析系列-组件化和渲染优化(todo)
      • Vue router源码解析(todo)
    • React源码解析(todo)
    • 微前端
      • qiankun源码解析(todo)
    • Vite源码解析
      • Vite Client源码
      • Vite Server源码(todo)
  • 前端技术
    • javaScript
      • ES6
        • 变量声明
        • 模块化
        • 箭头函数
        • 你不知道的for...of
        • 新的数据结构Set和Map
        • JavaScript异步编程终极解决方案
        • ES6 Class 3a0c0a225a534984aabe9a943c5df975
      • JavaScript Error
      • JavaScript浅拷贝和深拷贝
      • JavaScript闭包
      • JavaScript最佳实践
      • JavaScript设计模式
      • async函数的polyfill
    • 深入理解JavaScript系列
      • JavaScript中的继承
      • JavaScript原始类型和引用类型
      • JavaScript浅拷贝和深拷贝
      • JavaScript手写系列
      • JavaScript之this
      • 词法环境和环境记录
      • JavaScript内存泄漏
      • 执行上下文
      • 从ECMAScript规范中学习this
    • TypeScript
      • TypeScript基础教程
      • Typescript高级操作
      • TypeScript工具类型
      • Typescript手写实现工具类型
      • Typescript总结(思维导图)
    • 浏览器原理
      • 页面渲染原理
      • 浏览器存储
      • JavaScript事件循环
      • 事件循环
      • 跨域
      • DOM事件流
      • 从输入url到页面渲染
      • 判断节点之间的关系及根据节点关系查找节点
      • history API
    • 跨端技术
      • Flutter
        • Flutter布局组件
    • 前端工程化
      • Babel插件开发指南
      • 循环依赖
      • pm2
    • React
      • React 状态管理
      • React组件通讯
      • Redux入门
      • Flux
      • React Hook(todo)
      • Effect
  • 服务器端
    • 计算机网络
      • 应用层
      • 运输层
      • 物理层
      • 数据链路层
      • HTTP缓存
      • HTTPS
      • 网络层
    • NodeJs
      • Node.js
      • nodejs最佳实践
      • 《深入浅出Nodejs》小结
      • mongoose填充(populate)
      • node事件循环
      • Node子进程
      • nestjs从零开始
      • nodejs流
      • Nodejs调试
      • Koa源码解析
    • 服务器
      • 操作系统
      • Linux
      • nginx常用指令
      • nginx常用配置
    • 数据库
      • Mysql常见语法
      • MongoDB Indexes索引
  • 前端安全与性能优化
    • 前端安全
      • 跨站脚本攻击(XSS)
      • 跨站点请求伪造(CSRF)
      • 点击劫持
      • 中间人攻击
      • 越权攻击与JWT
    • 前端性能优化
      • 前端监控系统
      • 前端性能优化总结与分析 7348bba0918645b1899006dc842a64c1
      • 衡量性能的核心指标 0dc15ef127cf4f4a9f1137c377420292
      • 图片懒加载
  • 杂项
    • 其他
      • Git
      • web component框架
      • 实现滚动框的懒加载
      • Stencil指南
    • CSS
      • 定位和层叠上下文
      • BFC
      • 盒模型
      • css选择器
      • css变量
由 GitBook 提供支持
在本页
  • history API
  • URL跳转
  • 更新URL但不进行跳转
  • history API
  • location.hash
  • location.hash和history.push的区别
在GitHub上编辑
  1. 前端技术
  2. 浏览器原理

history API

history API

history对象表示当前窗口首次使用以来用户的导航历史记录,history对象不会暴露用户已经访问过的URL,但是会提供一些API来实现URL的前进和后退。

URL跳转

window.history 提供三个API来实现页面URL的跳转:

  • go():传递一个整数,正整数表示向前跳多少,负整数表示向后跳多少。

  • forward():向前跳一页,类似浏览器的前进按钮。

  • back(): 向后跳一页,类似浏览器的回退按钮。

例如:

window.history.back();
// 等同于
window.history.go(-1);

window.history.forward();
// 等同于
window.history.go(1);

还可以通过查看长度属性的值来确定的历史堆栈中页面的数量:

window.history.length;

可以用这个属性来判断你的页面是否是用户第一个打开的页面:

if(window.history.length === 1){
	// ...
}

更新URL但不进行跳转

history API

history.pushState

随着前端技术的不断发展,传统的URL跳转API已经不能在满足我们的需求了,例如在SPA应用中,页面跳转本质上并非是向服务器请求新的页面,而是通过JavaScript、DOM API等技术直接由前端改变页面内容,这个时候就需要一种能够实现可以更新URL,但是不需要进行跳转的技术。

  1. 状态对象:状态对象 state 是一个 JavaScript 对象,通过 pushState () 创建新的历史记录条目。无论什么时候用户导航到新的状态,popstate 事件就会被触发,且该事件的 state 属性包含该历史记录条目状态对象的副本。状态对象的大小是有限制的,通常是500KB~1MB以内,如果超过这个限制就会报错。如果你需要更大的空间,建议使用 sessionStorage以及 localStorage 。

  2. 标题,目前没有实现,可以传个空字符串。

  3. URL:要跳转的URL,可以是相对路径URL,也可以是一个绝对路径URL,但是新 URL 必须与当前 URL 同源。

window.history.pushState({a: 1}, '', '/')

window.onpopstate

每当活动的历史记录项发生变化时, popstate事件都会被传递给 window 对象。如果当前活动的历史记录项是被 pushState创建的,或者是由 replaceState改变的,那么 popstate事件的状态属性 state会包含一个当前历史记录状态对象的拷贝。

注意 调用 history.pushState()或者 history.replaceState()不会触发 popstate 事件。popstate事件只会在浏览器某些行为下触发,比如点击后退按钮(或者在JavaScript 中调用 history.back() 方法)。即,在同一文档的两个历史记录条目之间导航会触发该事件。

history.state

history.state可以获取到当前历史记录的状态对象,如果当前没有使用过pushState或者replaceState,则该属性返回null。

刷新

使用history.pushState和history.replaceState 时需要格外注意,当用户点击刷新按钮时页面重新加载,此时浏览器仍然会发送当前URL的http请求到服务器,因此使用这两个API时需要考虑到服务端对这些GET 请求的处理,你可以

  • 在服务端为每个URL设置一个真实的URL地址,返回对应的页面。

  • 将所有URL都指向index.html文件(SPA)

如果你用过vue-router的history模式,那么你应该就有过这种体验,如果你没有配置好服务端,那么当你在某个URL刷新时,会得到一个404的页面,而解决的方法是在服务端为该应用的所有路径都配置返回index.html文件,这背后的原理就是其底层使用的正是history.pushState 。

示例

下面示例中实现了三个按钮,其中pushState每点击一次都会执行一次history.pushState({a: a}),变量a初始等于0,每次使用都会+1,点击replaceState会执行history.replaceState({a: a}) ,点击back按钮时会执行history.back()函数以实现历史记录的回退,这会触发popState事件,此时你应当可以在控制台看到事件对象(event)。

location.hash

另一种能更新URL但不会进行跳转的方法是使用window.location.hash 。window.location 对象保存了当前URL中的各项信息,例如hostname、protocol、port和hash等属性,其中hash属性中保存了当前URL以’#’开头的URL片段,并且更新这部分片段浏览器不会发送新的http请求。

location.hash的一个应用在于页内跳转锚点,例如

<a href="#a">跳转到id=a的元素</a>

当点击该链接时,页面会跳转到对应id的元素上,但是不会发送请求重新加载页面。

window.location.hash 属性可以被修改,并且修改操作会将修改后的URL添加到历史记录中,以便能够实现前进和后退。

window.location.hash = '/s/1'

和history API类似,location.hash也具有相应的事件监听器:hashchange。

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

要注意的是,和前面的popstate事件一样,当修改window.location.hash时并不会触发hashchange事件,只有当前进或者后退,即在两个历史记录间切换时才会触发hashchange事件。

location.hash和history.push的区别

从某种程度来说,调用 pushState() 和 window.location = "#foo"基本上一样,他们都会在当前的 document 中创建和激活一个新的历史记录。但是 pushState() 有以下优势:

  • 新的 URL 可以是任何和当前 URL 同源的 URL。但是设置 [window.location](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/location) 只会在你只设置锚的时候才会使当前的 URL。

  • 非强制修改 URL。相反,设置 window.location = "#foo"; 仅仅会在锚的值不是 #foo 情况下创建一条新的历史记录。

  • 可以在新的历史记录中关联任何数据。window.location = "#foo"形式的操作,你只可以将所需数据写入锚的字符串中。

上一页判断节点之间的关系及根据节点关系查找节点下一页跨端技术

最后更新于1年前

HTML5 引入了 和 方法,它们分别可以添加和修改历史记录条目。他们和前面讲到的go、replace等API很相似,但是最大的区别是这两个方法只会改变URL,不会向服务器发送页面请求。Vue-Router的history模式底层就是使用的这种方法。

和 方法接收三个参数:

和 方法的区别在于前者是新建一个历史记录,而后者是替换当前的历史记录。

history.pushState()
history.replaceState()
history.pushState()
history.replaceState()
history.pushState()
history.replaceState()
https://codesandbox.io/s/history-state-4hqci3?file=/index.html