嵌套子路由
作用:一个页面可以通过嵌套子路由展示不同的组件的内容
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<!-- 添加子路由导航 -->
<p>导航 :
<router-link to="/home">首页</router-link> |
<router-link to="/home/one">-子页面1</router-link> |
<router-link to="/home/two">-子页面2</router-link>
</p>
<!-- 子页面展示部分 -->
<router-view/>
</div>
</template>
<script>
export default {
name: 'Home',
data () {
return {
msg: 'Home Page!'
}
}
}
</script>
<style scoped>
</style>
one.vue/two.vue
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: "One",
data() {
return {
msg: "Welcome to One/Two!"
};
}
};
</script>
<style scoped>
</style>
index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
import One from '@/components/One'
import Two from '@/components/Two'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/', // 默认页面重定向到主页
redirect: '/home'
},
{
path: '/home', // 主页路由
name: 'Home',
component: Home,
children:[ // 嵌套子路由
{
path:'one', // 子页面1
component:One
},
{
path:'two', // 子页面2
component:Two
},
]
}
]
})
这样home页就能点击显示one或者two组件内容了
routers中name属性作用
用法一:
通过name属性,为一个页面中不同的router-view渲染不同的组件,可以跳转路由
<template>
<div id="app">
<router-view></router-view>
<router-view name="Foo"></router-view> //将渲染Foo组件
<router-view name="Bar"></router-view> //将渲染Bar组件
</div>
</template>
用法二
用name传参,使用$route.name获取组件name值
<template>
<div id="app">
<p>{{ $route.name }}</p> //可以获取到渲染进来的组件的name值
<router-view></router-view>
</div>
</template>
用法三(路由传参)
var router = new VueRouter({
routes: [
{ name:'register', path: '/register/:id/:name', component: register }
]
})
<router-link :to="{name:'register',params:{id:10,name:'小明'}}">注册</router-link>
Vue router的实现
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes: [...]
})
new Vue({
router
...
})
Vue 通过 use 方法,加载VueRouter中的 install 方法。install 完成 Vue 实例对 VueRouter 的挂载过程
export function install (Vue) {
// ...
// 混入 beforeCreate 钩子
Vue.mixin({
beforeCreate () {
// 在option上面存在router则代表是根组件
if (isDef(this.$options.router)) {
this._routerRoot = this
this._router = this.$options.router
// 执行_router实例的 init 方法
this._router.init(this)
// 为 vue 实例定义数据劫持
Vue.util.defineReactive(this, '_route', this._router.history.current)
} else {
// 非根组件则直接从父组件中获取
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
registerInstance(this, this)
},
destroyed () {
registerInstance(this)
}
})
// 设置代理,当访问 this.$router 的时候,代理到 this._routerRoot._router
Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
// 设置代理,当访问 this.$route 的时候,代理到 this._routerRoot._route
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
// 注册 router-view 和 router-link 组件
Vue.component('RouterView', View)
Vue.component('RouterLink', Link)
// Vue钩子合并策略
const strats = Vue.config.optionMergeStrategies
// use the same hook merging strategy for route hooks
strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
// ...
}
在构造Vue实例的时候,我们会传入router对象:
new Vue({
router
})
此时的router会被挂载到 Vue 的跟组件this.options,则对_routerRoot 和 _router进行赋值操作,之后执行 _router.init() 方法。 然后通过 registerInstance(this, this)这个方法来实现对router-view的挂载操作:
// 执行 vm.$options._parentVnode.data.registerRouteInstance 渲染 router-view 组件
const registerInstance = (vm, callVal) => {
let i = vm.$options._parentVnode
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
i(vm, callVal)
}
}
因为只有 router-view 组件定义了data.registerRouteInstance函数。data.registerRouteInstance 主要用来执行 render 的操作,创建 router-view 组件的 Vnode :
data.registerRouteInstance = (vm, val) => {
// ...
return h(component, data, children)
}
vue-router是通过vue.use()方法被挂载到vue的实例中,为了构造出router对象,我们还需要对VueRouter进行实例化操作,像这样:
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', name: 'home', component: Home },
{ path: '/foo', name: 'foo', component: Foo },
{ path: '/bar/:id', name: 'bar', component: Bar }
]
})
constructor
VueRouter内部源码定义
export default class VueRouter {
// ...
constructor (options: RouterOptions = {}) {
this.app = null
this.apps = []
this.options = options
this.beforeHooks = []
this.resolveHooks = []
this.afterHooks = []
this.matcher = createMatcher(options.routes || [], this)
let mode = options.mode || 'hash'
this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract'
}
this.mode = mode
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
}
match (
raw: RawLocation,
current?: Route,
redirectedFrom?: Location
): Route {
return this.matcher.match(raw, current, redirectedFrom)
}
get currentRoute (): ?Route {
return this.history && this.history.current
}
init () {}
beforeEach () {}
beforeResolve () {}
afterEach () {}
onReady () {}
onError () {}
push () {}
replace () { }
go () {}
back () { }
forward () { }
getMatchedComponents () { }
resolve ( ) { }
addRoutes () { }
}
constructor实例化的时候:通过new VueRouter({...})创建了一个VueRouter的实例,VueRouter通过参数mode来指定路由模式:
- 首先根据mode来确定所选的模式,如果当前情况下不支持history模式,会强制切换到hash模式
- 如果当前环境部署浏览器环境,会切换到abstract模式,之后通过不同模式生成不同的history对象
init
在安装的时候,会执行该对象的init()方法
init (app: any /* Vue component instance */) {
// ...
this.apps.push(app)
// main app already initialized.
if (this.app) {
return
}
this.app = app
const history = this.history
if (history instanceof HTML5History) {
history.transitionTo(history.getCurrentLocation())
} else if (history instanceof HashHistory) {
const setupHashListener = () => {
history.setupListeners()
}
history.transitionTo(
history.getCurrentLocation(),
setupHashListener,
setupHashListener
)
}
history.listen(route => {
this.apps.forEach((app) => {
app._route = route
})
})
}
在安装的beforeCreate钩子中,通过调用实例的init()方法
this._router.init(this)
init()方法中的app变量存储当前vue实例的this,然后将app存入数组apps中,通过判断this.app()判断是否被实例化,然后通过history来确定不同路由的切换history.transitionTo(),之后通过history.listen()来注册路由变化的响应回调。