开篇日常立个flag……
前言
最近在做一些应用,类似于单页应用,想实现类似于 Vue 路由的效果。
但是个人 Vue 基础四舍五入约等于无,而且看着 Vue-router 吃力+用不起来(因为我的项目前后端不分离,而且使用的 js 语法基本上停留在远古时代:ES5甚至更久远以前……)
之前尝试过模拟,但是模拟太痛苦了,而且一堆问题,还不好维护。
于是想着自己用原生 js 写一个简单的单页应用路由吧。
效果
话不多说,先上效果图
源码
gitee:https://gitee.com/chen3322275/singlepagerouter/
思路与设定
-思路
1、location.hash 可以修改页面的锚点
2、当前页面锚点的改变,会触发 hashchange 事件
这样,注册一个 hashchange 事件,监听 hash 的变化,切换页面指定元素的显示与隐藏,可以达到单页应用的效果。
-一些设定
首先我们约定一个路由对象 route,route 包含两个属性,即 id 和 handle。
接着,我们将会设定一个路由表,记录为 routes,为一个 route 对象的数组。
最后,实现一个路由器 Router 对象,还监听以及操作路由跳转。
三者的定义如下:
//route 单个路由对象 var route = { id: 'next', handle: function () { console.log("切换到next");} //routes 路由表 var routes = [{ id: 'index' }, { id: 'next', handle: function () { console.log("切换到next");} }] //router 路由器 var router = new Router(routes);
注释:
route 对象的 id,除了为操作元素(一般为 div)的 id 外,还将会是 hash 的值。
handle 为一个函数,类似于回调函数,在切换该路由时执行。
若 id 为空,则隐藏路由表所有路由。
Router 模块 js代码
直接上代码
/* * 模块:单页应用路由 * 作者:cls * 日期:2021.04.17 * 使用:var routes = [{ id: 'index' }, { id: 'next', handle: function () { console.log("切换到next");} }] * var router = new Router(routes); */ function Router(routes, defaultRoute) { var othis = this; //路由初始化 routes && othis.init(routes, defaultRoute); //绑定 hashchange 事件 window.addEventListener("hashchange", function() { let route = location.hash.slice(1) || ""; othis.oldRoute = othis.currentRoute; othis.currentRoute = route; othis.changePage(route); }); } //初始化,可多次初始化 Router.prototype.init = function(routes, defaultRoute) { if (routes == undefined || routes.length == undefined || routes.length == 0) { console.error("Router初始化失败:routes错误!"); return; } this.routes = routes; this.currentRoute = location.hash.slice(1) || defaultRoute || routes[0].id; //当前路由获取顺序 this.oldRoute = ""; location.hash || history.replaceState(null, null, '#' + this.currentRoute); //hash为空,切换当前hash this.changePage(this.currentRoute); this.oldRoute = location.hash; } //切换路由 Router.prototype.push = function(route, callback) { //获取route switch (typeof(route)) { case "string": break; case "undefined": route = location.hash.slice(1) || ""; break; case "number": route = this.routes[route] || ""; break; case "object": route = route.id || ""; break; } location.hash = route; //切换hash,接下来的事情交给hashchange去做。如果与上一次的route一致,不会触发hashchange事件 } //切换页面:route为字符串,为空则隐藏所有路由 Router.prototype.changePage = function(route) { for (let i = 0; i < this.routes.length; i++) { let e = document.getElementById(routes[i].id); if (routes[i].id == route) { e && (e.style.display = "block"); (typeof(routes[i].handle) === "function") && routes[i].handle(); //handle 存在,执行函数 } else { e && (e.style.display = "none"); } } }
测试页面的 Html 代码
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>单页路由测试</title> </head> <body> <div> <h1>Welcome</h1> <a href="#">空的</a> <a href="#index">index啊啊啊</a> <button onclick="router.push('next')">next测试</button> <div id="index"> index啊啊啊 </div> <div id="next"> next啊啊啊 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊 </div> </div> <script src="~/js/site.js"></script> <script> //路由表 var routes = [ { id: '' }, { id: 'index' }, { id: 'next', handle: function () { console.log("切换到next"); console.log(this); } } ] //路由器 var router = new Router(routes, 'index'); </script> </body> </html>
使用说明
1、整个应用生命周期,只创建一次路由器,即 var router = new Router() 时,只 new 一次。(否则会注册多次 hashchange 事件)
2、路由器创建以后,可以使用 router.init() 切换路由表
3、控制 router 跳转路由使用 router.push(route) 方法,输入的 route 可以是路由表的下标,也可以是路由表 id 的字符串,也可以是 route 对象,若没有输入,则默认不会跳转。
参考来源
小蚊 的 用原生js做单页应用(博文挂掉了,链接复制不到)