路由(Router)原理极简说明

在各种框架中,路由的实现主要有两种方式:

  1. 用 hashchange 事件监听 url 来实现;
  2. 用 history.pushState() + popstate 来实现。

先看第一种,用 hashchange 实现路由相当简单:

// html
<a href="#/">首页</a>
<a href="#/blog">博客</a>
<a href="#/college">学院</a>

当点击 a 标签,hash 值发生变化,触发事件,回调函数可得到新 url 的 hash 值:

// js
window.addEventListener('hashchange', () => {
    console.log(location.hash.slice(1))		// 得到 '/'、'/blog' 或 '/college'
})

配置好之后如下:

window.addEventListener('hashchange', () => {
    const currHash = location.hash.slice(1),
        view = document.querySelector('#view');
    if (currHash == '/blog') {
        view.innerHTML = '博客';
    } else if (currHash == '/college') {
        view.innerHTML = '学院';
    } else {
        view.innerHTML = '首页';
    }
})

简单封装如下:

// 路由配置
const routes = [
    { path: '/', page: '首页' },
    { path: '/blog', page: '博客' },
    { path: '/college', page: '学院' },
]

// 路由方法
function route() {
    const currHash = location.hash.slice(1),
        view = document.querySelector('#view');

    let page = '';
    for (let i = 0; i < routes.length; i++) {
        if (routes[i].path == currHash) {
            page = routes[i].page;
            break;
        }

    }
    view.innerHTML = page;
}

// 路由注册
window.addEventListener('hashchange', () => {
    route()
})
window.addEventListener('load', () => {
    route()
})

如果仿 vue-router 的用法,封装后大概这样:

// html
<router>
    <a href="#/">首页</a>
    <a href="#/blog">博客</a>
    <a href="#/college">学院</a>
</router>

<router-view></router-view>

// js
// 路由构造函数
function Router(routes) {
    if (this instanceof Router) {
        this.routes = routes.routes;
        this.init();
    }
    else {
        return new Router(routes);
    }
}
// 路由刷新
Router.prototype.refresh = function () {
    const currHash = location.hash.slice(1);
    for (let i = 0; i < this.routes.length; i++) {
        if (this.routes[i].path == currHash) {
            document.querySelector('router-view').innerHTML = this.routes[i].component;
            break;
        }
    }
}
// 路由初始化
Router.prototype.init = function () {
    window.addEventListener('hashchange', this.refresh.bind(this))
    window.addEventListener('load', this.refresh.bind(this))
}

// 路由配置
const routes = [
    { path: '/', component: '首页' },
    { path: '/blog', component: '博客' },
    { path: '/college', component: '学院' },
]
// 路由使用
new Router({
    routes
})

当然,这只是模仿 vue 路由的部分功能和使用,并不是 vue 路由源码,再看看另一种模式 history.pushState() + popstate :

// html
<router>
    <a href="/">首页</a>
    <a href="/blog">博客</a>
    <a href="/college">学院</a>
</router>
<router-view></router-view>

// js部分
// 路由配置
const routes = [
    { path: '/', component: '首页' },
    { path: '/blog', component: '博客' },
    { path: '/college', component: '学院' }
]

// 路由刷新
function refresh(path) {
    for (let i = 0; i < routes.length; i++) {
        if (routes[i].path == path) {
            document.querySelector('router-view').innerHTML = routes[i].component;
            break;
        }
    }
}

// 监听onpopstate事件
window.addEventListener('onpopstate', e => {
    const state = e.state.route;
    refresh(state);
})

// 监听点击事件
document.querySelector('router').addEventListener('click', function (e) {
    if (e.target.localName == 'a') {
        const path = e.target.getAttribute('href');
        refresh(path);
        history.pushState({ route: path }, null, path);
        e.preventDefault();
    }
})

封装之后:

// 构造函数
function Router(routes) {
    if (this instanceof Router) {
        this.routes = routes.routes;
        this.init();
    } else {
        return new Router()
    }
}
// 路由刷新
Router.prototype.refresh = function () {
    const view = document.querySelector('router-view');
    for (let i = 0; i < this.routes.length; i++) {
        if (this.routes[i].path == this.path) {
            view.innerHTML = this.routes[i].component;
            break;
        }
    }
}
// 路由初始化
Router.prototype.init = function () {
    window.onpopstate = e => {
        this.path = e.state.route;
        this.refresh();
    }
    document.querySelector('router').addEventListener('click', e => {
        if (e.target.localName == 'a') {
            e.preventDefault();
            this.path = e.target.getAttribute('href');
            this.refresh();
            history.pushState({ route: this.path }, null, this.path);
        }
    })
}


// 路由配置
const routes = [
    { path: '/', component: '首页' },
    { path: '/blog', component: '博客' },
    { path: '/college', component: '学院' }
]

// 路由使用
new Router({
    routes
})

history 的详细解读另写一篇 history详解


一句话日记:有事干的时候才觉得时间太珍贵

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值