路由就是指随着浏览器地址栏的变化,展示给用户的页面也不相同。 传统的网页根据用户访问的不同的地址,浏览器从服务器获取对应页面的内容展示给用户。这样造成服务器压力比较大,而且用户访问速度也比较慢。在这种场景下,出现了单页应用。 单页应用(spa),就是只有一个页面,用户访问一个网址,服务器返回的页面始终只有一个,不管用户改变了浏览器地址栏的内容或者在页面内发生了跳转,服务器不会重新返回新的页面,而是通过相应的js操作来实现页面的更改。而地址栏内容的改变,显示不同的页面,实现的手段就是前端路由。 前端路由不需要请求服务器通过js改变页面,后端路由需要请求服务器来改变页面 前端路由是现代SPA应用必备的功能,每个现代前端框架都有对应的实现,例如vue-router、react-router。 我们不去探究vue-router或者react-router们的实现,因为不管是哪种路由无外乎用兼容性更好的hash实现或者是H5 History实现,与框架几个只需要做相应的封装即可。
我们经常在 url 中看到 #,这个 # 有两种情况,一个是我们所谓的锚点,比如典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,但是路由里的 # 不叫锚点,我们称之为 hash。 现在的前端主流框架的路由实现方式都会采用 Hash 路由,本项目采用的也是。 当 hash 值发生改变的时候,我们可以通过 hashchange 事件监听到,从而在回调函数里面触发某些方法。
hash+hashchange实现
这种方法的好处在于支持IE浏览器。对早期的一些浏览器的支持比较好。 实现原理: location.hash始终指向页面url 中#之后的内容 当当前页面的url =’www.baidu.com’,可以在浏览器的控制台输入location.hash为空,当页面指向url =’www.baidu.com/#/hello’的时候,location.hash = ‘#/hello’。通过读取location.hash可以知道当前页面所处的位置。通过hashchange事件可以监听location.hash的变化,从而进行相应的处理即可。
那么如何触发hash的改变呢?这里主要有两种方法:
设置a标签,href = ‘#/blue’,当点击标签的时候,可以在当前url的后面增加上’#/blue’,同时触发hashchange,再回调函数中进行处理。另外 直接在js中对location.hash = ‘#/blue’即可,此时url会改变,也会触发hashchange事件。下面我们自己实现一个前端路由
<!-- 导航 --> <a href="#/chat">聊天</a> <a href="#/contact">通讯录</a> <!-- 页面中显示内容的部分 --> <div class="router-view"> adfadsf </div> let routerView = document.querySelector('.router-view') window.onhashchange = function() { console.log('hash change', location.hash) if (location.hash == '#/chat') { routerView.innerHTML = ` 我是聊天页面 ` } else if (location.hash == '#/contact') { routerView.innerHTML = '我是通讯录页面' } } //初始页面设为聊天页面 window.onload = function() { location.hash = '#/chat'; }以上代码封装为:
// 配置不同的路由 要显示的页面内容 let routes = [{ path: '#/chat', page: `我是聊天页面` }, { path: '#/contact', page: `我是contact页面` } ] class MyVueRouter { constructor(obj) { let routes = obj.routes; let routerView = document.querySelector('.router-view') // 监听hashchange 变化,一旦有变化,意味着可能要改变页面显示的内容 window.onhashchange = function() { //#/chat console.log('hash change', location.hash) // 获取变化以后的hash // 去路由配置中查找是否配置过这个路由,并找到这个路由配置 let routeObj = routes.find(v => v.path == location.hash) console.log(routeObj) if (routerObj) { // 如果存在这个配置 就把配置中要显示的内容显示到页面上 routerView.innerHTML = routeObj.page; } } } } let router = new MyVueRouter({ routes })