本身着手完成一個前端路由

媒介

用過當代前端框架的同硯,對前端路由肯定不生疏, vue, react, angular 都有本身的 router, 那末你對 router 的事情道理相識嗎?
假如還不相識, 那末請跟我一起來手寫一個簡樸的前端路由, 趁便相識一下.

完成路由的2種體式格局

  1. hash形式
  2. history形式

瑕玷:

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

history: 兼容性較差, 直接接見會400 ,除非後端或許服務器有做處置懲罰

基礎道理

hash是基於 監聽 hashchange 事宜完成的,history 是基於 pushState 和 popState,

完成

由於history兼容性較差,而且完成體式格局基礎沒多大區分,本文就以hash形式來完成,history的完成體式格局只完成差別的部份。

<html>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</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>Document</title>
    <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即可。

    // 監聽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來定位該頁面的參數。

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

如許,我們就開端完成了一個hash路由,那末接下來,我們來看看history路由怎樣完成。

history 形式

history 形式重要依託 挪用 history.pushState() 要領 和 監聽 popstate 事宜。

history.pushState() 要領吸收3個參數:

  1. 要通報的數據(參數)
  2. 給頁面設置的題目(兼容性差,險些沒用)
  3. url

我們看看挪用實例

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

須要注重的是 pushState 的時刻,並不會觸發 popstate 事宜,只要在行進退卻的時刻,才會觸發,所以 pushState 以後,須要手動去設置頁面的相干狀況。如上面的例子,我們須要如許做

    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,運轉。

假如以為本文對您有效,請給本文的github加個star,萬分謝謝

別的,github上另有其他一些關於前端的教程和組件,有興緻的童鞋能夠看看,你們的支撐就是我最大的動力。

    原文作者:noahlam
    原文地址: https://segmentfault.com/a/1190000015429806
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