自己動手實現一個前端路由,自己動手實現路由

    前前言

    最近工作比較忙,寫的比較慢,當我寫完這一篇準備發布的時候,發現掘金剛好也有一篇關于前端路由的,而且寫的比我詳細,不過辛辛苦苦寫的總不能刪掉吧,再說我的路由風格是純個人思路實現的,所以還是硬著頭皮發了哈。愛掏網 - it200.com

    前言

    用過現代前端框架的同學,對前端路由一定不陌生, vue, react, angular 都有自己的 router, 那么你對 router 的工作原理了解嗎? 如果還不了解, 那么請跟我一起來手寫一個簡單的前端路由, 順便了解一下.

    實現路由的2種方式

    1. hash模式
    2. history模式

    缺點:

    hash:丑(地址欄要多一個#), 某些特殊場景有坑 ,如微信支付回調

    history: 兼容性較差, 直接訪問會400 ,除非后端或者服務器有做處理

    基本原理

    hash是基于 監聽 hashchange 事件實現的,history 是基于 pushState 和 popState,

    實現

    由于history兼容性較差,而且實現方式基本沒多大區別,本文就以hash模式來實現,history的實現方式只實現不同的部分。愛掏網 - it200.com

    html>
    head>
        meta charset="UTF-8">
        title>Documenttitle>
    head>
    body>
    input type="button" onclick="hashPush('index')" value="goto index">
    body>
    script>
        // 頁面跳轉
        function hashPush(url) {
            location.hash = "#" + url
        }
    
        // 監聽hash的變動
        window.addEventListener('hashchange', function (e) {
            console.log('當前的hash地址', location.hash.slice(1) || "/")
        })
    script>
    html>
    

    以上就是hash模式的最簡單實現,查看控制臺,可以看到不管是點擊跳轉按鈕,還是點擊瀏覽器的前進/后退按鈕,控制臺都會輸出當前頁面對應的 'url',有了 'url',我們就可以對內容做條件渲染了

    我們在上面的代碼的基礎上,稍作修改一下,增加2個div,一個是login,一個是index

    html>
    head>
        meta charset="UTF-8">
        title>Documenttitle>
        style>
            body{
                margin: 0;
                padding: 0;
            }
            #login, #index{
                width: 100%;
                height: 100%;
                display: none;
            }
    
            #login{
                background: #f5f5f5;
                display: block;
            }
    
            #index{
                background: #aaddff;
            }
        style>
    head>
    body>
    
    
    div id="login" class="component">
        input type="button" onclick="hashPush('index')" value="登陸">
    div>
    
    
    div id="index" class="component">
        input type="button" onclick="hashPush('login')" value="退出登陸">
    div>
    
    body>
    script>
    
        // 頁面跳轉
        function hashPush(url) {
            location.hash = "#" + url
        }
    
        // 監聽hash的變動
        window.addEventListener('hashchange', function (e) {
            let url = location.hash.slice(1) || "/"
            console.log('當前的hash地址', url)
            setVisible(url)
        })
    
        // 顯示跟路由地址對應的內容,隱藏其他內容
        function setVisible(url) {
            let components = Array.from(document.body.querySelectorAll(".component"))
            components.map(item => {
                if(item.id===url) {
                    console.log('顯示',item.id)
                    item.style.display = 'block'
                } else {
                    console.log('隱藏',item.id)
                    item.style.display = 'none'
                }
    
            })
        }
    script>
    html>
    
    

    這樣,我們就能根據不同的hash地址,顯示不同的內容,是不是已經有點路由的味道了呢?但是目前還無法傳參,傳參的方式有很多種,最常見最通俗的,應該是query string 了吧? query string 其實很簡單,解析url即可。愛掏網 - it200.com

        // 監聽hash的變動
        window.addEventListener('hashchange', function (e) {
            let url = location.hash.slice(1) || "index"
            // 解析url
            let questionIndex = url.indexOf("?")
            let path, query
            if(questionIndex >= 0){
                path = url.substr(0,questionIndex)
                let queryString = url.substr(questionIndex+1)
                let queryArray = queryString.split("&")
                let queryObject = {}
                queryArray.map(str => {
                    let equalIndex = str.indexOf("=")
                    if(equalIndex > 0) {
                        let key = str.substr(0,equalIndex)
                        let value = str.substr(equalIndex+1)
                        queryObject[key] = value
                    }
                })
                query = queryObject
            } else {
                path = url
                query = {}
            }
            
            console.log('接收到的參數', query)
            setVisible(path)
        })
    

    這樣,我們就能獲取到URL上傳遞的query string , 還順便解決了傳參會導致路由解析不正確的bug.

    傳參其實還有個更簡單的方法,就是 設置一個全局變量 param,然后在需要傳值的時候,直接對param 賦值即可

        // 設置一個全局變量
        var param = {}
    
        // 頁面跳轉
        // 這個多了個參數 args
        function hashPush(url, args) {
            location.hash = "#" + url
            param = args
        }
    

    由于是全局變量,所以可以在任意位置使用 param,不過這樣直接使用全局變量的方式,也有它的弱點,就是點擊返回按鈕的時候無法保存變量,而query string 因為是在 url 里面的,所以返回的時候,可以拿到上個頁面的 query string, 那我們有沒有辦法讓全局變量的方式也能保存上一個頁面的參數呢? 我們來稍微修改一下代碼

        // 設置一個全局變量
        var param = {}
    
        // 頁面跳轉
        // 這個多了個參數 args
        function hashPush(url, args) {
            location.hash = "#" + url
            // 如果有傳args參數,就在 param 對象下建一個名字跟當前url一樣的對象,并把args賦值給它
            if(args) {
                param[url] = args
            }
        }
    

    那我們在 hashchange 的時候,可以根據url來定位該頁面的參數。愛掏網 - it200.com

        // 監聽hash的變動
        window.addEventListener('hashchange', function (e) {
            // 省略其他代碼
            args = param[path] || {}
            // 省略其他代碼
        })
    

    這樣,我們就初步實現了一個hash路由,那么接下來,我們來看看history路由怎么實現。愛掏網 - it200.com

    history 模式

    history 模式主要依靠 調用 history.pushState() 方法 和 監聽 popstate 事件。愛掏網 - it200.com

    history.pushState() 方法接收3個參數:

    1. 要傳遞的數據(參數)
    2. 給頁面設置的標題(兼容性差,幾乎沒用)
    3. url

    我們看看調用實例

        history.pushState({id:1}, '我是頁面標題', url)
    

    需要注意的是 pushState 的時候,并不會觸發 popstate 事件,只有在前進后退的時候,才會觸發,所以 pushState 之后,需要手動去設置頁面的相關狀態。愛掏網 - it200.com如上面的例子,我們需要這樣做

        history.pushState({id:1}, '我是頁面標題', url)
        setVisible(url)
    

    然后監聽 popstate 事件,捕獲 前進/后退

     window.addEventListener('popstate',function (e) {
         // e.state 就是你 pushState 的時候,傳的第一個參數
         let state = e.state || {}  
         let url = state.target.location.pathName
         //  根據參數 做一些其他操作
      })
    

    最后

    本文最終實現代碼已經放在 github上,想要直接看效果的同學,可以上去直接copy,運行。愛掏網 - it200.com

    如果覺得本文對您有用,請給本文的github加個star,萬分感謝

    另外,github上還有其他一些關于前端的教程和組件,有興趣的童鞋可以看看,你們的支持就是我最大的動力。愛掏網 - it200.com


    其他鏈接

    1. 用proxy實現一個更優雅的vue
    2. 3行代碼實現一個簡易版promise
    3. vuex其實超簡單,只需3步
    4. vuex其實超簡單,喝完這3步,還有3步
    5. 從零開始徒手擼一個vue的toast彈窗組件
    6. JS中的繼承(上)
    7. JS中的繼承(下)
    8. JS中的原型對象
    9. JS中創建對象的方法
    10. moment太重? 那就試試miment--一個超輕量級的js時間庫
    11. 美團小程序框架mpvue入門教程
    12. 美團小程序框架mpvue(花名:沒朋友)蹲坑指南
    13. 一個基于mpvue的toast彈窗組件mptoast



    原文發布時間為:2024年06月29日

    本文作者:noahlam

    本文來源:掘金?如需轉載請聯系原作者
    聲明:所有內容來自互聯網搜索結果,不保證100%準確性,僅供參考。如若本站內容侵犯了原著者的合法權益,可聯系我們進行處理。
    發表評論
    更多 網友評論0 條評論)
    暫無評論

    返回頂部

    主站蜘蛛池模板: 亚洲乱色熟女一区二区三区丝袜| 精品一区二区三区免费毛片爱| 日本人的色道www免费一区| 少妇精品久久久一区二区三区| 激情综合丝袜美女一区二区| 精品福利一区二区三| 四虎永久在线精品免费一区二区 | 国产福利一区二区在线视频 | 中文字幕一区二区区免| 亚洲一区精品视频在线| 日韩精品无码Av一区二区| 亚洲高清毛片一区二区| 老熟女高潮一区二区三区| 国产一区二区三区久久| 久久99久久无码毛片一区二区| 精品一区二区三区在线观看| 国产一区二区三区在线看片| 亚洲男女一区二区三区| 亚洲一区精品中文字幕| 精品国产免费一区二区三区香蕉| 色窝窝无码一区二区三区| 视频一区视频二区制服丝袜| 日本免费一区尤物| 国产一区二区在线|播放| 日韩AV片无码一区二区不卡| 日本精品无码一区二区三区久久久 | 国产一区二区三区美女| 无码人妻一区二区三区兔费| 国产精品一区二区久久| 亚洲爆乳精品无码一区二区| 无码中文字幕乱码一区| 国产伦精品一区二区三区免.费| 国产成人久久精品麻豆一区| 一区二区三区免费高清视频| 午夜无码视频一区二区三区| 久久毛片免费看一区二区三区| 亚洲一区二区三区四区在线观看| 亚洲AV香蕉一区区二区三区| 亚洲av色香蕉一区二区三区| 成人毛片无码一区二区| 国产成人高清亚洲一区久久|