前言前言还是那样,懂得如何使用一个常用库,还得了解其原理或者怎么模拟实现,今天实现一下 vue-router 。有一些知识我这篇文章提到了,这里就不详细一步步写,请看我 手写一个简易的 Vuex 手写一个简易的 Vuex 基本骨架
基本骨架

Vue 里面使用插件的方式是 Vue.use(plugin) ,这里贴出它的用法:


Vue 里面使用插件的方式是 Vue.use(plugin) ,这里贴出它的用法:

Vue.use(plugin)
安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。
安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。

全局混入
全局混入使用 Vue.mixin(mixin)Vue.mixin(mixin)
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。可以使用混入向组件注入自定义的行为,它将影响每一个之后创建的 Vue 实例。
全局注册一个混入,影响注册之后所有创建的每个 Vue 实例。可以使用混入向组件注入自定义的行为,它将影响每一个之后创建的 Vue 实例。

路由用法


路由用法

比如简单的:
// 路由数组
const routes = [
{

path: '/',

name: 'Page1',

component: Page1,
},
{

path: '/page2',

name: 'Page2',

component: Page2,
},
]

const router = new VueRouter({
mode: 'history', // 模式
routes,
})
// 路由数组
const routes = [
{

path: '/',

name: 'Page1',

component: Page1,
},
{

path: '/page2',

name: 'Page2',

component: Page2,
},
]

const router = new VueRouter({
mode: 'history', // 模式
routes,
})它是传入了mode和routes,我们实现的时候需要在VueRouter构造函数中接收。moderoutesVueRouter在使用路由标题的时候是这样:





Go to Foo
Go to Bar









Go to Foo
Go to Bar




故我们需要使用Vue.component( id, [definition] )注册一个全局组件。Vue.component( id, [definition] )了解了大概,我们就可以写出一个基本骨架
let Vue = null

class VueRouter {
constructor(options) {

this.mode = options.mode || 'hash'

this.routes = options.routes || []
}
}

VueRouter.install = function (_Vue) {
Vue = _Vue

Vue.mixin({

beforeCreate() {

// 根组件

if (this.$options && this.$options.router) {

this._root = this // 把当前vue实例保存到_root上

this._router = this.$options.router // 把router的实例挂载在_router上

} else if (this.$parent && this.$parent._root) {

// 子组件的话就去继承父组件的实例,让所有组件共享一个router实例

this._root = this.$parent && this.$parent._root

}

},
})

Vue.component('router-link', {

props: {

to: {

type: [String, Object],

required: true,

},

tag: {

type: String,

default: 'a', // router-link 默认渲染成 a 标签

},

},

render(h) {

let tag = this.tag || 'a'

return {this.$slots.default}

},
})

Vue.component('router-view', {

render(h) {

return h('h1', {}, '视图显示的地方') // 暂时置为h1标签,下面会改

},
})
}

export default VueRouter
let Vue = null

class VueRouter {
constructor(options) {

this.mode = options.mode || 'hash'

this.routes = options.routes || []
}
}

VueRouter.install = function (_Vue) {
Vue = _Vue

Vue.mixin({

beforeCreate() {

// 根组件

if (this.$options && this.$options.router) {

this._root = this // 把当前vue实例保存到_root上

this._router = this.$options.router // 把router的实例挂载在_router上

} else if (this.$parent && this.$parent._root) {

// 子组件的话就去继承父组件的实例,让所有组件共享一个router实例

this._root = this.$parent && this.$parent._root

}

},
})

Vue.component('router-link', {

props: {

to: {

type: [String, Object],

required: true,

},

tag: {

type: String,

default: 'a', // router-link 默认渲染成 a 标签

},

},

render(h) {

let tag = this.tag || 'a'

return {this.$slots.default}

},
})

Vue.component('router-view', {

render(h) {

return h('h1', {}, '视图显示的地方') // 暂时置为h1标签,下面会改

},
})
}

export default VueRoutermodemodevue-router有两种模式,默认为 hash 模式。vue-routerhistory 模式history 模式通过window.history.pushStateAPI 来添加浏览器历史记录,然后通过监听popState事件,也就是监听历史记录的改变,来加载相应的内容。window.history.pushStateAPI popState

