Questions d'entretien classiques | Concept et mise en œuvre du routage frontal

"Je me suis inscrit pour participer au 1er challenge du Golden Stone Project - pour partager la cagnotte de 100 000, ceci est mon premier article, cliquez pour voir les détails de l'événement "

Le recrutement d'automne approche. Lors de la candidature au poste de front-end, je pense que de nombreux étudiants ont été interrogés par l'intervieweur sur le routage front-end et sa méthode de mise en œuvre. Après l'étude, l'auteur parlera brièvement de son point de vue et compréhension des problèmes ci-dessus.

avant-propos

En ce qui concerne le routage, nous devons mentionner l'application monopage SPA, c'est-à-dire que l'ensemble du projet n'a qu'un seul fichier html, et que toutes les conversions de données de page et les sauts de contenu sont mis en œuvre par routage. Autrement dit, faites correspondre différents chemins d'URL, analysez-les, puis restituez dynamiquement le contenu html régional.

Routage frontal

Décrivez le mappage entre l'url et l'interface utilisateur (page), appelons-le 前端路由, appartenons 单向映射, le changement d'url entraîne le changement de l'interface utilisateur (rendu du contenu html différent).

Comment implémenter le routage frontal

  1. Comment puis-je détecter que l'URL a changé ?
  2. Comment puis-je changer l'url sans provoquer l'actualisation de la page ?

Mode de mise en œuvre du routage frontal

Pour la plupart des projets actuels sont des projets SPA, lorsque le projet est un peu plus compliqué, un routage frontal est requis, et dans les deux frameworks frontaux les plus populaires de vue, vue-router est le routage standard de vue , et il y a deux modes : hashet history.

Analysons en détail comment les deux modes implémentent le routage frontal.

mode de hachage

Tout d'abord, nous devons savoir que la valeur de hachage du navigateur (hash) signifie que url 后面的#号以及后面的字符le changement de la valeur de hachage n'entraînera pas l'envoi d'une requête par le navigateur au serveur, et le changement de la valeur de hachage déclenchera l'événement onhashchange. Bien que le hachage apparaisse dans l'URL, il ne sera pas inclus dans la requête HTTP et n'a aucun effet sur le backend, donc changer le hachage ne rechargera pas la page.

Le noyau d'implémentation de hash :

1. La modification de la valeur de hachage du navigateur n'entraînera pas l'envoi d'une requête par le navigateur au serveur et n'actualisera pas la page de l'interface utilisateur.

2. Utilisez la propre hashchangeméthode du navigateur pour surveiller les modifications du hachage du navigateur.

<!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 事件。

但是官方也说明了

image.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 页面。

结语

Par conséquent, le choix de notre routage frontal dépend de nos scénarios d'utilisation, de nos exigences, etc. Les deux peuvent être implémentés, chacun avec ses avantages et ses inconvénients. Voici quelques-unes de mes opinions personnelles sur le routage frontal. Toutes les questions peuvent être discuté dans la zone de commentaire, merci Merci d'avoir regardé!

J'aime.jpg

Je suppose que tu aimes

Origine juejin.im/post/7143059639196712997
conseillé
Classement