Hash模式
- URL中#后面的内容作为路径地址 // hash值发生改变的时候不会请求后端。
- 监听hashchange事件
- 根据当前路由地址找到对应的组件重新渲染。
History模式
- 通过history.pushState()方法改变地址栏 // 只是改变地址栏地址,改变浏览器访问历史。 兼容IE10及以上
- 监听popstate事件 // 浏览器历史的变化 back(). go() 会触发。
- 根据当前路由地址找到对应组件重新渲染
实现自己的vue Router ----准备
Vue.use() // 可以一个函数或者一个对象。
Vue Router -install
let _Vue = null
export default class VueRouter {
/**
* @name: install
* @msg: 静态类
* @param {Object} Vue
* @return {null} null
*/
static install (Vue) {
// 1. 判断当前插件是否被安装
if (VueRouter.install.installed) {
return
}
VueRouter.install.installed = true
// 2. 把Vue构造函数记录到全局变量
_Vue = Vue
// 3. 把创建Vue实例时候传入的router对象注入到Vue实例上。
// _Vue.prototype.$router = this.$options.router
// 混入
_Vue.mixin({
beforeCreate () {
if (this.$options.router) {
_Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
/**
* @name: constructor
* @msg: 构造函数
* @param {viod} options
* @return {null} null
*/
constructor (options) {
this.options = options
this.routeMap = {}
console.log(_Vue)
this.data = _Vue.observable({
current: '/'
})
}
init () {
this.createRouteMap()
this.initComponents(_Vue)
this.initEvent()
}
/**
* @name: createRouteMap
* @msg: options参数遍历到routeMap这个对象里面
* @param {null} null
* @return {null} null
*/
createRouteMap () {
// 遍历所有的路由规则,把路由规则解析成键值对的形成 储存到routeMap中
this.options.routes.forEach(route => {
// 见路由和组件以键值对的方式
this.routeMap[route.path] = route.component
})
}
/**
* @name: initComponents
* @test: test font
* @msg: 初始化路由组件
* @param {Object} Vue
* @return {null} null
*/
initComponents (Vue) {
Vue.component('router-link', {
props: {
to: String
},
/**
* @name: render
* @msg: 渲染虚拟dom
* @param {function} h
* @return {function} h()
*/
render (h) {
/**
* @name: h
* @msg: 返回创建好的元素
* @param {string} 标签如:‘a’ 可以是选择器
* @param {Object} {}
* @return {type}
*/
// 创建的a标签会导致页面刷新,而且会再次请求服务端。
// 我需要的效果是浏览器地址栏改变且不请求服务端。且页面不刷新。
// vue监听浏览器地址栏的变化。因为vue是单页面应用。所有组件被render()
// 创建成vnode,然后根据监听地址栏渲染对应的组件。
// (createElement: () => VNode) => VNode
return h('a', {
attrs: {
href: this.to
},
on: {
click: this.clickHandler
}
}, [this.$slots.default])
},
methods: {
clickHandler (e) {
// 将当前地址栏改成this.to
// 将current赋值为this.to找到对应的组件。
history.pushState({}, '', this.to)
this.$router.data.current = this.to
e.preventDefault()
}
}
// template: '<a :href="to"><slot></slot></a>'
})
const self = this
Vue.component('router-view', {
render (h) {
// 根据当前浏览器地址获取到对应的组件。将组件交给h()解析出虚拟dom.
const component = self.routeMap[self.data.current]
return h(component)
}
})
}
/**
* @name: initEvent
* @msg: 注册popstate事件
* @param {null} null
*/
initEvent () {
window.addEventListener('popstate', () => {
this.data.current = window.location.pathname
})
}
}
vueRouter错误集合
错误一
vuecli创建的vue默认是运行版本的vue.也就是template模版已经编译成render 函数。如果这个时候我们再在组件加入template模版。这个时候vue就没法编译。这个时候我们可以创建一个vue.config.js配置文件。runtimeCompiler: true. 我们还可以使用直接写render方法创建元素。
是否使用包含运行时编译器的 Vue 构建版本。设置为 true 后你就可以在 Vue 组件中使用 template 选项了,但是这会让你的应用额外增加 10kb 左右。
- 因为在组件内部使用了template.
- 解决办法一
- 解决办法二
/**
* @name: render
* @msg: 渲染虚拟dom
* @param {function} h
* @return {function} h()
*/
render (h) {
/**
* @name: h
* @msg: 返回创建好的元素
* @param {string} 标签如:‘a’ 可以是选择器
* @param {Object} {}
* @return {type}
*/
return h('a', {
attrs: {
href: this.to
}
}, [this.$slots.default])
}
- router-link:
- active-class 所以to='/*'属性都加上class
- exact-active-class // 精确匹配