前端路由与单页页面
路由就是指随着浏览器地址栏的变化,展示给用户的页面也不相同。
传统的网页根据用户访问的不同的地址,浏览器从服务器获取对应页面的内容展示给用户。这样造成服务器压力比较大,而且用户访问速度也比较慢。在这种场景下,出现了单页应用。
单页应用(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
})