单页面应用原理
单页面应用的特征,说穿了就是两点:
1.通过 js 能够改变浏览器地址栏的地址,然后页面也跟着改变,但是不会重新加载页面。
想通过 js 能够改变浏览器地址栏的地址,很简单,比如对location.href,或者location.pathname进行赋值,但这会导致浏览器会重新加载一个目标页面。要想不重新加载页面有两种方式:一是对 location.hash 进行赋值,二是使用 histrory 对象的 pushState 或 replaceState 方法。还剩最后一个问题,如何让页面也跟着改变?如果我能监听到地址栏变化,然后调用函数(比如叫updateView)更新页面就行了。
如:
window.onTheUrlChange = function updateView() {}
如果不能监听到的话,那直接在改变地址栏后,再去调用updateView也行。
function toggleRoute() {
changeURL();
updateView();
}
幸运的是,还真能监听到地址栏变化。
window.onhashchange、window.onpopstate这两个api,不仅能监听到对location.hash的赋值,或者调用histrory.pushState\histrory.replaceState导致的地址栏改变,而且还能监听到浏览器前进、后退按钮带来的地址栏改变,或者history.go(-1)之类的,总之,只要是地址栏的改变,都能被捕获。
2.能恢复刷新前的页面
window.onhashchange、window.onpopstate有个小问题,刷新页面时两者都不会被触发。但是刷新又天然的会重新加载页面,导致刷新前的页面丢失。比如我们自应用首页进入了about页面,此时刷新浏览器,页面被重新加载,所以我们又回到了首页。这时候可以用load事件,根据当前url中的信息,知道应该渲染的是about。所以调用updateView('about')就行。
路由的实现方式
url的组成部分:
http:127.0.0.1:80/main/doc?lang=cn#part/1
协议
| 主机地址
| 端口
| 路径(pathname)
| search
| hash
pathname部分(/main/doc),刷新时会被带进重新请求的url中
hash部分(part/1),刷新时会被忽略掉
所以刷新后的请求url是http:127.0.0.1:80/main/doc?lang=cn
方式一:hash 模式
location.hash + hashchange
原理:通过location.hash 改变地址栏hash,监听地址栏hash变化,更新视图
- 当点击导航元素时,对location.hash进行赋值,触发hashchange
- 当点击浏览器前进后退按钮时,触发hashchange
- 当手动改变地址栏然后enter时以及当F5刷新时,hash部分,不会被加入到请求路径中,(如
http:127.0.0.1:80/main/doc?lang=cn#part/1
刷新,浏览器发送请求url是http:127.0.0.1:80/main/doc?lang=cn
)
无法监听到hashchange事件,则通过load事件来更新视图
$('.btn').click(function() {
location.hash = 'user'
})
window.onhashchange = function() {
$('router-outlet').html(user-template)
}
window.onload = function() {
const path = location.hash;
const template = getTemplate(path);
$('router-outlet').html(template)
}
方式二:history模式
history.pushState() + popstate
因为history模式,路由信息部分是加到url的pathname部分的,所以刷新时,路由信息会被添加到请求中,如果服务端不处理,会导致404,所以需要服务端配合重定向。只要收到对某个路由的请求,统一返回index.html。
$('.btn').click(function() {
let data = {name: '张三'};
history.pushState(data, '', 'user/zhangsan');
$('router-outlet').html(template)
})
window.onpopstate = function() {
$('router-outlet').html(user-template)
}
// 后端重定向后需要
window.onload = function() {
const path = location.pathname;
const template = getTemplate(path);
$('router-outlet').html(template)
}
框架路由的一些共同点(ngjs\ng\vue)
1.多视图,可以有多个路由容器
2.路由嵌套,可以层层嵌套
3.路由参数,参数都有三种形式
- 动态路由参数,出现在url的pathname部分
- params
- query ,出现在url的search部分
4.路由守卫
网友评论