vue-router使用
import VueRouter from 'vue-router'
// 1、引入
Vue.use(VueRouter) // 插件install
const routes = [
{
path: '/',
name: 'Home',
component: Home
}
]
// 2、new对象
const router = new VueRouter({
routes
})
// 3、放到opstions里面
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
实现一个简版
my-router.js
类vue-router
1、构造函数,保存options、设置初始值,作响应式,加监听
let Vue
class VueRouter {
// 构造器
constructor(options) {
// 保存options
this.$options = options
// 设初值
const initVal = window.location.hash.slice(1) || '/'
this.current = initVal
console.log('this.current', this.current)
// 作响应式
Vue.util.defineReactive(this, 'matched', [])
// this.current = window.location.hash.slice(1) || '/'
// 加监听
this.hashChange = this.hashChange.bind(this)
window.addEventListener('hashchange', this.hashChange)
}
// 监听的值变化实际操作
hashChange() {
this.current = window.location.hash.slice(1)
this.matched = []
this.match() // 重新匹配
}
match(routes) {
// 一般直接取options
routes = routes || this.$options.routes
// match的会生成一个数组
// 比如 /about/info
// match会生成 ['/about', '/about/info'] // 实际存的是对象
for (const route of routes) {
if (route.path === '/' && this.current === '/') {
this.matched.push(route)
return
}
if (route.path !== '/' && this.current.indexOf(route.path) > -1) {
this.matched.push(route)
}
if (route.children && route.children.length > 0) {
route.children.forEach((ele) => {
if (ele.path !== '/' && this.current.indexOf(ele.path) > -1) {
this.matched.push(ele)
}
})
}
}
}
}
2、install 方法实现,直接挂在静态下
VueRouter.install = function(_Vue) {
Vue = _Vue
// 利用全局混入,延迟执行下面的代码,这样可以获取router实例
Vue.mixin({
beforeCreate() {
// 组件实例
if (this.$options.router) {
Vue.prototype.$router = this.$options.router
}
},
beforeDesotry() {
}
})
// 注册组件 router-link、router-view实现
// ....
}
export default VueRouter;
router-link 实现
Vue.component('router-link', {
props: {
to: {
type: String,
required: true
}
},
render(h) {
return h(
'a',
{
attrs: {
href: '#' + this.to
}
},
this.$slots.default
)
}
})
router-view 实现
Vue.component('router-view', {
render(h) {
this.$vnode.data.routerView = true
let depth = 0
let parent = this.$parent
while (parent) {
const vnodeData = parent.$vnode &&
parent.$vnode.data
if (vnodeData && vnodeData.routerView) {
// 说明当前parent时routerview
depth++
}
parent = parent.$parent
}
console.log('执行-routerview渲染--')
console.log('this.$router.current' + '比较')
let component = null
// 获取path对应的component
const route = this.$router.matched[depth]
console.log(depth)
console.log(this.$router.matched)
if (route) {
component = route.component
} else {
component = this.$router.$options.notFund.component
}
// console.log(component)
return h(component)
}
})
3、vue-router源码里是如何生成matched的
在util/route下存在以下方法
function formatMatch (record: ?RouteRecord): Array<RouteRecord> {
const res = []
while (record) {
res.unshift(record)
record = record.parent
}
return res
}
4、源码vue-router问题解析
问题1:每个组件都是怎么拿到router的?
Vue.mixin 在beforeCreate时给根vue挂了个_router
Vue.util.defineReactive(this, '_route', this._router.history.current)
子组件在constructor的时候会走这一步:
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
从 this._routerRoot 获取
Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
问题2:如何做响应式的?(即push后页面会进行替换)
源码位置:src\index.js
根vue实例里作了响应式
Vue.util.defineReactive(this, '_route', this._router.history.current)
vueRoutr对象内有个apps数组,存的是根实例。
(为什么是数组,大概是处理多页面应用?)
init时会添加个监听函数,当history的current变化时会同步修改 _route
history.listen(function (route) {
this.apps.forEach(function (app) {
app._route = route;
});
});