涉及知识点:location对象、history对象
文章目录
- 基础概念
- 什么是路由
- 如何实现前端路由
- 涉及问题
- 前端路由实现方式
- 1. hash方式
- 2. history方式
- 3. debug:本地起服务报错
- 扩展:封装路由类Router
- hash
- history
基础概念
什么是路由
路由是一组映射关系,本质上来说是location.href和UI的映射关系
如何实现前端路由
修改location.href,而页面不会主动刷新,需要用js控制对应展示UI
涉及问题
- 如何修改location.href
- 何时改变UI
- 如何浏览器前进/后退🔙事件
前端路由实现方式
通过hash和history的方式切换路由时都不会引起页面的刷新
1. hash方式
hash通过a标签默认的行为来改变location.hash
通过hashchange事件来监听location.hash改变,可以指定相应呈现内容
此外,还可以跳转到对应id的元素处
<body>
<a href="#hash1">hash2</a>
<a href="#hash2">hash3</a>
<a href="#green">green</a>
<div id="content">当前路由的内容为:</div>
<div style="height: 1000px; background: red;"></div>
<div id="green" style="height: 1000px; background: green;"></div>
<script>
const onHashChange = () => {
const contentEl = document.getElementById('content')
contentEl.innerHTML = window.location.hash
}
window.addEventListener('hashchange', onHashChange)
</script>
</body>
2. history方式
单页面应用,变换loctaion的hash,但是页面不会自动刷新,需要js指定要改变的UI
原理:调用 history对象的replaceState或者pushState方法来改变路由,手动改变对应UI样式
<body>
<button>nav1</button>
<button>nav2</button>
<button>nav3</button>
<div id="content">当前路由的内容为:</div>
<script>
const onPopState = () => {
const contentEl = document.getElementById('content')
contentEl.innerHTML = window.location.pathname
}
// button 只会改变location.path,不会出发popstate实践,监听不到
const buttonEls = document.getElementsByTagName('button')
for (let btnEl of buttonEls) {
// 1 改变location中路径
btnEl.onclick = () => {
window.history.pushState(
{ nav: btnEl.innerHTML },
'title',
'/' + btnEl.innerHTML,
)
// 2 手动改变UI
onPopState()
}
}
// 点击前进返回按钮时,会调用popstate.改变UI
window.addEventListener('popstate', onPopState)
</script>
</body>
3. debug:本地起服务报错
原因:history路由中的popstate不支持file协议
解决步骤参考:https://blog.csdn.net/am123999/article/details/120582419
扩展:封装路由类Router
如何使用:class Router
注册:register(navName, fn)
执行:emit(navName)
初始化:init() 最初路由对应的UI
hash
<body>
<a href="#hash1">hash2</a>
<a href="#hash2">hash3</a>
<a href="#green">green</a>
<div id="content">当前路由的内容为:</div>
<div style="height: 1000px; background: red;"></div>
<div id="green" style="height: 1000px; background: green;"></div>
<script>
class Router {
routerList
constructor() {
this.routerList = {}
window.onhashchange = () => {
this.routerList[this.getHashName()]()
}
}
getHashName() {
return location.hash
}
register(navName, fn) {
this.routerList[navName] = fn
}
emit(navName) {
typeof this.routerList[navName] === 'function' &&
this.routerList[navName]()
}
init() {
this.emit('/')
}
}
const router = new Router()
router.register('#hash1', () => {
console.log('当前hash为#hash1')
})
router.register('#hash2', () => {
console.log('当前hash为#hash2')
})
router.register('#green', () => {
console.log('当前hash为green')
})
</script>
</body>
history
<body>
<a href="/">主页</a>
<a href="nav1">nav1</a>
<a href="nav2">nav2</a>
<a href="nav3">nav3</a>
<script>
class Router {
routerList
constructor() {
this.routerList = {}
window.onpopstate = () => {
this.routerList[this.getPathName()]()
}
}
register(navName, fn) {
this.routerList[navName] = fn
}
getPathName() {
return location.pathname
}
emit(navName) {
history.pushState({ nav: navName }, null, navName)
typeof this.routerList[navName] === 'function' &&
this.routerList[navName]()
}
init() {
this.emit('/')
}
}
// 使用
const router = new Router()
// 注册函数和路由
router.register('/', () => {
console.log('切换到主页')
})
router.register('nav1', () => {
console.log('切换到nav1')
})
router.register('nav2', () => {
console.log('切换到nav2')
})
router.register('nav3', () => {
console.log('切换到nav3')
})
router.init()
const aEls = document.getElementsByTagName('a')
for (const aEl of aEls) {
aEl.onclick = (e) => {
e.preventDefault()
router.emit(e.target.getAttribute('href'))
}
}
</script>
</body>