popstate 事件
popstate 事件
当活动历史记录条目更改时,将触发 popstate 事件。如果被激活的历史记录条目是通过对 history.pushState()的调用创建的,或者受到对 history.replaceState()的调用的影响,popstate 事件的 state 属性包含历史条目的状态对象的副本。
当活动历史记录条目更改时,将触发 popstate 事件。如果被激活的历史记录条目是通过对 history.pushState()的调用创建的,或者受到对 history.replaceState()的调用的影响,popstate 事件的 state 属性包含历史条目的状态对象的副本。

History.pushState()方法


History.pushState()方法


window.history.pushState(state, title, url)
window.history.pushState(state, title, url)该方法用于在历史中添加一条记录,接收三个参数,依次为:


state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地,重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null。

title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。

url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。



state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地,重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null。

title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。

url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。
state:一个与添加的记录相关联的状态对象,主要用于popstate事件。该事件触发时,该对象会传入回调函数。也就是说,浏览器会将这个对象序列化以后保留在本地,重新载入这个页面的时候,可以拿到这个对象。如果不需要这个对象,此处可以填null。title:新页面的标题。但是,现在所有浏览器都忽视这个参数,所以这里可以填空字符串。url:新的网址,必须与当前页面处在同一个域。浏览器的地址栏将显示这个网址。hash 模式
hash 模式
使用 URL 的 hash 来模拟一个完整的 URL。,通过监听hashchange事件,然后根据hash值(可通过 window.location.hash 属性读取)去加载对应的内容的。继续增加代码,
let Vue = null

class HistoryRoute {
constructor() {

this.current = null // 当前路径
}
}

class VueRouter {
constructor(options) {

this.mode = options.mode || 'hash'

this.routes = options.routes || []

this.routesMap = this.createMap(this.routes)

this.history = new HistoryRoute() // 当前路由

this.initRoute() // 初始化路由函数
}

createMap(routes) {

return routes.reduce((pre, current) => {

pre[current.path] = current.component

return pre

}, {})
}

initRoute() {

if (this.mode === 'hash') {

// 先判断用户打开时有没有hash值,没有的话跳转到 #/

location.hash ? '' : (location.hash = '/')

window.addEventListener('load', () => {

this.history.current = location.hash.slice(1)

})

window.addEventListener('hashchange', () => {

this.history.current = location.hash.slice(1)

})

} else {

// history模式

location.pathname ? '' : (location.pathname = '/')

window.addEventListener('load', () => {

this.history.current = location.pathname

})

window.addEventListener('popstate', () => {

this.history.current = location.pathname

})

}
}
}

VueRouter.install = function (_Vue) {
Vue = _Vue

Vue.mixin({

beforeCreate() {

if (this.$options && this.$options.router) {

this._root = this

this._router = this.$options.router

Vue.util.defineReactive(this, '_route', this._router.history) // 监听history路径变化

} else if (this.$parent && this.$parent._root) {

this._root = this.$parent && this.$parent._root

}

// 当访问this.$router时即返回router实例

Object.defineProperty(this, '$router', {

get() {

return this._root._router

},

})

// 当访问this.$route时即返回当前页面路由信息

Object.defineProperty(this, '$route', {

get() {

return this._root._router.history.current

},

})

},
})
}

export default VueRouter
let Vue = null

class HistoryRoute {
constructor() {

this.current = null // 当前路径
}
}

class VueRouter {
constructor(options) {

this.mode = options.mode || 'hash'

this.routes = options.routes || []

this.routesMap = this.createMap(this.routes)

this.history = new HistoryRoute() // 当前路由

this.initRoute() // 初始化路由函数
}

createMap(routes) {

return routes.reduce((pre, current) => {

pre[current.path] = current.component

return pre

}, {})
}

initRoute() {

if (this.mode === 'hash') {

// 先判断用户打开时有没有hash值,没有的话跳转到 #/

location.hash ? '' : (location.hash = '/')

window.addEventListener('load', () => {

this.history.current = location.hash.slice(1)

})

window.addEventListener('hashchange', () => {

this.history.current = location.hash.slice(1)

})

} else {

// history模式

location.pathname ? '' : (location.pathname = '/')

window.addEventListener('load', () => {

this.history.current = location.pathname

})

window.addEventListener('popstate', () => {

this.history.current = location.pathname

})

}
}
}

VueRouter.install = function (_Vue) {
Vue = _Vue

Vue.mixin({

beforeCreate() {

if (this.$options && this.$options.router) {

this._root = this

this._router = this.$options.router

Vue.util.defineReactive(this, '_route', this._router.history) // 监听history路径变化

} else if (this.$parent && this.$parent._root) {

this._root = this.$parent && this.$parent._root

}

// 当访问this.$router时即返回router实例

Object.defineProperty(this, '$router', {

get() {

return this._root._router

},

})

// 当访问this.$route时即返回当前页面路由信息

Object.defineProperty(this, '$route', {

get() {

return this._root._router.history.current

},

})

},
})
}

