面试官:说说单页面应用(SPA)的前端路由原理?

65 阅读3分钟

前言

单页面应用(SPA)中那个必问的前端路由原理。别说你没被问过这个问题,除非你还没参加过前端面试!掌握了这个,不仅能轻松应对面试,更能让你在实际开发中游刃有余。准备好了吗?咱们开始!

1. 什么是前端路由?

简单来说,前端路由就是让页面不刷新却能切换内容的神奇技术。就像变魔术一样,URL变了,内容也变了,但页面完全没有重新加载!

传统网站:点击链接 → 页面刷新 → 显示新内容
SPA网站:点击链接 → URL变化 → JS切换内容 → 完事!

2. 两种实现方式

2.1 Hash模式(#号大法)

这是最经典的方式,利用URL中的hash(#号后面的部分):

// 改变hash不会导致页面刷新
window.location.hash = '/home';

// 监听hash变化
window.addEventListener('hashchange', () => {
    const path = window.location.hash.slice(1); // 去掉#
    renderContent(path); // 根据路径渲染内容
});

面试要点:

  • ✅ 兼容性好(IE8都支持)
  • ✅ 实现简单
  • ❌ URL不太美观(总是带着#)

2.2 History模式(H5新特性)

HTML5带来了更优雅的History API:

// 添加历史记录但不刷新页面
history.pushState({}, '', '/home');

// 监听前进后退
window.addEventListener('popstate', (event) => {
    const path = window.location.pathname;
    renderContent(path);
});

面试要点:

  • ✅ URL美观(没有#)
  • ✅ 功能更强大
  • ❌ 需要服务端配合(避免404)
  • ❌ 兼容性稍差(IE10+)

3. 手写简易路由实现

面试官最爱让你手写代码了,来,看这个:

class MiniRouter {
    constructor(mode = 'hash') {
        this.routes = {};
        this.mode = mode;
        this.init();
    }

    // 初始化
    init() {
        if (this.mode === 'hash') {
            window.addEventListener('hashchange', () => this.handleChange());
            this.handleChange(); // 初始处理
        } else {
            window.addEventListener('popstate', () => this.handleChange());
            // 拦截链接点击
            document.addEventListener('click', (e) => {
                if (e.target.tagName === 'A') {
                    e.preventDefault();
                    this.navigate(e.target.getAttribute('href'));
                }
            });
        }
    }

    // 添加路由
    addRoute(path, callback) {
        this.routes[path] = callback;
    }

    // 导航
    navigate(path) {
        if (this.mode === 'hash') {
            window.location.hash = '#' + path;
        } else {
            history.pushState({}, '', path);
            this.handleChange();
        }
    }

    // 处理路由变化
    handleChange() {
        let path;
        if (this.mode === 'hash') {
            path = window.location.hash.slice(1) || '/';
        } else {
            path = window.location.pathname;
        }
        
        if (this.routes[path]) {
            this.routes[path]();
        } else {
            this.routes['/404']?.(); // 调用404页面
        }
    }
}

// 使用示例
const router = new MiniRouter('history');
router.addRoute('/', () => console.log('首页'));
router.addRoute('/about', () => console.log('关于页'));

4. 面试常问问题

4.1 Hash和History模式有什么区别?

特性Hash模式History模式
URL美观度有#号,不太美观无#号,美观
兼容性IE8+IE10+
服务端配置不需要特殊配置需要配置支持
SEO相对较差相对较好

4.2 为什么History模式需要服务端配置?

因为如果你直接访问/about这样的路径,服务端可能没有这个真实路径,就会返回404。解决方案是让服务端把所有路由都指向index.html,让前端路由来处理。

4.3 如何实现路由守卫?

// 简单的路由守卫实现
class RouterWithGuard extends MiniRouter {
    constructor() {
        super();
        this.beforeEachHooks = [];
    }

    beforeEach(callback) {
        this.beforeEachHooks.push(callback);
    }

    navigate(path) {
        // 执行所有守卫
        let shouldProceed = true;
        for (const hook of this.beforeEachHooks) {
            if (hook(path) === false) {
                shouldProceed = false;
                break;
            }
        }
        
        if (shouldProceed) {
            super.navigate(path);
        }
    }
}

5. 实战技巧

5.1 路由懒加载

// 动态导入实现懒加载
router.addRoute('/settings', () => {
    import('./Settings.js').then(module => {
        module.default(); // 渲染设置页面
    });
});

5.2 路由参数解析

// 支持动态参数的路由
router.addRoute('/user/:id', (params) => {
    console.log('用户ID:', params.id);
});

// 在handleChange中需要添加参数解析逻辑

6. 总结

前端路由是SPA的核心技术,面试必问!记住这几个关键点:

  1. 两种模式:Hash简单兼容性好,History美观但需要服务端配合
  2. 核心API:hashchange、pushState、popstate
  3. 实现思路:路由表 + 事件监听 + 内容渲染
  4. 进阶功能:路由守卫、懒加载、参数解析