概念
通过改变 URL,在不重新请求页面的情况下,更新页面视图。
前后端路由的区别
后端路由: 输入url->向服务器发送请求->服务器解析请求路径->拿到对应页面->返回给前端
前端路由: 输入url->js解析地址->找到对应的页面->执行页面生产的js->页面呈现
Vue工作原理
url改变
⬇️
触发监听事件
⬇️
改变vuerouter中的current变量
⬇️
vue监视current的改变
⬇️
获取到新的组件
⬇️
Render新组件
hash与history
更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有两种方式:
1.Hash --- 利用 URL 中的hash("#");
2.利用 History interface 在HTML5中新增的方法。
在 Vue 中,它是通过 mode 这一参数控制路由的实现模式
const router=new VueRouter({
mode:'history',
routes:[...]
})
两种模式的区别
-
hash - 即地址栏URL中携带 # 符号(此hash不是密码学里的散列运算)
比如这个URL:http://www.abc.com/#/hellohash的值为#/hello.它的特点在于:hash虽然出现在URL中,但不会被包括在HTTP请求中,对后端完全没有影响,因此改变hash不会重新加载页面。 -
history - 利用了HTML5 History Interface中新增的pushState()和replaceState()方法。(需要特定浏览器支持) 这两个方法应用于浏览器的历史记录栈,在当前已有的back、forward、go的基础上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的URL,但浏览器不会立即向后端发送请求。
因此hash模式和histoury模式都是属于浏览器自身的特性,Vue-Router只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由
使用场景
调用history.pushState()相比于直接修改hash ,存在以下优势:
- pushState()设置的新URL可以是与当前URL同源的任意URL;而hash只可修改#后面的部分,因此只能设置与当前URL同文档的URL;
- pushState()设置的新URL可以与当前URL一模一样,这样也会把记录添加到栈中;而hash设置的新值必须与原来不一样才会触发动作将记录添加到栈中;
- pushState()通过stateObject参数可以添加任意类型的数据到记录中;而hash只可添加短字符串;
- pushState()可额外设置title属性供后续使用。
当然history也不是样样都好。SPA虽然在浏览器里游刃有余,但真要通过URL向后端发起HTTP请求时,两者的差异就来了。尤其在用户手动输入URL后回车,或者刷新(重启)浏览器的时候。
- hash 模式下,仅hash符号之前的内容会被包含在请求中,如 www.abc.com 因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回404错误。
- history模式下,前端的URL必须和实际向后端发起请求的URL一致。如 htttp://www.abc.com/book/id 如果后端缺少对/book/id 的路由处理,将返回404错误
Vue-Router
-
vue.use
- 执行里面的方法
- 如果方法有install属性,并且install是个方法,仅执行install
- install方法的第一个参数是vue的构造函数
-
vueRouter实例
-
main.js
-
两个全局组件
代码
let Vue
class VueRouter{
constructor(options){
let initial = '/'
Vue.util.defineReactive(this,'current',initial)//实现响应式
this.current = '/'//当前路由地址
this.routes = options.routes//路由表
this.mode = options.mode || 'hash'
this.init()//监听路由改变
// console.log(Vue.util.defineReactive);//对objectdefineproperty进行了封装
}
init(){
if(this.mode === 'hash'){
//第一次加载
window.addEventListener('load',()=>{
this.current = location.hash.slice(1)
})
//监听hash事件
window.addEventListener('hashchange',()=>{
this.current = location.hash.slice(1)
})
}
}
}
VueRouter.install = function(_Vue){
Vue = _Vue
//给调用的组件添加router属性
Vue.mixin({//混入全局
beforeCreate() {
if(this.$options.router){
Vue.prototype.$router = this.$options.router
}
},
})
Vue.component('router-link',{
props:{
to:{
type:String,
require:true
}
},
render(h) {
return h('a',{
attrs:{
href:'#'+this.to
}
},this.$slots.default)
},
})
Vue.component('router-view',{
render(h) {
console.log(this.$router);
let current = this.$router.current
let routes = this.$router.routes
let com = routes.find(item=> current === item.path)
return h(com.component)
},
})
}
export default VueRouter