前端面试100道手写题(5)—— Router路由

简介: 前端路由,大家都使用过,那么有没有想过它是怎么实现的吗?如:Vue-Router 或者 React-Router。或许有个大概印象,但是真正要自己去实现还是没有什么思路,那么这篇文章将完整的实现思路去实现一次。

前言

前端路由,大家都使用过,那么有没有想过它是怎么实现的吗?如:Vue-Router 或者 React-Router。或许有个大概印象,但是真正要自己去实现还是没有什么思路,那么这篇文章将完整的实现思路去实现一次。

手写难度:⭐️⭐️⭐️

涉及知识点:

  • history api 和监听事件
  • onhashchange 监听事件
  • Web Component 自定义组件

路由管理

路由管理,是指的 web 应用在浏览器下根据不同的url地址展示不同的内容或者页面。

不管是 Vue-Router 或者 React-Router,基本上都是基于浏览器两种路由控制有一定了解,如下:

  • hash,代表网页中的一个位置,通常用来做锚点使用,后面被用于单页web 应用的路由控制
  • history,代表网页的历史记录,同时提供接口操作浏览器的曾经在标签页或者框架里访问的会话历史记录

下面我们对两个进行简单了解。

Hash

Hash,通常是指的浏览器 URL 地址中带#的值,如:URL = https://baidu.com/#/page1,那么 URL.hash='#/page1'

Hash 常用的 的几个方法:

hashchange

当 URL 的片段标识符更改时,将触发hashchange事件

window.addEventListener('hashchange', function() {
   
  console.log('The hash has changed!')
}, false);

调整 hash

除了监听改变之外,我们还需要对Hash 自由调整,如:添加或者修改,代码如下:

location.hash = '#/page2'

History

History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录。

History 提供的 API 接口:

  • pushState(state, unused, url) 按指定的名称和 URL(如果提供该参数)将数据 push 进会话历史栈 如:history.pushState({page: 1}, "title 1", "?page=1")
  • replaceState(state, unused, url) 按指定的数据、名称和 URL(如果提供该参数),更新 history 栈上最新的条目 如:history.replaceState({page: 3}, "title 3", "?page=3");
  • back() 转到浏览器会话历史的上一页 等价于 history.go(-1)
  • forward() 转到浏览器会话历史的下一页 等价于 history.go(1)

还有一个比较重要的事件就是 onpopstate,用来监听浏览器的历史记录发生变化的。

Router 实现

在了解完路由管理机制,接下来我们对 Vue-RouterReact-Router 的功能实现做一个总结,一个基础的 Router 应该具备以下功能:

  • 路由中心,负责注册、匹配、存储等功能
  • router-viewrouter-link组件实现
  • 跳转api

Router基本功能流程要点如下:

{% diagramsnet "/assets/drawio/router-flow.drawio" %}

接下来我们就按照每个功能模块进行简单实现。

路由中心

路由中心功能分为两块,一是注册管理,二是监听匹配。

注册管理

注册管理,顾名思义就是将所有路由对应页面组件配置统一管理,当路由改变的时候,可以直接从配置找到对应页面组件。

/**
 * 注册路由
 * @param {*} routes 
 * @param {*} mode 
 * @returns 
 */
function createRouter(routes, mode='history'){
   
    // 保存路由
    const matcherMap = new Map()
    for (let route of routes) {
   
        matcherMap.set(route.name, route)
    }

    // 添加路由
    function addRoutes(routes){
   
        for (let route of routes) {
   
            matcherMap.set(route.name, route)
        }
    }

    // 删除路由
    function removeRoutes(routes){
   
        for (let route of routes) {
   
            matcherMap.delete(route.name)
        }
    }

    // 获取路由
    function getRoutes(){
   
        return matcherMap
    }

    // 获取路由
    function getRoute(name){
   
        return matcherMap.get(name)
    }

    const router = {
   
        addRoutes,
        removeRoutes,
        getRoutes,
        getRoute
    }

    return router
}

路由匹配

这里我们就简单实现一下,将 path作为 map 的 key 去存储,忽略一下比较复杂的情况,如: query 中 params和 /path/:id等情况

因此我们只需要通过获取 matcherMap 对象中对应的组件即可。

组件

组件渲染,其实在 Vue 或者 React 中都有对应渲染组件的方法,这里为了更简单实现例子,我们使用了Web Component规范去实现自定义组件<router-view>展示和渲染组件。

分为两个功能点:

  1. 自定义组件<router-view>
  2. 匹配到路由组件后渲染对应组件

<router-view>组件实现

这里使用 WebCompoent 去实现,代码如下:

// 自定义路由组件
customElements.define('router-view', class extends HTMLElement {
   
    constructor() {
   
        super();
        const template = document.createElement('template');
        template.id = 'router-view';
        template.innerHTML = '<div><slot name="content"></slot></div>';
        const templateContent = template.content;

        const shadowRoot = this.attachShadow({
    mode: "open" });
        shadowRoot.appendChild(templateContent.cloneNode(true));
    }
});

渲染对应组件

当监听到

