典型的な面接の質問 | フロントエンド ルーティングの概念と実装

「ゴールデン ストーン プロジェクトの最初のチャレンジに参加するためにサインアップしました。賞金プール 100,000 を共有するために、これが私の最初の記事です。クリックしてイベントの詳細を表示します

秋採用が近づいてきましたが、フロントエンド職に応募する際、面接官からフロントエンドのルーティングとその実施方法について聞かれる学生も多いと思います.上記の問題の理解。

序文

ルーティングに関して言えば、SPA シングルページ アプリケーションについて言及する必要があります。つまり、プロジェクト全体で 1 つの html ファイルしかなく、すべてのページ データ変換とコンテンツ ジャンプはルーティングによって実装されます。つまり、さまざまな URL パスを照合して解析し、地域の HTML コンテンツを動的にレンダリングします。

フロントエンド ルーティング

URL と UI (ページ) の間のマッピングを記述し前端路由ます单向映射

フロントエンド ルーティングの実装方法

  1. URL が変更されたことを検出するにはどうすればよいですか?
  2. ページを更新せずに URL を変更するにはどうすればよいですか?

フロントエンド ルーティングの実装モード

現在のプロジェクトのほとんどは SPA プロジェクトです。プロジェクトがもう少し複雑な場合、フロントエンド ルーティングが必要です。また、vue の 2 つの最も人気のあるフロントエンド フレームワークでは、 vue-router が vue の標準ルーティングです。と の 2 つのモードがありhashますhistory

2 つのモードがフロントエンド ルーティングを実装する方法を詳しく分析してみましょう。

ハッシュモード

まず、ブラウザのハッシュ値 (hash) はurl 后面的#号以及后面的字符、ハッシュ値の変更によってブラウザがサーバーにリクエストを送信することはなく、ハッシュ値の変更によって onhashchange イベントがトリガーされることを意味することを知っておく必要があります。ハッシュは URL に表示されますが、HTTP リクエストには含まれず、バックエンドには影響しないため、ハッシュを変更してもページはリロードされません。

ハッシュの実装コア:

1. ブラウザのハッシュ値を変更しても、ブラウザはサーバーにリクエストを送信せず、UI ページを更新しません。

2. ブラウザ独自のhashchange方法を使用して、ブラウザのハッシュの変更を監視します。

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

但是官方也说明了

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

结语

したがって、フロントエンド ルーティングの選択は、使用シナリオ、要件などによって異なります. どちらも実装できますが、それぞれに長所と短所があります. 上記は、フロントエンド ルーティングに関する私の個人的な意見の一部です. ご不明な点がございましたら、コメント欄で議論してください、ありがとうございます ご覧いただきありがとうございます!

いいね.jpg

おすすめ

転載: juejin.im/post/7143059639196712997