利用hash或history實現單頁面路由
目錄
在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實現單頁面路由