前端面试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













这是标题
这是内容



```

参考资料

目录
相关文章
|
18天前
|
移动开发 缓存 前端开发
深入理解前端路由:原理、实现与应用
本书《深入理解前端路由:原理、实现与应用》全面解析了前端路由的核心概念、工作原理及其实现方法,结合实际案例探讨了其在现代Web应用中的广泛应用,适合前端开发者和相关技术人员阅读。
|
3月前
|
存储 JavaScript 前端开发
前端 vue:路由的高级使用
前端 vue:路由的高级使用
|
25天前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
63 1
|
1月前
|
缓存 前端开发 JavaScript
"面试通关秘籍:深度解析浏览器面试必考问题,从重绘回流到事件委托,让你一举拿下前端 Offer!"
【10月更文挑战第23天】在前端开发面试中,浏览器相关知识是必考内容。本文总结了四个常见问题:浏览器渲染机制、重绘与回流、性能优化及事件委托。通过具体示例和对比分析,帮助求职者更好地理解和准备面试。掌握这些知识点,有助于提升面试表现和实际工作能力。
66 1
|
3月前
|
Web App开发 前端开发 Linux
「offer来了」浅谈前端面试中开发环境常考知识点
该文章归纳了前端开发环境中常见的面试知识点,特别是围绕Git的使用进行了详细介绍,包括Git的基本概念、常用命令以及在团队协作中的最佳实践,同时还涉及了Chrome调试工具和Linux命令行的基础操作。
「offer来了」浅谈前端面试中开发环境常考知识点
|
2月前
|
JavaScript 前端开发 API
前端技术分享:Vue.js 动态路由与守卫
【10月更文挑战第1天】前端技术分享:Vue.js 动态路由与守卫
|
2月前
|
JavaScript 前端开发
vue尚品汇商城项目-day01【8.路由跳转与传参相关面试题】
vue尚品汇商城项目-day01【8.路由跳转与传参相关面试题】
43 0
vue尚品汇商城项目-day01【8.路由跳转与传参相关面试题】
|
3月前
|
资源调度 JavaScript 前端开发
前端vue:路由的基本使用
前端vue:路由的基本使用
|
3月前
|
移动开发 前端开发 JavaScript
浅谈前端路由原理hash和history
该文章详细解析了前端路由的两种模式——Hash模式与History模式的工作原理及其实现方式,并通过实例代码展示了如何在实际项目中运用这两种路由模式。
|
2月前
|
JavaScript 前端开发 应用服务中间件
Vue开发中,在实现单页面应用(SPA)前端路由时的hash模式和history模式的区别及详细介绍
Vue开发中,在实现单页面应用(SPA)前端路由时的hash模式和history模式的区别及详细介绍
43 0
下一篇
DataWorks