shou ye

前端 JS 原生JS实现一个单页应用的路由 router

 开篇日常立个flag……

 

前言

最近在做一些应用,类似于单页应用,想实现类似于 Vue 路由的效果。

但是个人 Vue 基础四舍五入约等于无,而且看着 Vue-router 吃力+用不起来(因为我的项目前后端不分离,而且使用的 js 语法基本上停留在远古时代:ES5甚至更久远以前……)

之前尝试过模拟,但是模拟太痛苦了,而且一堆问题,还不好维护。

于是想着自己用原生 js 写一个简单的单页应用路由。

 

效果

想实现的效果,无非是页面内切换不同的组件不会整个页面刷新,且操作“前进”、“后退”按钮页面也会对应响应。

话不多说,先上效果图

 

源码

gitee:https://gitee.com/lisheng741/singlepagerouter/

 

思路与设定

:思路

1、location.hash 可以修改页面的锚点

2、当前页面锚点的改变,会触发 hashchange 事件

这样,注册一个 hashchange 事件,监听 hash 的变化,切换页面指定元素的显示与隐藏,可以达到单页应用的效果。

:一些设定

首先我们约定一个路由对象 route,route 包含三个属性,即 id 、title 和 handle。

接着,我们将会设定一个路由表,记录为 routes,为一个 route 对象的数组。

最后,实现一个路由器 Router 对象,来监听以及操作路由跳转。

三者的定义如下

//route 单个路由对象
var route = { id: 'next', title: '下一页', handler: function () { console.log("切换到next");}

//routes 路由表
var routes = [
    { id: '' },
    { id: 'index', title: '首页' },
    { id: 'next', title: '下一页', handler: function () { console.log("切换到next"); } }
]

//router 路由器
var router = new Router(routes);

route 注释

属性 是否必须 说明
id 操作元素的id,也是hash值(即url的#后面的内容)
title 页面标题,切换路由时一并切换,若没有,则保留上一路由的标题值
handler 类似于回调函数,在切换路由时执行

router 说明

方法 是否可用 参数 说明
init() 是,可多次调用 routes路由表,defaultRoute初始化后默认路由 初始化路由器,必须给router输入routes
push() route路由,要切换的路由 切换路由
changPage() 否,不推荐使用 route路由(字符串) 不推荐直接使用

注:

1、不要对一个 router 进行多次 new Router()操作 。即 var router = new Router() 时,只 new 一次。(否则会对同一个 router 注册多次 hashchange 事件)

2、路由器创建以后,可以使用 router.init() 切换路由表

3、基于1和2,new Router 时,可不传入 routes,但 init() 时,必须传入不为空的 routes。

4、控制 router 跳转路由使用 router.push(route) 方法,输入的 route 可以是路由表的下标,也可以是路由表 id 的字符串,也可以是 route 对象,若没有输入,则默认不会跳转。

 

Router 模块 js代码

偷一下懒,实现过程就不磨鸡了,直接上代码

/*
 * 模块:单页应用路由
 * 作者:lisheng741@qq.com
 * 日期:2021.04.17
 * 使用:var routes = [{ id: '' }, { id: 'index', title: '首页' }, { id: 'next', handler: function () { console.log("切换到next");} }]
 *       var router = new Router(routes);
 * 注意:不要对一个 router 进行多次 new Router()操作 。若要切换路由表使用 init() 方法即可
 */
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 || !routes.length) {
        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 "number":
            route = this.routes[route] || "";
            break;
        case "object":
            route = route.id || "";
            break;
        case "undefined":
        default:
            route = location.hash.slice(1) || "";
            break;
    }
    location.hash = route; //切换hash,接下来的事情交给hashchange去做。如果与上一次的route一致,不会触发hashchange事件
}

//切换页面:route为字符串,为空则隐藏所有路由
Router.prototype.changePage = function(route) {
    if (!this.routes || !this.routes.length) { //没有初始化成功的路由,直接返回
        return;
    }
    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].title) === "string") && routes[i].title && (document.title = routes[i].title);
            (typeof(routes[i].handler) === "function") && routes[i].handler(); //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', title: '首页' },
            { id: 'next', title: '下一页', handle: function () { console.log("切换到next"); console.log(this); } }
        ]

        //路由器
        var router = new Router(routes, 'index');
    </script>
</body>
</html>

  

 参考来源

鱼丸粗面不要香菜 的 使用js实现单页应用路由转跳功能

小蚊 的 用原生js做单页应用(博文挂掉了,链接复制不到)

posted @ 2021-04-17 22:45  芦荟柚子茶  阅读(2225)  评论(1编辑  收藏  举报
ye jiao