export default VueRouterrouter-link 和 router-view 组件router-link 和 router-view 组件
VueRouter.install = function (_Vue) {
Vue = _Vue

Vue.component('router-link', {

props: {

to: {

type: [String, Object],

required: true,

},

tag: {

type: String,

default: 'a',

},

},

methods: {

handleClick(event) {

// 阻止a标签默认跳转

event && event.preventDefault && event.preventDefault()

let mode = this._self._root._router.mode

let path = this.to

this._self._root._router.history.current = path

if (mode === 'hash') {

window.history.pushState(null, '', '#/' + path.slice(1))

} else {

window.history.pushState(null, '', path.slice(1))

}

},

},

render(h) {

let mode = this._self._root._router.mode

let tag = this.tag || 'a'

let to = mode === 'hash' ? '#' + this.to : this.to

console.log('render', this.to)

return (



{this.$slots.default}



)

// return h(tag, { attrs: { href: to }, on: { click: this.handleClick } }, this.$slots.default)

},
})

Vue.component('router-view', {

render(h) {

let current = this._self._root._router.history.current // current已经是动态响应

let routesMap = this._self._root._router.routesMap

return h(routesMap[current]) // 动态渲染对应组件

},
})
}
VueRouter.install = function (_Vue) {
Vue = _Vue

Vue.component('router-link', {

props: {

to: {

type: [String, Object],

required: true,

},

tag: {

type: String,

default: 'a',

},

},

methods: {

handleClick(event) {

// 阻止a标签默认跳转

event && event.preventDefault && event.preventDefault()

let mode = this._self._root._router.mode

let path = this.to

this._self._root._router.history.current = path

if (mode === 'hash') {

window.history.pushState(null, '', '#/' + path.slice(1))

} else {

window.history.pushState(null, '', path.slice(1))

}

},

},

render(h) {

let mode = this._self._root._router.mode

let tag = this.tag || 'a'

let to = mode === 'hash' ? '#' + this.to : this.to

console.log('render', this.to)

return (



{this.$slots.default}



)

// return h(tag, { attrs: { href: to }, on: { click: this.handleClick } }, this.$slots.default)

},
})

Vue.component('router-view', {

render(h) {

let current = this._self._root._router.history.current // current已经是动态响应

let routesMap = this._self._root._router.routesMap

return h(routesMap[current]) // 动态渲染对应组件

},
})
}至此,一个简易的vue-router就实现完了,案例完整代码附上:
let Vue = null

class HistoryRoute {
constructor() {

this.current = null
}
}

class VueRouter {
constructor(options) {

this.mode = options.mode || 'hash'

this.routes = options.routes || []

this.routesMap = this.createMap(this.routes)

this.history = new HistoryRoute() // 当前路由

// 初始化路由函数

this.initRoute()
}

createMap(routes) {

return routes.reduce((pre, current) => {

pre[current.path] = current.component

return pre

}, {})
}

initRoute() {

if (this.mode === 'hash') {

// 先判断用户打开时有没有hash值,没有的话跳转到 #/

location.hash ? '' : (location.hash = '/')

window.addEventListener('load', () => {

this.history.current = location.hash.slice(1)

})

window.addEventListener('hashchange', () => {

this.history.current = location.hash.slice(1)

})

} else {

// history模式

location.pathname ? '' : (location.pathname = '/')

window.addEventListener('load', () => {

this.history.current = location.pathname

})

window.addEventListener('popstate', () => {

this.history.current = location.pathname

})

}
}
}

