1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        你好,談談你對前端路由的理解

        共 4665字,需瀏覽 10分鐘

         ·

        2021-01-19 09:27

        一篇文章,不可能做的面面俱到,全部受眾。希望大家?guī)еl(fā)散思維去看文章,將文章涉及的知識點,吸收為己所用。這樣看完一篇文章,才能有所收獲。

        前言

        好了不裝了,今天我就化身性感面試官在線問大家一個問題,“談談你對前端路由的理解”??吹竭@個問題,那回答可多了去了。但是換位思考一下,你問候選人這個問題的時候,你想要得到什么答案?以我個人拙見,我希望候選人能從全局解讀這個問題,大致以下三點。

        1、為什么會出現(xiàn)前端路由。

        2、前端路由解決了什么問題。

        3、前端路由實現(xiàn)的原理是什么。

        我們帶著這三個問題,繼續(xù)往下看,閱讀的過程中如果同學們有自己的見解,可以評論區(qū)發(fā)表自己的看法。如果覺得講的內(nèi)容讓你有了新的見解,請獻上你寶貴的一贊??,這將是我繼續(xù)寫作的動力。

        傳統(tǒng)頁面

        這里不糾結叫法,凡是整個項目都是 DOM 直出的頁面,我們都稱它為“傳統(tǒng)頁面”(SSR 屬于首屏直出,這里我不認為是傳統(tǒng)頁面的范疇)。那么什么是 DOM 直出呢?簡單說就是在瀏覽器輸入網(wǎng)址后發(fā)起請求,返回來的 HTML 頁面是最終呈現(xiàn)的效果,那就是 DOM 直出。并且每次點擊頁面跳轉,都會重新請求 HTML 資源。耳聽為虛,眼見為實。我們以這個地址為例,驗證以下上述說法。

        www.cnblogs.com/han-1034683…

        腚眼一看,就能明白上圖在描述什么。沒錯,博客園就是一個傳統(tǒng)頁面搭建而成的網(wǎng)站,每次加載頁面,都會返回 HTML 資源以及里面的 CSS 等靜態(tài)資源,組合成一個新的頁面。

        “瞎了”的同學,我再教一個方法,就是在瀏覽器頁面右鍵,點擊“顯示網(wǎng)頁源代碼”,打開后如下所示:

        網(wǎng)頁上能看到什么圖片或文字,你能在上述圖片中找到相應的 HTML 結構,那也屬于傳統(tǒng)頁面,也就是 DOM 直出。

        單頁面

        時代在進步,科技在發(fā)展,面對日益增長的網(wǎng)頁需求,網(wǎng)頁開始走向模塊化、組件化的道路。隨之而來的是代碼的難以維護、不可控、迭代艱難等現(xiàn)象。面臨這種情況,催生出不少優(yōu)秀的現(xiàn)代前端框架,首當其沖的便是 React 、 Vue 、 Angular 等著名單頁面應用框架。而這些框架有一個共同的特點,便是“通過 JS 渲染頁面”。

        舉個例子,以前我們直出 DOM ,而現(xiàn)在運用這些單頁面框架之后, HTML 頁面基本上只有一個 DOM 入口,大致如下所示:

        所有的頁面組件,都是通過運行上圖底部的 app.js 腳本,掛載到

        這個節(jié)點下面。用一個極其簡單的 JS 展示掛載這一個步驟:


        ??<div?id="root">div>
        ??<script>
        ????const?root?=?document.getElementById('root')?//?獲取根節(jié)點
        ????const?divNode?=?document.createElement('div')?//?創(chuàng)建?div?節(jié)點
        ????divNode.innerText?=?'你媽貴姓?'?//?插入內(nèi)容
        ????root.appendChild(divNode)?//?插入根節(jié)點
        ??
        script>

        </body>

        image.png

        脫去所有的凡塵世俗,最本真的單頁項目運行形式便是如此。注意,我要點題了?。。。?/strong>

        image.png

        既然單頁面是這樣渲染的,那如果我有十幾個頁面要互相跳轉切換,咋整!?。??這時候 前端路由 應運而生,它的出現(xiàn)就是為了解決單頁面網(wǎng)站,通過切換瀏覽器地址路徑,來匹配相對應的頁面組件。我們通過一張丑陋的圖片來理解這個過程:

        前端路由 會根據(jù)瀏覽器地址欄 pathname 的變化,去匹配相應的頁面組件。然后將其通過創(chuàng)建 DOM 節(jié)點的形式,塞入根節(jié)點

        。這就達到了無刷新頁面切換的效果,從側面也能說明正因為無刷新,所以 React 、 Vue 、 Angular 等現(xiàn)代框架在創(chuàng)建頁面組件的時候,每個組件都有自己的 生命周期 。

        原理

        前端路由 插件比較火的倆框架對應的就是 Vue-RouterReact-Router ,但是它們的邏輯,歸根結底還是一樣的,用殊途同歸四個字,再合適不過。

        通過分析哈希模式和歷史模式的實現(xiàn)原理,讓大家對前端路由的原理有一個更深刻的理解。

        哈希模式

        a 標簽錨點大家應該不陌生,而瀏覽器地址上 # 后面的變化,是可以被監(jiān)聽的,瀏覽器為我們提供了原生監(jiān)聽事件 hashchange ,它可以監(jiān)聽到如下的變化:

        • 點擊 a 標簽,改變了瀏覽器地址
        • 瀏覽器的前進后退行為
        • 通過 window.location 方法,改變?yōu)g覽器地址

        接下來我們利用這些特點,去實現(xiàn)一個 hash 模式的簡易路由:在線運行

        html>
        <html?lang="en">
        <head>
        ??<meta?charset="UTF-8">
        ??<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
        ??<title>Hash?模式title>
        head>
        ??<body>
        ????<div>
        ??????<ul>
        ????????<li><a?href="#/page1">page1a>li>
        ????????<li><a?href="#/page2">page2a>li>
        ??????ul>
        ??????
        ??????<div?id="route-view">div>
        ????div>
        ??<script?type="text/javascript">
        ????//?第一次加載的時候,不會執(zhí)行?hashchange?監(jiān)聽事件,默認執(zhí)行一次
        ????//?DOMContentLoaded?為瀏覽器?DOM?加載完成時觸發(fā)
        ????window.addEventListener('DOMContentLoaded',?Load)
        ????window.addEventListener('hashchange',?HashChange)
        ????//?展示頁面組件的節(jié)點
        ????var?routeView?=?null
        ????function?Load()?{
        ??????routeView?=?document.getElementById('route-view')
        ??????HashChange()
        ????}
        ????function?HashChange()?{
        ??????//?每次觸發(fā)?hashchange?事件,通過?location.hash?拿到當前瀏覽器地址的?hash?值
        ??????//?根據(jù)不同的路徑展示不同的內(nèi)容
        ??????switch(location.hash)?{
        ??????case?'#/page1':
        ????????routeView.innerHTML?=?'page1'
        ????????return
        ??????case?'#/page2':
        ????????routeView.innerHTML?=?'page2'
        ????????return
        ??????default:
        ????????routeView.innerHTML?=?'page1'
        ????????return
        ??????}
        ????}
        ??
        script>
        ??body>
        html>

        當然,這是很簡單的實現(xiàn),真正的 hash 模式,還要考慮到很多復雜的情況,大家有興趣就去看看源碼。

        瀏覽器展示效果如下:

        歷史模式

        history 模式會比 hash 模式稍麻煩一些,因為 history 模式依賴的是原生事件 popstate ,下面是來自 MDN 的解釋:

        image.png

        小知識:pushState 和 replaceState 都是 HTML5 的新 API,他們的作用很強大,可以做到改變?yōu)g覽器地址卻不刷新頁面。這是實現(xiàn)改變地址欄卻不刷新頁面的重要方法。

        包括 a 標簽的點擊事件也是不會被 popstate 監(jiān)聽。我們需要想個辦法解決這個問題,才能實現(xiàn) history 模式。

        image.png

        **解決思路:**我們可以通過遍歷頁面上的所有 a 標簽,阻止 a 標簽的默認事件的同時,加上點擊事件的回調(diào)函數(shù),在回調(diào)函數(shù)內(nèi)獲取 a 標簽的 href 屬性值,再通過 pushState 去改變?yōu)g覽器的 location.pathname 屬性值。然后手動執(zhí)行 popstate 事件的回調(diào)函數(shù),去匹配相應的路由。邏輯上可能有些饒,我們用代碼來解釋一下:在線地址

        html>
        <html?lang="en">
        <head>
        ??<meta?charset="UTF-8">
        ??<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
        ??<title>History?模式title>
        head>
        <body>
        ??<div>
        ????<ul>
        ??????<li><a?href="/page1">page1a>li>
        ??????<li><a?href="/page2">page2a>li>
        ????ul>
        ????<div?id="route-view">div>
        ??div>
        ??<script?type="text/javascript">
        ????window.addEventListener('DOMContentLoaded',?Load)
        ????window.addEventListener('popstate',?PopChange)
        ????var?routeView?=?null
        ????function?Load()?{
        ??????routeView?=?document.getElementById('route-view')
        ??????//?默認執(zhí)行一次?popstate?的回調(diào)函數(shù),匹配一次頁面組件
        ??????PopChange()
        ??????//?獲取所有帶?href?屬性的?a?標簽節(jié)點
        ??????var?aList?=?document.querySelectorAll('a[href]')
        ??????//?遍歷?a?標簽節(jié)點數(shù)組,阻止默認事件,添加點擊事件回調(diào)函數(shù)
        ??????aList.forEach(aNode?=>?aNode.addEventListener('click',?function(e)?{
        ????????e.preventDefault()?//阻止a標簽的默認事件
        ????????var?href?=?aNode.getAttribute('href')
        ????????//??手動修改瀏覽器的地址欄
        ????????history.pushState(null,?'',?href)
        ????????//?通過?history.pushState?手動修改地址欄,
        ????????//?popstate?是監(jiān)聽不到地址欄的變化,所以此處需要手動執(zhí)行回調(diào)函數(shù)?PopChange
        ????????PopChange()
        ??????}))
        ????}
        ????function?PopChange()?{
        ??????console.log('location',?location)
        ??????switch(location.pathname)?{
        ??????case?'/page1':
        ????????routeView.innerHTML?=?'page1'
        ????????return
        ??????case?'/page2':
        ????????routeView.innerHTML?=?'page2'
        ????????return
        ??????default:
        ????????routeView.innerHTML?=?'page1'
        ????????return
        ??????}
        ????}
        ??
        script>
        body>
        html>

        這里注意,不能在瀏覽器直接打開靜態(tài)文件,需要通過 web 服務,啟動端口去瀏覽網(wǎng)址。

        總結

        這篇文章主要知識點集中在前端路由這塊,能完全看完,并且把實現(xiàn)原理捋一遍,我想你應該對現(xiàn)代前端框架會有一個新的理解。沒有新的理解的同學,來杭州打我,我不還手。

        作者:尼克陳

        來源:https://juejin.cn/post/6917523941435113486


        The End

        如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想請你幫我三個小忙:

        1、點個?「在看」,讓更多的人也能看到這篇內(nèi)容

        2、關注官網(wǎng)?https://muyiy.cn,讓我們成為長期關系

        3、關注公眾號「高級前端進階」,公眾號后臺回復?「加群」?,加入我們一起學習并送你精心整理的高級前端面試題。》》面試官都在用的題庫,快來看看《


        “在看”嗎?在看就點一下吧


        瀏覽 34
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            婷婷五月综合久久 | 我爱搞搞| 大黑鸡巴插逼 | 成人性生交大片免费看黄花女 | 996热re视频精品视频这里 | 国产午夜精品一区二区三区_久久精品无 | 国产熟妇 码视频黑料 | 国产乱伦免费观看 | 国产日韩+欧美在线观看 | 另类小说亚洲 |