// 路由回调
function callback() {
   
    const route = match(window.location)
    if (currentRoute && currentRoute.path === route.path) {
   
        return
    }
    if (!route) {
   
        // 路由不存在,跳转到首页
        push('/')
        return
    }
    if (route) {
   
        currentRoute = route
        const component = route.component
        // 渲染组件
        document.querySelector('router-view').innerHTML = `<${component} slot="content"></${component}>`
    }
}

完整代码我放到 github 上,大家感兴趣可以去看看Github Router完整实现

Demo体验可以看这里

额外知识点

WebComponent

Web Component 是一套不同的技术,允许你创建可重用的定制元素(它们的功能封装在你的代码之外)并且在你的 web 应用中使用它们。 —— Web Component

简单的理解,就是浏览器可以允许你自定义HTML 标签,且包含自定义的 CSS 样式和 JS 脚本逻辑。里面有三个点学习:

  • Custom element(自定义元素),通过 JS 可以自定义 HTML 标签
  • Shadow DOM(影子 DOM),可以将HTML DOM 树以附加 Shadow DOM到自定义 HTMl 标签中,从而不影响原本 HTML DOM 树结构
  • HTML template(HTML 模板),支持 和 元素,使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用

实践例子

  1. 自定义HTML 标签代码如下:
class CustomHTMl extends HTMLElement{
   
    constructor(){
   
        // 必须首先调用 super 方法
        super();

        // 创建一个 shadow root
        const shadow = this.attachShadow({
   mode: 'open'});

        // 创建一个 spans
        const wrapper = document.createElement('h1');
        wrapper.innerHTML = '测试自定义元素';

        shadow.appendChild(wrapper);
    }

    // 首次被插入到文档 DOM 节点上时被调用
    connectedCallback() {
   
        console.log('首次被插入到文档 DOM 节点上时被调用');
    }
    // 当 custom element 从文档 DOM 中删除时,被调用
    disconnectedCallback() {
   
        console.log('当 custom element 从文档 DOM 中删除时,被调用');
    }
    // 当 custom element 被移动到新的文档时,被调用
    adoptedCallback() {
   
        console.log('当 custom element 被移动到新的文档时,被调用');
    }
    // 增加、删除或者修改某个属性时被调用
    attributeChangedCallback(name, oldValue, newValue) {
   
        console.log('增加、删除或者修改某个属性时被调用');
    }

}
// 注册组件标签,这里比较重要
customElements.define('custom-html', PopUpInfo);

实际应用如下:

<body>
    <!-- 这里就会展示h1 -->
    <custom-html></custom-html>
</body>
  1. 使用 template模板 + slot插槽
    ```html













这是标题
这是内容



```

参考资料

目录
相关文章
|
1月前
|
前端开发 JavaScript 网络协议
前端最常见的JS面试题大全
【4月更文挑战第3天】前端最常见的JS面试题大全
|
4月前
|
移动开发 前端开发 API
深入理解前端路由:构建现代 Web 应用的基石(上)
深入理解前端路由:构建现代 Web 应用的基石(上)
深入理解前端路由:构建现代 Web 应用的基石(上)
|
11天前
|
移动开发 前端开发 JavaScript
前端vue2、vue3去掉url路由“ # ”号——nginx配置(一)
前端vue2、vue3去掉url路由“ # ”号——nginx配置
40 0
|
11天前
|
前端开发 JavaScript 应用服务中间件
前端vue2、vue3去掉url路由“ # ”号——nginx配置(二)
前端vue2、vue3去掉url路由“ # ”号——nginx配置
36 0
|
6天前
|
缓存 前端开发 JavaScript
【JavaScript 技术专栏】JavaScript 前端路由实现原理
【4月更文挑战第30天】本文探讨了JavaScript前端路由在SPA中的重要性,阐述了其基本原理和实现方式,包括Hash路由和History路由。前端路由通过监听URL变化、匹配规则来动态切换内容,提升用户体验和交互性。同时,文章也提到了面临的SEO和页面缓存挑战,并通过电商应用案例分析实际应用。理解并掌握前端路由能助开发者打造更流畅的单页应用。
|
10天前
|
前端开发 JavaScript 数据可视化
前端vite+vue3——自动化配置路由布局
前端vite+vue3——自动化配置路由布局
27 0
|
2月前
|
存储 缓存 监控
2024年春招小红书前端实习面试题分享
春招已经拉开帷幕啦! 春招的拉开,意味着新一轮的求职大战已经打响,希望每位求职者都能充分准备,以最佳的状态迎接挑战,找到心仪的工作,开启职业生涯的新篇章。祝愿每位求职者都能收获满满,前程似锦!
78 3
|
2月前
|
前端开发 数据可视化 安全
2024金三银四必看前端面试题!简答版精品!
2024金三银四必看前端面试题!2w字精品!简答版 金三银四黄金期来了 想要跳槽的小伙伴快来看啊
90 3
|
3月前
|
存储 前端开发 JavaScript
前端面试:如何实现并发请求数量控制?
前端面试:如何实现并发请求数量控制?
89 0
|
4月前
|
消息中间件
【面试问题】MQ 消息怎么路由?
【1月更文挑战第27天】【面试问题】MQ 消息怎么路由?