编程式导航
// 将新的路由推入 history 中,history.length + 1
this.$router.push()
// 用新的路由替换掉当前 histroy 中的末尾项
this.$router.replace()
// 在 history 数组中,前进回退
// + 前进 - 后退
this.$router.go(-1)
hash 模式
- URL中带有 # 标识,如:https://music.163.com/#/playlist?id=202
- URL 中 # 后面的内容作为路径地址
- 基于锚点,以及 onhashchange 事件
- 当锚点变化时,触发 onhashchange 事件,根据当前路由地址找到对应组件,重新渲染、更新页面
history 模式
概述
- 链接更为简洁,如:https://music.163.com/playlist/202
- 基于 HTML5 中的 History API
- history.pushState()
- history.replaceState()
- 监听 popstate 事件
- 根据当前路由地址找到对应组件重新渲染
使用事项
- 需要服务器的支持
- 在服务端应该除了静态资源外都返回单页应用的 index.html
- 单页应用中,服务端只有一个 index.html 页面
- 单页应用中,服务端不存在 http://www.test.com/login 这样的地址,若未进行配置,则会因为找不到该页面对应的文件,返回 404
实现原理
分析
属性 | options | 记录构造函数中传入的对象 |
data | 响应式对象 包含属性 current,用于记录当前地址 | |
routeMap | 记录路由地址和组件的对应关系 | |
方法 | Constructor(Options): VueRouter | |
install(Vue): void | 静态方法,用于实现 Vue 的插件机制 | |
init(): void | 用于调用后边的 3 个方法 | |
initEvent(): void | 用于注册 popState(),监听浏览器历史变化 | |
createRouteMap(): void | 由于初始化 routeMap,将构造函数中的路由规则转化为键值对的形式,存储到 routeMap 中 | |
initComponents(vue): void | 用于创建 <route-link> 和 <route-view> |
实现示例
let _Vue = null
export default class VueRouter {
static install(Vue) {
// 1、判断当前插件是否已经被安装
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
// 2、把 Vue 构造函数记录到全局变量
_Vue = Vue
// 3、把创建 Vue 实力时候传入的 router 对象注入到 Vue 实例上
_Vue.mixin({ // 混入
beforeCreate() {
if (this.$options.router) { // 判断是组件还是实例,组件的 $options 中不含有 router 属性
_Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
constructor(options) {
this.options = options
this.routeMap = {}
this.data = _Vue.observable({
current: '/'
})
}
init() {
this.createRouteMap()
this.initComponents(_Vue)
this.initEvent()
}
createRouteMap() {
// 遍历所有的路由规则,将路由规则解析成键值对的形式,存储到 routeMap 中
this.options.routes.forEach(route => {
this.routeMap[route.path] = route.component
})
}
initComponents(Vue) {
Vue.component('router-link', {
props: {
to: String
},
// 运行时版本实现方案
/*
* h() 有 Vue 传递进来
* h() 可传入 3个参数
* 1、选择器,传入标签名
* 2、为创建的对象添加设置,值为一个对象,通过对象中 attrs 来设置标签属性,通过 on 设置事件监听
* 3、生成的标签里的内容,值为一个数组,通过 default 获取默认插槽的内容
* */
render(h) {
return h('a', {
attrs: {
href: this.to
},
on: {
click: this.clickHandler
}
}, [this.$slots.default])
},
methods: {
clickHandler(e) {
history.pushState({}, '', this.to)
this.$router.data.current = this.to
e.preventDefault()
}
}
// 完整版,即配置携带编译器的情况下可用
// template: '<a :href="to"><slot></slot></a>'
})
const _this = this
Vue.component('router-view', {
render(h) {
const component = _this.routeMap[_this.data.current]
return h(component)
}
})
}
initEvent() {
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname
})
}
}
注意事项
修改配置
// router/index.js
import VueRouter from 'vue-router'
// ===>
import VueRouter from '../myrouter'
$router / $route
- this.$router 中包含属性 currentRoute,该属性可以用来获取当前的路由信息
- this.$route 是当前路由的信息
Hash 模式
因为本身是基于锚点的,因此刷新浏览器返回的始终是 SPA 的指定的 index.html,所以服务端不需要特殊配置
Vue 的构建问题
- 运行时版
- 不支持 template 模板,需要打包的时候提前编译
- 完整版
- 包含运行时和编译器,体积比运行时版大 10K 左右,程序运行时会把模板转换成 render 函数
- 配置
// vue.config.js
module.exports = {
runtimeCompiler: true // 是否带编译(完整)版本,默认为 false
}