现代前端项目多为单页Web应用(SPA),在单页Web应用中路由是其中的重要环节。SPA 是 single page web application 的简称,译为单页Web应用。
简单的说 SPA 就是一个WEB项目只有一个 html 页面,一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转。 取而代之的是利用 JS 动态的变换 HTML 的内容,从而来模拟多个视图间跳转。
简单的说,就是在保证只有一个 HTML 页面,且与用户交互时不刷新和跳转页面的同时,为 SPA 中的每个视图展示形式匹配一个特殊的 url。在刷新、前进、后退和seo时均通过这个特殊的 url 来实现。
我们需要实现下满两点:
在url后面加上#,如 http://127.0.0.1:5500/前端路由/hash.html#/page1 这个url后面的 #/page1 就是hash值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<ul>
<li><a href="#/">/</a></li>
<li><a href="#/page1">page1</a></li>
<li><a href="#/page2">page2</a></li>
</ul>
<div class="content-div"></div>
</body>
<script>
class RouterClass {
constructor() {
this.routes = {}; // 记录路径标识符对应的cb
this.currentUrl = ""; // 记录hash只为方便执行cb
window.addEventListener("load", () => this.render());
window.addEventListener("hashchange", () => this.render());
}
/* 初始化 */
static init() {
window.Router = new RouterClass();
}
/* 注册路由和回调 */
route(path, cb) {
this.routes[path] = cb || function() {};
}
/* 记录当前hash,执行cb */
render() {
this.currentUrl = window.location.hash.slice(1) || "/";
this.routes[this.currentUrl]();
}
}
RouterClass.init();
const Contentdom = document.querySelector(".content-div");
const changeContent = content => (ContentDom.innerHTML = content);
Router.route("/", () => changeContent("默认页面"));
Router.route("/page1", () => changeContent("page1页面"));
Router.route("/page2", () => changeContent("page2页面"));
</script>
</html>
History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。可以参考下两篇文章对history的说明
https://css-tricks.com/using-the-html5-history-api/
https://developer.mozilla.org/zh-CN/docs/Web/API/History
下面介绍在这个模式下需要用到的api
history.go(n) :路由跳转几步,n为2往前跳转2个页面,-2往后跳转两个页面
history.back() :路由后退,相当于 history.go(-1) ,用户可点击浏览器左上角的后退按钮模拟此方法
history.forward() :路由前进,相当于 history.go(1) ,用户可点击浏览器左上角的前进按钮模拟此方法
history.pushState() :添加一条路由历史记录,如果设置跨域网址则报错
history.pushState 用于在浏览历史中添加历史记录,但是并不触发跳转,此方法接受三个参数,依次为:
state :一个与指定网址相关的状态对象, popstate 事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填 null 。 title :新页面的标题,但是所有浏览器目前都忽略这个值,因此这里可以填 null 。 url :新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
当活动历史记录条目更改时,将触发popstate事件。如果被激活的历史记录条目是通过对history.pushState()的调用创建的,或者受到对history.replaceState()的调用的影响,popstate事件的state属性包含历史条目的状态对象的副本。
需要注意的是调用history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back())
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<ul>
<li><a href="/">/</a></li>
<li><a href="/page1">page1</a></li>
<li><a href="/page2">page2</a></li>
</ul>
<div class="content-div"></div>
</body>
<script>
class RouterClass {
constructor(path) {
this.routes = {}; // 记录路径标识符对应的cb
history.replaceState({ path }, null, path); // 进入状态
this.routes[path] && this.routes[path]();
window.addEventListener("popstate", e => {// 当用户点击浏览器的前进或者后退触发
console.log(e.state)
const path = e.state && e.state.path;
this.routes[path] && this.routes[path]();
});
}
/* 初始化 */
static init() {
window.Router = new RouterClass(location.pathname);
}
/* 注册路由和回调 */
route(path, cb) {
this.routes[path] = cb || function() {};
}
/* 跳转路由,并触发路由对应回调 */
go(path) {
history.pushState({ path }, null, path);
console.log(history);
this.routes[path] && this.routes[path]();
}
}
RouterClass.init();
const ul = document.querySelector("ul");
const ContentDom = document.querySelector(".content-div");
const changeContent = content => (ContentDom.innerHTML = content);
Router.route("/", () => changeContent("默认页面"));
Router.route("/page1", () => changeContent("page1页面"));
Router.route("/page2", () => changeContent("page2页面"));
ul.addEventListener("click", e => {
console.log(e.target.tagName);
if (e.target.tagName === "A") {
e.preventDefault();
Router.go(e.target.getAttribute("href"));
}
});
</script>
</html>
Hash 模式是使用 URL 的 Hash 来模拟一个完整的 URL,因此当 URL 改变的时候页面并不会重载。History 模式则会直接改变 URL,所以在路由跳转的时候会丢失一些地址信息,在刷新或直接访问路由地址的时候会匹配不到静态资源。因此需要在服务器上配置一些信息,让服务器增加一个覆盖所有情况的候选资源,比如跳转 index.html 什么的
优点
ie8
hash
ajax
缺点
对于部分需要重定向的操作,后端无法获取 hash 部分内容,导致后台无法取得 url 中的数据,典型的例子就是微信公众号的 oauth 验证
服务器端无法准确跟踪前端路由信息
对于需要锚点功能的需求会与目前路由机制冲突
在使用vue-cli搭建的环境中,浏览器上URL地址中是存在#的,这是由于vue-router 默认 hash 模式,不难发现#的出现真的很丑陋。官网给出了如何使用history模式mode: history
vue中路由传参主要的3种方式:query方式(push时使用path来匹配)、params模式(push时使用name来匹配)、location预声明参数模式(push使用path来匹配,但是它跟params模式不同)
我们的通用的后台管理系统中,我们会根据权限的粗细不同,会对每个角色每个权限每个资源进行控制。同样的我们也需要实现一个这样的功能。 这篇文章我将主要讲vue端的实现,关于后台接口我就不会涉及,当我接触的时候我们的后台接口是springcloud实现。
前后端分离开发模式,后端会把路由控制丢在前端,这几天再开发单页面小的项目,手动撸了个路由。前端路由实现有两种方法。HTML5 History API包括2个方法:history.pushState()和history.replaceState(),和1个事件:window.onpopstate。hash + location.onhashchange
在后台管理系统中,一般都会采用权限管理。路由菜单数据都会保存到数据库中,在vue-router 2.2版本新增了一个router.addRoutes(routes)方法,即可用它来实现动态路由了
vue-router 默认 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,于是当 URL 改变时,页面不会重新加载。如果不想要很丑的 hash,我们可以用路由的 history 模式,这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。
原本想用动态路由的思路去做,按权限加载对应路由表,但是由于权限可以交叉(比如一个人可以同时是主题管理员和数据服务管理员),导致权限路由表还是得去做判断组合。于是放弃了这个思路,索性就在beforeEach里直接判断了。
使用vue-cli构建项目后,我们会在Router文件夹下面的index.js里面引入相关的路由组件,webpack在打包的时候会把整个路由打包成一个js文件,如果页面一多,会导致这个文件非常大,加载缓慢
query和params的区别,query相当于get请求,在页面跳转的时候,可以在地址栏看到请求参数,然而params则相当于post请求,参数不会在地址栏中显示。
当服务端接收到HTTP请求时,可以通过onRequest() 获取到url, pathname,query,及paramParams参数;为了解析这些数据需要使用url和querystring模块
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!