原生JS实现并封装前端路由

版权声明:转载请声明原地址 https://blog.csdn.net/dk2290/article/details/85128771

目前,前端中所有的MVVM框架中基本都有自己的Router组件,比如React-router或者Vue-router,主要的作用就是通过拦截url来返回相应的组件。如果我们通过原生js来实现一个类似的router,应该怎么做呢?本文将提供一个思路和完整demo,以解释其中的原理。


一.效果图

在这里插入图片描述

二.代码示例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <meta name="author" content="[email protected]">
    <title>前端路由</title>
    <style type="text/css">
        .router_box,
        #router-view {
            padding: 0 20px;
            background-color: gainsboro;
            height: 55px;
            line-height: 50px;
        }

        .router_box>a {
            padding: 0 10px;
            color: #364086;
        }

        .content{
            border: 1px solid #a030b3;
        }
    </style>
</head>

<body>
    <div class="router_box">
        <a href="/home" class="router">主页</a>
        <a href="/sort" class="router">分类</a>
        <a href="/image" class="router">图片</a>
        <a href="/own" class="router">我的</a>
    </div>
    <div class="content">
        <iframe name="mainiframe" src="" id="mainiframe" scrolling="no" onload="" frameborder="0" width="100%"></iframe>
    </div>
    <script type="text/javascript">
        (function (win, undefined) {
            function Router(parames) {
                if (!(this instanceof Router)) {
                    return new Router(arguments)
                }
                let router = {};
                router.routes = parames.routes || [];
                router.target = parames.target || "";
                router.index = parames.index || "";

                this.loadIndex(router);

                document.querySelectorAll(".router").forEach((item, index) => {
                    item.addEventListener("click", function (e) {
                        let event = e || win.event;
                        event.preventDefault();
                        win.location.href = `#${this.getAttribute("href")}`;
                    }, false);
                });

                win.addEventListener("hashchange", function () {
                    this.routerLoad(router);
                }.bind(this));
            };

            Router.prototype = {
                routerLoad:  router => {
                    let nowHash = win.location.hash;
                    let index = router.routes.findIndex((item, index) => {
                        return nowHash == ('#' + item.path);
                    });
                    document.querySelector(`${router.target}`).src = router.routes[index].component;
                },
                loadIndex:  router => {
                    document.querySelector(`${router.target}`).src = router.index;
                }
            }
            win.Router = Router;
        })(window, undefined)

        new Router({
            target: '#mainiframe',
            index: '/static/templates/home.html',
            routes: [{
                    path: '/home',
                    component: '/static/templates/home.html'
                },
                {
                    path: '/sort',
                    component: '/static/templates/sort.html'
                },
                {
                    path: '/image',
                    component: '/static/templates/image.html'
                },
                {
                    path: '/own',
                    component: '/static/templates/own.html'
                }
            ]
        });
    </script>
</body>

</html>

服务端(nodejs+express):

const express = require('express');
const path = require('path');
let app = express();

app.use('/static',express.static(path.join('public')));
app.get('/', (req, res) => {
    res.sendFile(__dirname +'/index.html')   
})

app.get('/router', (req, res) => {
    res.sendFile(__dirname +'/router.html')   
})

app.listen(2000)

三.代码分析

我先讲讲实现个路由需要从哪几方面入手。

第一步:监听a标签,并给href里的url加锚
我们知道,一般情况下菜单栏的加载模式中,都是通过<a>中的href='/xxxx'来跳转到指定的页面,所以路由的第一步就是监听到此菜单栏中<a href='/xxx'>的点击事件,并在点击时通过event.preventDefault()阻止浏览器的默认行为。阻止默认行为后,咱们就可以通过#/index这种形式给拿到的url加锚,至于为什么要加锚,第二步中会说明。

第二步:监听hashchange事件,并在监听被触发时加载对应的页面
在第一步中我们给url加上了锚,目的就是通过hashchange函数来监听加了锚之后的url(即hash),监听到hash的变化后,我们可以拿到点击时的url,通过调用Router(params)时传入的params参数来找到此文件的静态路径,然后传入到iframe 的 src中。

注意:在这个例子中我使用的iframe来替代组件,在实际业务情景中,你当然也可以将这个iframe换成一个主容器(<div class="content"></div>之类的),然后在’component’属性中传入组件的具体内容,或者文件路径,都是可以的。

第三步:在Router调用时添加目标容器,添加首页加载
因为我在这个例子中使用的内容容器是个iframe,所以我需要考虑多个iframe嵌套下的Loader目标(target)问题,当然,首页也是需要可以设置默认加载的。
所以,在new Roter()时我们可以看到这两个参数:
在这里插入图片描述

四.注意事项

1.这段代码的意义是将函数内this环境绑定到函数内,而不是window。
在这里插入图片描述
当然,你也可以这样:

let _this = this;

2.要注意箭头函数中this的指向(箭头函数没有自己的this)
3.如果想把这个demo完整的跑起来,需要安装node环境,然后npm install express

猜你喜欢

转载自blog.csdn.net/dk2290/article/details/85128771
今日推荐