vue学习07——router动态路由02
嵌套路由
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL中各段动态路径也按某种结构对应嵌套的各层组件
/user/foo/profile /user/foo/posts +------------------+ +-----------------+ | User | | User | | +--------------+ | | +-------------+ | | | Profile | | +------------> | | Posts | | | | | | | | | | | +--------------+ | | +-------------+ | +------------------+ +-----------------+
借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种关系
<div id="app">
<p>
<router-link to="/" exact>index</router-link>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<router-view></router-view>
</div>
const Home = { template: '<div>home</div>' }
const Foo = { template: `
<div>
<p>
<router-link to="/foo/foo1">to Foo1</router-link>
<router-link to="/foo/foo2">to Foo2</router-link>
<router-link to="/foo/foo3">to Foo3</router-link>
</p>
<router-view></router-view>
</div>
` }
const Bar = { template: '<div>bar</div>' }
const Foo1 = { template: '<div>Foo1</div>' }
const Foo2 = { template: '<div>Foo2</div>' }
const Foo3 = { template: '<div>Foo3</div>' }
const routes = [
{ path: '/', component: Home },
{ path: '/foo', component: Foo ,children:[
{path:'foo1',component:Foo1},
{path:'foo2',component:Foo2},
{path:'foo3',component:Foo3},
]},
{ path: '/bar', component: Bar },
]
要特别注意的是,router的构造配置中,children属性里的path属性只设置为当前路径,因为其会依据层级关系;而在router-link的to属性则需要设置为完全路径
如果要设置默认子路由,即点击foo时,自动触发foo1,则需要进行如下修改。将router配置对象中children属性的path属性设置为'',并将对应的router-link的to属性设置为'/foo'
const Foo = { template: `
<div>
<p>
<router-link to="/foo" exact>to Foo1</router-link>
<router-link to="/foo/foo2">to Foo2</router-link>
<router-link to="/foo/foo3">to Foo3</router-link>
</p>
<router-view></router-view>
</div>
` }
const routes = [
{ path: '/', component: Home },
{ path: '/foo', component: Foo ,children:[
{path:'',component:Foo1},
{path:'foo2',component:Foo2},
{path:'foo3',component:Foo3},
]},
{ path: '/bar', component: Bar },
]
结果如下所示
命名路由
有时,通过一个名称来标识一个路由显得更方便,特别是在链接一个路由,或者是执行一些跳转时。可以在创建Router实例时,在routes配置中给某个路由设置名称
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
要链接到一个命名路由,可以给 router-link 的 to 属性传一个对象:
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
这跟代码调用 router.push() 是一回事
router.push({ name: 'user', params: { userId: 123 }})
这两种方式都会把路由导航到 /user/123 路径
命名路由的常见用途是替换router-link中的to属性,如果不使用命名路由,由router-link中的to属性需要设置全路径,不够灵活,且修改时较麻烦。使用命名路由,只需要使用包含name属性的对象即可
[注意]如果设置了默认子路由,则不要在父级路由上设置name属性
<div id="app">
<p>
<router-link to="/" exact>index</router-link>
<router-link :to="{ name: 'foo1' }">Go to Foo</router-link>
<router-link :to="{ name: 'bar' }">Go to Bar</router-link>
</p>
<router-view></router-view>
</div>
const Home = { template: '<div>home</div>' }
const Foo = { template: `
<div>
<p>
<router-link :to="{ name: 'foo1' }" exact>to Foo1</router-link>
<router-link :to="{ name: 'foo2' }" >to Foo2</router-link>
<router-link :to="{ name: 'foo3' }" >to Foo3</router-link>
</p>
<router-view></router-view>
</div>
` }
const Bar = { template: '<div>bar</div>' }
const Foo1 = { template: '<div>Foo1</div>' }
const Foo2 = { template: '<div>Foo2</div>' }
const Foo3 = { template: '<div>Foo3</div>' }
const routes = [
{ path: '/', name:'home', component: Home },
{ path: '/foo', component: Foo ,children:[
{path:'',name:'foo1', component:Foo1},
{path:'foo2',name:'foo2', component:Foo2},
{path:'foo3',name:'foo3', component:Foo3},
]},
{ path: '/bar', name:'bar', component: Bar },
]
结果如下所示
命名视图
有时候想同时(同级)展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar(侧导航) 和 main(主内容) 两个视图,这个时候命名视图就派上用场了。可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default
<router-view class="view one"></router-view> <router-view class="view two" name="a"></router-view> <router-view class="view three" name="b"></router-view>
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用components配置
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
下面是一个实例
<div id="app">
<p>
<router-link to="/" exact>index</router-link>
<router-link :to="{ name: 'foo' }">Go to Foo</router-link>
<router-link :to="{ name: 'bar' }">Go to Bar</router-link>
</p>
<router-view></router-view>
<router-view name="side"></router-view>
</div>
const Home = { template: '<div>home</div>' }
const Foo = { template: '<div>Foo</div>'}
const MainBar = { template: '<div>mainBar</div>' }
const SideBar = { template: '<div>sideBar</div>' }
const routes = [
{ path: '/', name:'home', component: Home },
{ path: '/foo', name:'foo', component: Foo},
{ path: '/bar', name:'bar', components: {
default: MainBar,
side:SideBar
} },
]
结果如下所示
动态路径
经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。那么,可以在 vue-router 的路由路径中使用动态路径参数(dynamic segment)来达到这个效果
const User = {
template: '<div>User</div>'
}
const router = new VueRouter({
routes: [
// 动态路径参数以冒号开头
{ path: '/user/:id', component: User }
]
})
现在,像 /user/foo 和 /user/bar 都将映射到相同的路由
下面是一个比较完整的实例,path:'/user/:id?'表示有没有子路径都可以匹配
<div id="app">
<router-view></router-view>
<br>
<p>
<router-link to="/" exact>index</router-link>
<router-link :to="{name:'user'}">User</router-link>
<router-link :to="{name:'bar'}">Go to Bar</router-link>
</p>
</div>
const home = { template: '<div>home</div>'};
const bar = { template: '<div>bar</div>'};
const user = {template: `<div>
<p>user</p>
<router-link style="margin: 0 10px" :to="'/user/' + item.id" v-for="item in userList" key="item.id">{{item.userName}}</router-link>
</div>`,
data(){
return{userList:[{id:1,userName:'u1'},{id:2,userName:'u2'},{id:3,userName:'u3'}]}
}
};
const app = new Vue({
el:'#app',
router:new VueRouter({
routes: [
{ path: '/', name:'home', component:home },
{ path: '/user/:id?', name:'user', component:user},
{ path: '/bar', name:'bar', component:bar},
],
}),
})
一个路径参数使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用。于是,可以更新 User 的模板,输出当前用户的 ID:
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
下面是一个实例
<div id="app">
<p>
<router-link to="/user/foo">/user/foo</router-link>
<router-link to="/user/bar">/user/bar</router-link>
</p>
<router-view></router-view>
</div>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
<script>
const User = {
template: `<div>User {{ $route.params.id }}</div>`
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
const app = new Vue({ router }).$mount('#app')
</script>
可以在一个路由中设置多段『路径参数』,对应的值都会设置到 $route.params 中。例如:
模式 匹配路径 $route.params
/user/:username /user/evan { username: 'evan' }
/user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: 123 }
除了 $route.params 外,$route 对象还提供了其它有用的信息,例如,$route.query(如果 URL 中有查询参数)、$route.hash 等等
【响应路由参数的变化】
使用路由参数时,例如从 /user/foo 导航到 user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用
复用组件时,想对路由参数的变化作出响应的话,可以简单地 watch(监测变化) $route 对象:
const User = {
template: '...',
watch: {
'$route' (to, from) {
// 对路由变化作出响应...
}
}
}
[注意]有时同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高
下面是一个实例
const home = { template: '<div>home</div>'};
const bar = { template: '<div>bar</div>'};
const user =
{template: `<div>
<p>user</p>
<router-link style="margin: 0 10px" :to="'/user/' +item.type + '/'+ item.id" v-for="item in userList" key="item.id">{{item.userName}}</router-link>
<div v-if="$route.params.id">
<div>id:{{userInfo.id}};userName:{{userInfo.userName}} ;type:{{userInfo.type}};</div>
</div>
</div>`,
data(){
return{
userList:[{id:1,type:'vip',userName:'u1'},{id:2,type:'common',userName:'u2'},{id:3,type:'vip',userName:'u3'}],
userInfo:null,
}
},
methods:{
getData(){
let id = this.$route.params.id;
if(id){
this.userInfo = this.userList.filter((item)=>{
return item.id == id;
})[0]
}else{
this.userInfo = {};
}
}
},
created(){
this.getData();
},
watch:{
$route(){
this.getData();
},
}
};
const app = new Vue({
el:'#app',
router:new VueRouter({
routes: [
{ path: '/', name:'home', component:home },
{ path: '/user/:type?/:id?', name:'user', component:user},
{ path: '/bar', name:'bar', component:bar},
],
}),
})
查询字符串
实现子路由,除了使用动态参数,也可以使用查询字符串
const home = { template: '<div>home</div>'};
const bar = { template: '<div>bar</div>'};
const user =
{template: `<div>
<p>user</p>
<router-link style="margin: 0 10px" :to="'/user/' +item.type + '/'+ item.id" v-for="item in userList" key="item.id">{{item.userName}}</router-link>
<div v-if="$route.params.id">
<div>id:{{userInfo.id}};userName:{{userInfo.userName}} ;type:{{userInfo.type}};</div>
<router-link to="?info=follow" exact>关注</router-link>
<router-link to="?info=share" exact>分享</router-link>
</div>
</div>`,
data(){
return{
userList:[{id:1,type:'vip',userName:'u1'},{id:2,type:'common',userName:'u2'},{id:3,type:'vip',userName:'u3'}],
userInfo:null,
}
},
methods:{
getData(){
let id = this.$route.params.id;
if(id){
this.userInfo = this.userList.filter((item)=>{
return item.id == id;
})[0]
}else{
this.userInfo = {};
}
}
},
created(){
this.getData();
},
watch:{
$route(){
this.getData();
},
}
};
const app = new Vue({
el:'#app',
router:new VueRouter({
routes: [
{ path: '/', name:'home', component:home },
{ path: '/user/:type?/:id?', name:'user', component:user},
{ path: '/bar', name:'bar', component:bar},
],
}),
})
当需要设置默认查询字符串时,进行如下设置
const user =
{template: `<div>
<p>user</p>
<router-link style="margin: 0 10px" :to="{path:'/user/' +item.type + '/'+ item.id,query:{info:'follow'}}" v-for="item in userList" key="item.id">{{item.userName}}</router-link>
<div v-if="$route.params.id">
<div>id:{{userInfo.id}};userName:{{userInfo.userName}} ;type:{{userInfo.type}};</div>
<router-link to="?info=follow" exact>关注</router-link>
<router-link to="?info=share" exact>分享</router-link>
{{$route.query}}
</div>
</div>`,
data(){
return{
userList:[{id:1,type:'vip',userName:'u1'},{id:2,type:'common',userName:'u2'},{id:3,type:'vip',userName:'u3'}],
userInfo:null,
}
},
methods:{
getData(){
let id = this.$route.params.id;
if(id){
this.userInfo = this.userList.filter((item)=>{
return item.id == id;
})[0]
}else{
this.userInfo = {};
}
}
},
created(){
this.getData();
},
watch:{
$route(){
this.getData();
},
}
};
滚动行为
使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它可以自定义路由切换时页面如何滚动
[注意]这个功能只在 HTML5 history 模式下可用
当创建一个 Router 实例,可以提供一个 scrollBehavior 方法。该方法在前进、后退或切换导航时触发
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
// return 期望滚动到哪个的位置
}
})
scrollBehavior 方法返回 to 和 from 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用,返回滚动条的坐标{x:number,y:number}
如果返回一个布尔假的值,或者是一个空对象,那么不会发生滚动
scrollBehavior (to, from, savedPosition) {
return { x: 0, y: 0 }
}
对于所有路由导航,简单地让页面滚动到顶部。返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
}
下面是一个实例,点击导航进行切换时,滚动到页面顶部;通过前进、后退按钮进行切换时,保持坐标位置
const router = new VueRouter({
mode:'history',
routes ,
scrollBehavior (to, from, savedPosition){
if(savedPosition){
return savedPosition;
}else{
return {x:0,y:0}
}
}
})

还可以模拟『滚动到锚点』的行为:
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
}
}
}
下面是一个实例
<div id="app">
<router-view></router-view>
<br>
<p>
<router-link to="/" exact>index</router-link>
<router-link :to="{name:'foo' ,hash:'#abc'}">Go to Foo</router-link>
<router-link :to="{ name: 'bar' }">Go to Bar</router-link>
</p>
</div>
const ruter = new VueRouter({
mode:'history',
routes ,
scrollBehavior (to, from, savedPosition){
if(to.hash){
return {
selector: to.hash
}
}
if(savedPosition){
return savedPosition;
}else{
return {x:0,y:0}
}
}
})
浙公网安备 33010602011771号