涉及知识点: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 手动改变UIonPopState()}}// 点击前进返回按钮时,会调用popstate.改变UIwindow.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 {routerListconstructor() {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 {routerListconstructor() {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>