路由原理
更新视图但不重新请求页面
,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有2种方式:hash和history interface,mode取值其实还有abstract,用于非浏览器的情况。
hash方式主要是通过url中的#,在HTML中#是锚点,指向页面中的位置,#后面的数据改变,不会重新请求页面。通过hashchange监听实现更新页面操作。在vue中,hash模式主要是创建HashHistory
对象,然后通过调用push和replace来更改路由。
html5中的history interface
,它提供了访问浏览器历史记录栈的接口,通过go()/back()/forward()
来读取历史记录,通过pushState() 和 replaceState() 修改路由。同时通过监听onpopstate()监听地址变化。
1. 利用URL中的hash("#");
在初始化对应的history之前,会对mode做一些校验:若浏览器不支持HTML5History方式(通过supportsPushState变量判断),则mode设为hash;若不是在浏览器环境下运行,则mode设为abstract;
abstract模式:其原理为用一个数组stack模拟出浏览器历史记录栈的功能。
VueRouter类中的onReady(),push()等方法只是一个代理,实际是调用的具体history对象的对应方法,在init()方法中初始化时,也是根据history对象具体的类别执行不同操作
2. 利用History interface在HTML5中新增的方法;
History interface是浏览器历史记录栈提供的接口,通过back(),forward(),go()
等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
从HTML5开始,History interface提供了2个新的方法:pushState(),replaceState()使得我们可以对浏览器历史记录栈进行修改:
window.history.pushState(stateObject,title,url)
window.history,replaceState(stateObject,title,url)
stateObject:当浏览器跳转到新的状态时,将触发popState事件,该事件将携带这个stateObject参数的副本
title:所添加记录的标题
url:所添加记录的url
这2个方法有个共同的特点:当调用他们修改浏览器历史栈后,虽然当前url改变了,但浏览器不会立即发送请求该url,这就为单页应用前端路由,更新视图但不重新请求页面提供了基础。
一、hashchange
hash虽然出现在url中,但不会被包括在http请求中,它是用来指导浏览器动作的,对服务器端完全无用,因此,改变hash不会重新加载页面。
每一次改变hash(window.location.hash),都会在浏览器访问历史中增加一个记录。
可以为hash的改变添加监听事件:
window.addEventListener("hashchange",funcRef,false)
通过监听hashchange事件,可以监听到url上锚点数据(#xxx)的改变,可以获取这个事件。
window.addEventListener('hashchange',function(){
console.log(location.hash);
//锚点值,即#以及后面的字符
})
利用hash的以上特点,就可以来实现前端路由"更新视图但不重新请求页面"的功能了。
SPA(单页应用)页面不跳转,只通过局部数据改变。
<body>
<a href="#login">登录</a>
<a href="#register">注册</a>
<div id = "main"></div>
</body>
<script type="text/javascript">
var mainPage = document.getElementById("main")
window.addEventListener('hashchange',function(){
console.log(location.hash);
//锚点值
switch(location.hash){
case "#login" :
mainPage.innerHTML = "登录页面";
break;
case "#register" :
mainPage.innerHTML = "注册页面";
break;
}
})
</script>
<body>
<div id="app">
<router-link :to="{name:'login'}">登录</router-link>
<router-link to="/register">注册</router-link>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
</body>
<script type="text/javascript">
var Login = {
template: '<h2>login</h2>'
}
var Reg = {
template: `<h2>Register</h2>`
}
// 2、Vue使用vueRouter插件
Vue.use(VueRouter);
// 3、创建一个路由对象实例
var router = new VueRouter({
// 4、配置路由对象
//路由对象赋予name,那么在修改path的时候,不需要改其他引用路由的地方
//这里是routes不要写错了,否则路由有变化但不会加载页面
routes: [
{
<!-- 命名路由 -->
name:'login',
path: '/mylogin',
component: Login
},
{
path: '/register',
component: Reg
}
]
})
// 5、 将配置好的路由对象关联到Vue实例中
new Vue({
el: '#app',
router: router,
});
</script>
二、$router 和 $route
$router 路由操作对象,只写
this.$router.push({name:"login",params:{id:2}});
//router-link方式
<router-link :to="{path:'/mylogin',query:{str:'hehe'}}">登录</router-link>
$route 路由信息对象,只读
this.$route.params.id //'2'
this.$route.query.str //'hehe'
三、嵌套路由
{
path: '/register',
component: Reg,
children:[{
name:'register.women',
path:'women',
component:Women,
}]
}
//注册页面中要加上<router-view>作为子路由的出口
var Reg = {
template: `
<div>
<h2>Register</h2>
<router-view></router-view>
</div>`
}
//调用
<router-link :to='{name:"register.women"}'>跳转子路由</router-link>
- 如果子路由的path加了/,那么url显示就不包括父路由
- 路由meta元数据:对于路由规则是否需要验证权限的配置
- 路由钩子:权限控制的函数执行时期
router.beforeEach(function(to,from,next){})
四、路由中的component和components
- component 是常规路由中的组件配置名(例子如上)
- components 是命名视图中的组件配置名
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
//一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s):
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
五、路由组件传参
$route 或者 props
六、导航守卫
- 当离开一个页面时,导航被触发
- 执行组件内守卫
beforeRouteLeave
- 调用全局前置守卫
beforeEach
- 在重用组件里面调用
beforeRouteUpdate
- 在路由配置里面调用
beforeEnter
- 解析异步路由组件
- 在激活的组件里面调用
beforeRouteEnter
- 调用全局解析守卫
beforeResolve
- 导航被确认
- 调用全局后置守卫
afterEach
钩子 - 触发DOM更新
beforeEach
- 在重用组件里面调用
beforeRouteUpdate
- 在路由配置里面调用
beforeEnter
- 解析异步路由组件
- 在激活的组件里面调用
beforeRouteEnter
- 调用全局解析守卫
beforeResolve
- 导航被确认
- 调用全局后置守卫
afterEach
钩子 - 触发DOM更新
- 创建好的实例调用
beforeRouterEnter
守卫中传给next
的回调函数