VueRouter.install = function(_Vue) {
Vue = _Vue

Vue.mixin({

beforeCreate() {

// 根组件

if (this.$options && this.$options.router) {

this._root = this // 把当前vue实例保存到_root上

this._router = this.$options.router // 把router的实例挂载在_router上

Vue.util.defineReactive(this, '_route', this._router.history) // 监听history路径变化

} else if (this.$parent && this.$parent._root) {

// 子组件的话就去继承父组件的实例,让所有组件共享一个router实例

this._root = this.$parent && this.$parent._root

}

// 当访问this.$router时即返回router实例

Object.defineProperty(this, '$router', {

get() {

return this._root._router

},

})

// 当访问this.$route时即返回当前页面路由信息

Object.defineProperty(this, '$route', {

get() {

return this._root._router.history.current

},

})

},
})

Vue.component('router-link', {

props: {

to: {

type: [String, Object],

required: true,

},

tag: {

type: String,

default: 'a',

},

},

methods: {

handleClick(event) {

// 阻止a标签默认跳转

event && event.preventDefault && event.preventDefault() // 阻止a标签默认跳转

let mode = this._self._root._router.mode

let path = this.to

this._self._root._router.history.current = path

if (mode === 'hash') {

window.history.pushState(null, '', '#/' + path.slice(1))

} else {

window.history.pushState(null, '', path.slice(0))

}

},

},

render(h) {

let mode = this._self._root._router.mode

let tag = this.tag || 'a'

let to = mode === 'hash' ? '#' + this.to : this.to

return (



{this.$slots.default}



)

// return h(tag, { attrs: { href: to }, on: { click: this.handleClick } }, this.$slots.default)

},
})

Vue.component('router-view', {

render(h) {

let current = this._self._root._router.history.current // current已经是动态

let routesMap = this._self._root._router.routesMap

return h(routesMap[current]) // 动态渲染对应组件

},
})
}

export default VueRouter
let Vue = null

class HistoryRoute {
constructor() {

this.current = null
}
}

class VueRouter {
constructor(options) {

this.mode = options.mode || 'hash'

this.routes = options.routes || []

this.routesMap = this.createMap(this.routes)

this.history = new HistoryRoute() // 当前路由

// 初始化路由函数

this.initRoute()
}

createMap(routes) {

return routes.reduce((pre, current) => {

pre[current.path] = current.component

return pre

}, {})
}

initRoute() {

if (this.mode === 'hash') {

// 先判断用户打开时有没有hash值,没有的话跳转到 #/

location.hash ? '' : (location.hash = '/')

window.addEventListener('load', () => {

this.history.current = location.hash.slice(1)

})

window.addEventListener('hashchange', () => {

this.history.current = location.hash.slice(1)

})

} else {

// history模式

location.pathname ? '' : (location.pathname = '/')

window.addEventListener('load', () => {

this.history.current = location.pathname

})

window.addEventListener('popstate', () => {

this.history.current = location.pathname

})

}
}
}

VueRouter.install = function(_Vue) {
Vue = _Vue

Vue.mixin({

beforeCreate() {

// 根组件

if (this.$options && this.$options.router) {

this._root = this // 把当前vue实例保存到_root上

this._router = this.$options.router // 把router的实例挂载在_router上

Vue.util.defineReactive(this, '_route', this._router.history) // 监听history路径变化

} else if (this.$parent && this.$parent._root) {

// 子组件的话就去继承父组件的实例,让所有组件共享一个router实例

this._root = this.$parent && this.$parent._root

}

// 当访问this.$router时即返回router实例

Object.defineProperty(this, '$router', {

get() {

return this._root._router

},

})

// 当访问this.$route时即返回当前页面路由信息

Object.defineProperty(this, '$route', {

get() {

return this._root._router.history.current

},

})

},
})

Vue.component('router-link', {

props: {

to: {

type: [String, Object],

required: true,

},

tag: {

type: String,

default: 'a',

},

},

methods: {

handleClick(event) {

// 阻止a标签默认跳转

event && event.preventDefault && event.preventDefault() // 阻止a标签默认跳转

let mode = this._self._root._router.mode

let path = this.to

this._self._root._router.history.current = path

if (mode === 'hash') {

window.history.pushState(null, '', '#/' + path.slice(1))

} else {

window.history.pushState(null, '', path.slice(0))

}

},

},

render(h) {

let mode = this._self._root._router.mode

let tag = this.tag || 'a'

let to = mode === 'hash' ? '#' + this.to : this.to

return (



{this.$slots.default}



)

// return h(tag, { attrs: { href: to }, on: { click: this.handleClick } }, this.$slots.default)

},
})

Vue.component('router-view', {

render(h) {

let current = this._self._root._router.history.current // current已经是动态

let routesMap = this._self._root._router.routesMap

return h(routesMap[current]) // 动态渲染对应组件

},
})
}

export default VueRouterps: 个人技术博文 Github 仓库,觉得不错的话欢迎 star,给我一点鼓励继续写作吧~个人技术博文 Github 仓库以上就是如何手写简易的 Vue Router的详细内容,关于手写简易的 Vue Router的资料请关注其它相关文章!