最近看了一些教学,作为自己的总结一下。实现一下low 版本Vue-router
第一步,分析
- vue-router是作为插件来使用的。所以需要暴露一个
install
方法,详情参考这里 - 在我们平常使用过程中,需要就收参数,是一个对象。所以类的
constructor
需要接收一个对象 - Vue-router里面有两个方法,
router-link
,router-view
,需要全局注册 - 分析一下他总共做的事情,
- 监听路由的变化
- 改变对应的模板渲染
- 实现
router-link
,router-view
这两个方法
第二步,开始搭建框架
- 首先我先创建一个叫MyVueRouter的类,里面的构造方法里面有一个叫
options
的参数。还有对应的三个方法,分别执行 第一步4中的事情,还是实现install
方法
MyVueRouter类
let Vue // 先定义定义一个Vue
class MyVueRouter {
constructor (options) { // 在创建实例的时候会接受一个配置项
this.$options = options
this.routeMap = {} // 后面会说
// 路由响应式 强绑定,这就是与 react 里面router区别的原因 只能用于Vue
this.app = new Vue({
data: {
current: '/'
}
})
}
init () {
this.bindEvents() // 监听 url变化,绑定事件
this.createRouterMap(this.$options) // 解析路由配置
this.initComponent() // 实现router-view 那两个组件
}
bindEvents () { // 监听 url变化
}
onHashChange () { // 解析路由配置
}
createRouterMap (options) { // 实现router-view 那两个组件
}
initComponent () {
// router-link router-view
Vue.component('router-link', {
}
})
// router-view
Vue.component('router-view', {
}
}
说一下上面为什么要new Vue()
,我们需要一个值来 记录当前的地址栏current
,所以这里面就用到了Vue的特性。这就是Vue-router只能用在Vue不能用在其他语言中的原因
install 方法
// 作为创建
MyVueRouter.install = function (_vue) { // 作为插件 实际执行的是这个方法 在执行过程中会返回一个Vue实例
Vue = _vue
Vue.mixin({ // 扩展组件
beforeCreate () {
// this 是当前Vue实例
if (this.$options.router) {
// 在跟组件执行一次
Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
// 使用组件
Vue.use(MyVueRouter)
第三步 具体方法的实现
注意这里只实现了hash路由
bindEvents方法
首先理清楚这个方法的作用就是,监听url的变化。当url变化的时候执行指定的方法/函数
bindEvents () {
// 监听刚打开页面的时候 注意绑定this指向
window.addEventListener('load', this.onHashChange.bind(this))
// 监听刚url变化的时候
window.addEventListener('hashchange', this.onHashChange.bind(this))
}
常用事件的网址,需要的大家自行点击 这里 ,注意 不需要加on
onHashChange方法
onHashChange () {
this.app.current = window.location.hash.slice(1) || '/'
// 方便大家看到所有改变 打印的日志
console.log(this.app.current)
console.log(window.location.hash)
}
这个没什么解释的,就是利用改变Vue的响应, 改变current
为什么要从1位开始?
hash路由,前面有# 详细请看 打印日志
createRouterMap
作用 解析路由配置,让其一一对应
这时在上面创建的routeMap
就有用了
createRouterMap (options) {
options.routes.forEach(item => {
this.routeMap[item.path] = item.component
})
}
创建键值对,使component
和路由current
一一对应
initComponent方法
作用 实现router-link
,router-view
这两个方法
initComponent () {
// router-link router-view
Vue.component('router-link', {
props: { to: String },
render (createElement) {
// createElement 参数 tag data(可选) children attrs原生的html属性 this.$slots.default a标签里面的文字
return createElement('a', { attrs: { href: '#' + this.to } }, [this.$slots.default])
// 返回Vnode 虚拟dom
}
})
// router-view
Vue.component('router-view', {
render: (h) => { // h createElement简写 用件头函数 改变了this指向
// 全局组件 跟全局vue有关 里面属性发生变化
const comp = this.routeMap[this.app.current]
// comp 拿到了对应的组件
return h(comp)
}
})
}
router-link
有关 createElement的详情,请点击这里
有关render 函数,请点击 这里
首先进行全局注册
接受一个参数 props:{to: String}
平时使用的是这样使用,如果有需要多加一些,这里只实现这一个
<router-link to="/">Home</router-link>
因为 router-link
最主要作用是跳转,所以我们创建一个a标签
,配置跳转信息来实现跳转
router-view
注意,这里面使用的是箭头函数,不是普通函数,目的,解决this指向问题
因为使用的过程中不需要参数,所以不用写props
我们只需要拿到当前current
对应的,拿到对应的模板,交给createElement渲染就行
第四步 完整代码
import a from './../components/a'
import b from './../components/b'
// url变化 路由解析 组件配置
class MyVueRouter {
constructor (options) { // 在创建实例的时候会接受一个配置项
this.$options = options
this.routeMap = {}
// 路由响应式 强绑定,这就是与 react 里面router区别的原因 只能用于Vue
this.app = new Vue({
data: {
current: '/form'
}
})
}
init () {
this.bindEvents() // 监听 url变化
this.createRouterMap(this.$options) // 解析路由配置
this.initComponent() // 实现router-view 那两个组件
}
bindEvents () {
window.addEventListener('load', this.onHashChange.bind(this))
window.addEventListener('hashchange', this.onHashChange.bind(this))
}
onHashChange () {
this.app.current = window.location.hash.slice(1) || '/'
console.log(this.app.current)
console.log(window.location.hash)
}
createRouterMap (options) {
options.routes.forEach(item => {
this.routeMap[item.path] = item.component
})
}
initComponent () {
// router-link router-view
Vue.component('router-link', {
props: { to: String },
render (createElement) {
// createElement 参数 tag data(可选) children attrs原生的html属性 this.$slots.default a标签里面的文字
return createElement('a', { attrs: { href: '#' + this.to } }, [this.$slots.default])
}
})
// router-view
Vue.component('router-view', {
render: (h) => {
// h createElement缩写 用件头函数 改变了this指向 // 全局组件 跟全局vue有关 里面属性发生变化
const comp = this.routeMap[this.app.current]
console.log(comp)
return h(comp)
}
})
}
}
// 作为创建
MyVueRouter.install = function (Vue) {
// 作为插件 实际执行的是这个方法
// 是可以收到一个Vue实例
Vue.mixin({ // 扩展组件
beforeCreate () {
// this 是当前Vue实例
if (this.$options.router) {
// 在跟组件执行一次
Vue.prototype.$router = this.$options.router
this.$options.router.init()
}
}
})
}
Vue.use(MyVueRouter)
export default new MyVueRouter({
routes: [
{
path: '/',
name: 'A',
component: a
},
{
path: '/b',
name: 'B',
component: b
}
]
})
APP.js代码
<template>
<div id="app">
<router-link to="/">Home</router-link> |
<router-link to="/b">B</router-link> |
<router-view></router-view>
</div>
</template>