零、引言
本篇是关于 window.location (history/hash) 的尝试,算是为了学习各种 router 的基础吧。
参考资料:
1. url 中的 hash;
一、基础准备
基础的 index.html 页面。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>js - router</title> </head> <body> <ul> <!-- 定义路由 --> <li><a href="#/home">home</a></li> <li><a href="#/about">about</a></li> <!-- 渲染路由对应的 UI --> <div id="routeView"></div> </ul> <!-- 不同场景下引入对应文件 --> <!-- <script src="hash.js"></script> --> <!-- <script src="history.js"></script> --> </body> </html>
二、hash(#)
最开始是用在 a 标签的 href 中,用来跳转到页面的指定位置,当然,因为经常与 id 选择器一起使用,所以一开始经常认为单纯的是 id 选择器。在 H5 更新的标准中,它有了更多的意义。
上面参考资料中提及的需要注意的地方是 4.5.7 三点,因此也有了我们接下来的实践:
// 维护 UI 页面 let routerView = null; // 路由变化时,根据路由渲染对应的 ui 页面 function onHashChange() { switch (window.location.hash) { case ‘‘: case ‘#/home‘: routerView.innerHTML = ‘Home‘; return ; case ‘#/about‘: routerView.innerHTML = ‘About‘; break; default: break; } } // 页面加载完不会触发 hashchange,这里主动触发一次 hashchange 事件。 window.addEventListener(‘DOMContentLoaded‘, () => { routerView = document.querySelector(‘#routeView‘); onHashChange(); }) // 监听路由变化。 window.addEventListener(‘hashchange‘, onHashChange);
三、history
history 在 H5 标准中新增了 pushState/replaceState 两个 api,这两均能够修改浏览器的历史记录,同时也方便了在写代码的过程中对页面流向的控制,不过也存在些问题,下一个章节进行比较。
先看实践:
// 维护 UI 页面。 let routerView = null; // 路由变化时,根据路由渲染对应 UI 页面。 function onPopState() { switch (window.location.pathname) { case ‘/‘: case ‘/home‘: routerView.innerHTML = ‘Home‘; return ; case ‘/about‘: routerView.innerHTML = ‘About‘; break; default: break; } } // 页面加载完不会触发 onPopState, 这里主动触发一次 onPopState 事件。 window.addEventListener(‘DOMContentLoaded‘, () => { routerView = document.querySelector(‘#routeView‘); // 刷新页面。 onPopState(); // 拦截 <a> 标签点击事件默认行为, 点击时使用 pushState 修改 URL并更新手动 UI,从而实现点击链接更新 URL 和 UI 的效果。 let links = document.querySelectorAll(‘a[href]‘); links.forEach(el => { el.addEventListener(‘click‘, e => { e.preventDefault(); // 手动拦截 window.history.pushState(null, ‘‘, el.getAttribute(‘href‘)); onPopState(); }) }) }) // 监听路由变化。 window.addEventListener(‘popstate‘, onPopState);
和 hash 的处理思路相同,调用的 api 略有区别,增加了取消 DOM 元素的原生事件步骤。
四、两者的特点
1. hash -- 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。比如这个 URL:http://www.abc.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
2. history -- 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
所以呢, 在 hash 模式下,无论怎么刷新网页,浏览器一直是请求的同一个 index.html 文件,所以服务器不需要做额外的处理,只是显示在 url 栏中不那么美观。但是在 history 模式下,前进/后退等操作也只是修改的本地浏览器的历史栈,不发请求,刷新就不同了,这个操作会实打实地去请求当前 url 对应的资源,这意味着服务器不做处理,分分钟是 404,因此 vue-router 文档中强调开启 history 需要服务器支持。