Klassische Interviewfragen | Frontend-Routing-Konzept und Umsetzung

"Ich habe mich angemeldet, um an der 1. Herausforderung des Golden Stone Project teilzunehmen - um den Preispool von 100.000 zu teilen. Dies ist mein erster Artikel. Klicken Sie hier, um die Details der Veranstaltung anzuzeigen . "

Die Herbstrekrutierung naht. Ich glaube, dass viele Studenten bei der Bewerbung auf die Frontend-Stelle vom Interviewer nach dem Frontend-Routing und seiner Implementierungsmethode gefragt wurden. Nach der Studie wird der Autor kurz über seine Ansichten sprechen und Verständnis für die oben genannten Probleme.

Vorwort

Beim Routing ist die SPA-Single-Page-Anwendung zu erwähnen, d. h. das gesamte Projekt hat nur eine HTML-Datei, und alle Seitendatenkonvertierungen und Inhaltssprünge werden durch Routing implementiert. Das heißt, vergleichen Sie verschiedene URL-Pfade, parsen Sie sie und rendern Sie dann dynamisch den regionalen HTML-Inhalt.

Front-End-Routing

Beschreiben Sie die Zuordnung zwischen URL und UI (Seite), nennen wir es 前端路由, Zugehörigkeit 单向映射, URL-Änderung bewirkt, dass sich die UI ändert (anderen HTML-Inhalt rendert).

So implementieren Sie Front-End-Routing

  1. Wie kann ich feststellen, dass sich die URL geändert hat?
  2. Wie kann ich die URL ändern, ohne dass die Seite aktualisiert wird?

Implementierungsmodus des Front-End-Routings

Bei den meisten aktuellen Projekten handelt es sich um SPA-Projekte, wenn das Projekt etwas komplizierter ist, ist ein Front-End-Routing erforderlich, und in den beiden beliebtesten Front-End-Frameworks von vue ist vue-router das Standard-Routing von vue und Es gibt zwei Modi darin: hashund history.

Lassen Sie uns im Detail analysieren, wie die beiden Modi das Front-End-Routing implementieren.

Hash-Modus

Zunächst müssen wir wissen, dass der Hash-Wert des Browsers (Hash) bedeutet, dass url 后面的#号以及后面的字符die Änderung des Hash-Werts nicht dazu führt, dass der Browser eine Anfrage an den Server sendet, und die Änderung des Hash-Werts das Ereignis onhashchange auslöst. Obwohl der Hash in der URL erscheint, wird er nicht in die HTTP-Anforderung aufgenommen und hat keine Auswirkungen auf das Backend, sodass die Seite durch Ändern des Hashs nicht neu geladen wird.

Der Implementierungskern von Hash:

1. Die Änderung des Hash-Werts des Browsers führt nicht dazu, dass der Browser eine Anfrage an den Server sendet, und aktualisiert die UI-Seite nicht.

2. Verwenden Sie die Browser-eigene hashchangeMethode, um die Änderungen des Browser-Hash zu überwachen.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <ul>
        <li><a href="#/home">首页</a></li>
        <li><a href="#/about">关于</a></li>
    </ul>

    <!-- 渲染对应的ui -->
    <div id="routerView"></div>


    <script>
        let routerView = document.getElementById('routerView');   // 获取插入html的dom结构

        window.addEventListener('load',onHashchange)

        window.addEventListener('hashchange', onHashchange)// 浏览器自带的监听哈希值改变的方法:hashchange
         
        // 控制渲染对应的 UI 
        function onHashchange() {
            // console.log(location.hash);
            switch (location.hash) { //  location.hash为哈希值
                case '#/home': 
                  routerView.innerHTML = 'Home'
                  return
                case '#/about':
                  routerView.innerHTML = 'About'
                  return
                default:
                  return
            }
        }

        routerView.innerHTML = 'Home'
    </script>
</body>
</html>
复制代码

load 事件在整个页面及所有依赖资源如样式表和图片都已完成加载时触发。先记录一次浏览器的url,当点击两个a标签中任意一个都可以触发hashchange事件,而我们写的onHashchange函数,就是模拟页面匹配 url 去渲染相应的 HTML 内容,例子中我们使用的是向一个div容器中插入html内容 。

  • hash模式所有的操作都是在前端完成的,不需要向后端(服务器)发出请求。
  • 通过监听URL中hash部分的变化,从而做出对应的渲染逻辑。
  • 缺点是url中带有 '#' 影响美观。

