1. 程式人生 > >利用hash或history實現單頁面路由

利用hash或history實現單頁面路由

標簽 play log adding ash min slice koa fff

目錄

在chrome(版本 70.0.3538.110)測試正常
編寫涉及:css, html,js, node(koa)

在線演示codepen

html代碼

<div class="hash"> <div class="title">hash 路由</div> <a href="#/one">hash 1</a> <a href="#/two">hash 2</a> <a href="#/three">hash 3</a> <a onclick="hashRoute.skip(‘other‘)">other</a> <div id="hashContent"></div> </div> <div class="history"> <div class="title">history 路由</div> <div> <button onclick="historyRoute.skip(‘pushStateOne‘)">history.pushState(1)</button> <button onclick="historyRoute.skip(‘pushStateTwo‘)">history.pushState(2)</button> <button onclick="historyRoute.skip(‘pushStateThree‘)">history.pushState(3)</button> <button onclick="historyRoute.skip(‘pushStateTwo‘)">history.replaceState(pushStateTwo)</button> <button onclick="historyRoute.skip(‘go‘)">history.go(2)</button> <button onclick="historyRoute.skip(‘forward‘)">history.forward()</button> <button onclick="historyRoute.skip(‘back‘)">history.back()</button> </div> <div id="historyContent"></div> </div>

css代碼


.hash a {
    display: inline-block;
    padding: 5px 8px;
    margin: 10px 10px 10px 0;
    font-size: 15px;
    text-decoration: none;
    border: 0;
    cursor: pointer;
    color: #fff;
    background-color: rgb(17, 130, 236);
}
.title{
    margin: 10px 0;
    padding: 5px 8px;
    border-left: rgb(168, 168, 168) solid 2px;
    background-color: rgb(230, 230, 230);
}
.hash div:last-child{
    padding: 6px;
    min-height: 100px;
    background-color: rgb(243, 243, 243);
}

.history{
    margin: 10px 0;
}
.history button {
    padding: 8px 10px;
    border: 0;
    color: #fff;
    background-color: rgb(250, 144, 44);
}
.history div:last-child{
    margin-top: 10px;
    padding: 6px;
    min-height: 100px;
    background-color: rgb(243, 243, 243);
}

JavaScript代碼

hash方式


class HashRoute {
    setRoute() {
        const commandObj = {
            one: ‘page one‘,
            two: ‘page two‘,
            three: ‘page three‘
        }
        const hashRoute = location.hash ? location.hash.slice(2) : ‘one‘
        let re = commandObj[hashRoute]

        document.getElementById(‘hashContent‘).innerHTML =  re ? re : ‘page not find‘
    }

    skip(path) {
        window.location.hash= `#/${path}`
    }

    init() {
        window.addEventListener(‘DOMContentLoaded‘, this.setRoute)

        // 1.直接更改瀏覽器地址,在最後面增加或改變#hash; 
        // 2.通過改變location.href 或 location.hash的值; 
        // 3.通過觸發點擊帶錨點的鏈接; 
        // 4.瀏覽器前進後退可能導致hash的變化,前提是兩個網頁地址中的hash值不同
        window.addEventListener(‘hashchange‘, this.setRoute)
    }
}

const hashRoute = new HashRoute()

hashRoute.init()

history 方式

瀏覽器端代碼


// 服務端有效
class HistoryRoute {
    constructor() {
        this.currentPath = ‘‘
    }

    renderView(component) {
        const route = {
            pushStateOne: ‘route pushState one‘,
            pushStateTwo: ‘route pushState two‘,
            pushStateThree: ‘route pushState three‘,
            replaceState: ‘route replaceState‘,
            go: ‘route go‘,
            forward: ‘route forward‘,
            back: ‘route back‘,
            notFind: ‘not find‘,
        }
        document.getElementById(‘historyContent‘).innerHTML = route[component]
    }

    // 這裏所有涉及的跳轉都用js方式,不采用a標簽(采用a標簽請設置攔截)
    skip(path) {
        const commandObj = {
            pushStateOne: () => {
                history.pushState({ path }, path,path)
                this.renderView(path)
            },
            pushStateTwo: () => {
                history.pushState({ path }, path, path)
                this.renderView(path)
            },
            pushStateThree: () => {
                history.pushState({ path }, path, path)
                this.renderView(path)
            },
            replaceState: () => {
                // 是用來修改當前的history實體而不是創建一個新的,比如連轉順序為1,2,3,1執行replaceState(2),再執行back(),返回1,而不是3
                history.replaceState({ path }, path, path)
                this.renderView(path)
            },
            go: () => {
                history.go(2)
                this.renderView(‘go‘)
            },
            forward: () => {
                history.forward()
                this.renderView(‘forward‘)
            },
            back: () => {
                history.back()
            },

        }

        this.currentPath = path;
        commandObj[path]()
    }

    init() {
        window.addEventListener(‘DOMContentLoaded‘, () => {
            // 針對F5刷新問題:
            // 1.可以使用?後面跟參數形式
            // 2.統一入口利用忽略地址方式(後端配置 /page/:path 忽略page後跟的所有地址,通過前端去請求page後的對應路由數據,如下)

            const path = location.href.split(‘/page/‘) 
            this.skip(path[1])
        })

        // 調用history.pushState()或history.replaceState()不會觸發popstate。
        // 只有在用戶點擊前進回退按鈕,(或history.back(),forward,go)
        window.addEventListener(‘popstate‘, (event) => {
            console.log(‘popstate‘, this.currentPath, event.state);
            const { state } = event
            if (state && state.path) {
                this.renderView(state.path)
            } else {
                this.renderView(‘404‘)
            }
        })
    }
}

const historyRoute = new HistoryRoute()

historyRoute.init();

服務器端


// 結合前端,可以用以下方式處理
router.get(‘/page/:path‘, (ctx, next) => {
    ctx.response.type = ‘html‘;
    // 此處的singlePageRoute.html為單頁面html容器,即放置本文中的所有代碼文件
    ctx.response.body = fs.createReadStream(‘./dist/public/files/singlePageRoute.html‘);
    return next();
})
若有疑問或錯誤,請留言,謝謝!Github blog issues

原文地址:https://segmentfault.com/a/1190000017240640

利用hash或history實現單頁面路由