简单了解了hash模式的原理及实现方式,我们来聊聊history模式的原理及实现方式。

history模式

history模式的实现主要是因为HTML5中提供的history全局对象中的一些方法,比如:

  • window.history.go(pageNum) 可以跳转到浏览器会话历史中的指定的某一个记录页
  • window.history.forward() 指向浏览器会话历史中的下一页,跟浏览器的前进按钮相同
  • window.history.back() 返回浏览器会话历史中的上一页,跟浏览器的回退按钮功能相同
  • window.history.pushState(stateData, title, url) 可以将给定的数据压入到浏览器会话历史栈中
  • window.history.replaceState(stateData, title, url) 将当前的会话页面的url替换成指定的数据

而history模式的核心就是利用了 window.history.pushState(stateData, title, url)window.history.replaceState(stateData, title, url),因为这两种方法用于修改 url 时,不会刷新页面。

pushState是压入浏览器的会话历史栈中,会使得history.length加1,而replaceState是替换当前的这条会话历史,因此不会增加history.length。

第一个问题:如何改变 url 却不引起页面的刷新?history模式使用以上两种方法就可以实现,但是第二个问题,如何监听 url 发生改变?

其实官方提供了一个事件 Window: popstate event,当用户导航会话历史记录时活动历史记录条目发生更改时,会触发[Window](Window: popstate event - Web APIs | MDN (mozilla.org)) 界面的 popstate 事件。

但是官方也说明了

Bild.png

其中关键就是仅调用 history.pushState 或者 history.replaceState 方法时,不会触发popstate事件,但是单击浏览器前进或后退按钮会触发popstate事件(或者在js中调用history.forward()或者history.back()

意思就是不能用 popstate 来监听 history.forward()或者history.back()

在笔者查阅文章资料的时候发现了,我们可以重写 history.forward()history.back()两个方法,使其暴露在window全局,这样我们就可以监听重写的两个方法了。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <ul>
    <li><a href="/home">首页</a></li>
    <li><a href="/about">关于</a></li>
  </ul>

  <!-- 渲染对应的UI -->
  <div id="routerView"></div>

  <script>
    let routerView = document.getElementById('routerView')

    window.addEventListener('DOMContentLoaded', onLoad)


    // window.addEventListener('popstate', onPopState)  // 浏览器的前进后退能匹配
    

    function onLoad() {
      onPopState()

      let links = document.querySelectorAll('li a[href]')
      // 拦截a标签的默认跳转行为
      links.forEach(a => {
        a.addEventListener('click', (e) => {
          e.preventDefault() // 阻止a标签的href行为
          
          history.pushState(null, '', a.getAttribute('href'))  // 跳转
          onPopState()
        })
      })
    }


    function onPopState() {
      // console.log(location.pathname);
      switch (location.pathname) {
        case '/home':
          routerView.innerHTML = '<h2>home page</h2>'
          return
        case '/about':
          routerView.innerHTML = '<h2>about page</h2>'
          return
        default:
          return
      }
    }
  </script>
</body>
</html>
复制代码

笔者的例子中是采用了preventDefault()阻止a标签的默认href跳转行为,再利用document.querySelectorAll获取到a标签上的href属性,在此基础上再监听a标签的点击事件,在点击时间内,利用history.pushState 或者 history.replaceState 方法来实现模拟路由跳转。想监听history.pushState 或者 history.replaceState 可参考 (# 面试官为啥总是喜欢问前端路由实现方式?)

history 致命的缺点就是当改变页面地址后,强制刷新浏览器时,(如果后端没有做准备的话)会报错,因为刷新是拿当前地址去请求服务器的,如果服务器中没有相应的响应,会出现 404 页面。

结语

Daher hängt die Wahl unseres Front-End-Routings von unseren Nutzungsszenarien, Anforderungen usw. ab. Beide können implementiert werden, jedes mit seinen eigenen Vor- und Nachteilen. Das Obige sind einige meiner persönlichen Meinungen zum Front-End-Routing. Fragen können gestellt werden diskutiert werden im Kommentarbereich, danke Danke fürs Zuschauen!

Wie.jpg

Ich denke du magst

Origin juejin.im/post/7143059639196712997
Empfohlen
Rangfolge