Vue全家桶系列之Vue-router(四)

1.前言

上篇文章说了vue-router的路由的重定向,路由嵌套,命名路由以及路由的别名的使用,接下来再看看其他的用法

2.路由命名视图

有时候想同时 (同级) 展示多个视图,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了,如果 router-view 没有设置名字,那么默认为 default。还是用之前的例子,在home路由上添加一个sidebar视图,先修改下路由配置!

import Vue from 'vue'
import Router from 'vue-router'
import About from '@/components/About'
import Help from '@/components/Help'
import Home from '@/components/Home'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
import Sidebar from '@/components/Sidebar'

Vue.use(Router)

export default new Router({
  linkActiveClass: "active",
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      components: {
        default: Home,
        sidebar: Sidebar //添加一个sidebar视图
      }
    },
    {
      path: '/about',
      component: About,
      children: [
        {
          path: '',
          name: 'work',
          component: Work
        },
        {
          path: 'company',
          name: 'company',
          component: Company
        },
        {
          path: 'contactUs',
          name: 'contactUs',
          component: ContactUs
        }
      ]
    },
    {
      path: '/help',
      name: 'help',
      alias: "/123",
      component: Help
    }
  ]
})

然后再对应的html中添加一个< router-view >,并且设置name为sidebar与组件Sidebar相对应,修改App.vue如下!

<template>
  <div class="router-class">
    <div>
      <router-link to="/" exact>home页面</router-link>
      <router-link to="/about" active-class="about-router">about页面</router-link>
      <router-link to="/help" active-class="help-router">help页面</router-link>
    </div>
    <router-view name="sidebar" class="sidebar"></router-view>
    <router-view class="default"></router-view>
  </div>
</template>

<script>
export default {
  name: "App"
}
</script>

<style>
.active {
  background-color: yellow;
}
.about-router {
  background-color: pink;
}
.help-router {
  background-color: skyblue;
}
.sidebar {
  width: 250px;
  height: 2000px;
  float: left;
  border: 1px solid #aaa;
}
.default {
   height: 2000px;
  border: 1px solid #aaa;
}
</style>

可以看到home里面有2个同级的组件,并且2个组件分别对应自己的一个视图!
在这里插入图片描述

3.路由滚动行为

3.1 scrollBehavior函数

什么意思呢?当切换到新路由,你想保持之前的滚动位置,这个时候就会用到路由的滚动,它可以自定义路由切换时页面如何滚动!先来看下这个路由scrollBehavior滚动函数,我们来打印下这几个参数,看下到底是什么?

import Vue from 'vue'
import Router from 'vue-router'
import About from '@/components/About'
import Help from '@/components/Help'
import Home from '@/components/Home'
import Work from '@/components/Work'
import Company from '@/components/Company'
import ContactUs from '@/components/ContactUs'
import Sidebar from '@/components/Sidebar'

Vue.use(Router)

export default new Router({
  linkActiveClass: "active",
  mode: 'history',
  scrollBehavior (to, from, savedOPosition) {
    //to为当前的路由对象
    //from为上个路由对象
    //savedOPosition 为路由的滚动条的位置
    console.log(to, from, savedOPosition)
  },
  routes: [
    {
      path: '/',
      name: 'home',
      components: {
        default: Home,
        sidebar: Sidebar
      }
    },
    {
      path: '/about',
      component: About,
      children: [
        {
          path: '',
          name: 'work',
          component: Work
        },
        {
          path: 'company',
          name: 'company',
          component: Company
        },
        {
          path: 'contactUs',
          name: 'contactUs',
          component: ContactUs
        }
      ]
    },
    {
      path: '/help',
      name: 'help',
      alias: "/123",
      component: Help
    }
  ]
})

to为当前的路由对象,from为上个路由对象,savedPosition 为路由的滚动条的位置,当我点击about页面时候,先打印name为wrok的路由(work是about的子路由,并且是直接匹配到),然后再打印name为home的路由对象,再点击home页面,输出和之前相反,并且两个点击savedPosition 都是为null(因为点击是不会触发savedPosition ,只能通过浏览器的前进/后退按钮才触发)
在这里插入图片描述

再看下我点击之后,滚动了滚动条({x:0,y:900})再后退,再前进,注意我下面的操作和打印出来的内容!
在这里插入图片描述
说明savedPosition可以记录路由的滚动坐标的位置,点击前进后退时候会记录值,那么我们可以改成这样:

export default new Router({
  linkActiveClass: "active",
  mode: 'history',
  scrollBehavior (to, from, savedPosition) {
  //如果有保存滚动坐标位置就回到这个位置
    if (savedPosition) {
      return savedPosition
    } else {
    //没有就回到顶部
      return { x: 0, y: 0 }
    }
  }
//.......
})

再看来下效果,注意看滚动条位置:
在这里插入图片描述

3.2 hash定位

下面再来看下hash定位法,什么意思呢,可以利用scrollBehavior 方法可以直接定位到锚点(滚动到锚点),首先我们修改App.vue里面的help的path路径,让它指向一个锚点hash!

<template>
  <div class="router-class">
    <div>
      <router-link to="/" exact>home页面</router-link>
      <router-link to="/about" active-class="about-router">about页面</router-link>
      <router-link to="/help#hash" active-class="help-router">help页面</router-link>
    </div>
    <router-view name="sidebar" class="sidebar"></router-view>
    <router-view class="default"></router-view>
  </div>
</template>

然后在help组件中去定义这个hash的位置,我把它定义到1000px位置

<template>
  <div>
    help
    <div id="hash">我是锚点</div>
  </div>
</template>

<script>
export default {
  name: "Help"
}
</script>
<style>
#hash{
  margin: 1000px auto;
}
</style>

最后修改路由的配置

export default new Router({
  linkActiveClass: "active",
  mode: 'history',
  scrollBehavior (to, from, savedPosition) {
    if (to.hash) {
      return {
        selector: to.hash
        // behavior: 'smooth'  平滑滚动,滚动有个缓慢的过程
      }
    }
  }
  //.....

再来看下效果,当激活help路由直接跳转到hash的位置
在这里插入图片描述

3.3 异步滚动(promise)

当然你还可以返回一个 Promise 来得出预期的位置描述

export default new Router({
  linkActiveClass: "active",
  mode: 'history',
  scrollBehavior (to, from, savedPosition) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(
          { x: 0, y: 2000 }
        )
      }, 1000)
    })
  }
//....

当我刷新页面或者后退或者前进(不管之前在什么位置),过一秒之后都会自动跳到(滚动)到 { x: 0, y: 2000 }位置,看下面效果
在这里插入图片描述

4.动态路由匹配

比如有这么一个场景,要匹配用户信息的组件,有张三的信息,有李四的信息,有王二麻子的信息,难道我们要写3个用户信息组件吗?当然不需要,我们可以复用用一个组件(只是数据不一样,功能还是一样的),我们先来模拟下这3个用户的信息(后台请求回来的数据),每组信息都有个唯一的id,我们可以通过id不用来渲染不用的用户信息!

let data = [
  {
    id: "123",
    name: "张三",
    sex: "男",
    age: 18,
    sprot: "running",
    skill: "javascript",
  },
  {
    id: "345",
    name: "李四",
    sex: "男",
    age: 28,
    sprot: "footabll",
    skill: "java",
  },
  {
    id: "456",
    name: "王二麻子",
    sex: "女",
    age: 20,
    sprot: "swimming",
    skill: "node.js",
  },
];

首先我们来添加一个用户user组件, path: '/user/:id?', ?是一个正则的匹配,代表前面的字符最多只可以出现一次(0次、或1次),所以能匹配上path: '/user 或者path: '/user/:id

export default new Router({
  linkActiveClass: "active",
  mode: 'history',
  routes: [
  	//...
    {
      path: '/user/:id?',
      name: 'user',
      component: User
    }
  ]
})

然后再看下user组件,把之前模拟的data数据绑定到userList上,然后循环userList,当id不一样匹配不用的内容

<template>
  <div>
    <p>user</p>
    <div v-for="list in userList" :key="list.id" class="user">
      <router-link  :to="'/user/'+list.id">{{ list.name }}
      </router-link>
    </div>
  </div>
</template>
<script>
let data = [
  {
    id: "123",
    name: "张三",
    sex: "男",
    age: 18,
    sprot: "running",
    skill: "javascript",
  },
  {
    id: "345",
    name: "李四",
    sex: "男",
    age: 28,
    sprot: "footabll",
    skill: "java",
  },
  {
    id: "456",
    name: "王二麻子",
    sex: "女",
    age: 20,
    sprot: "swimming",
    skill: "node.js",
  },
];
export default {
  name: "User",
  data() {
    return {
      userList: data,
    };
  }
};
</script>
<style scoped>
.user {
  display: inline-block;
  margin: 20px;
}
.userInfo {
  margin: 60px;
}
</style>

先来看下效果,当点击不同的用户,路径已经发生了改变
在这里插入图片描述
那么怎么让不同的用户显示不用的信息的,我们先来看下当前路由对象有什么东西!我们再created钩子函数里面打印下当前路由对象

export default {
  name: "User",
  data() {
    return {
      userList: data,
    };
  },
  created() {
    console.log(this.$route);//当前路由对象
  },
};

在这里插入图片描述
路由对象表示当前激活的路由的状态结果,每次成功的导航后都会产生一个新的对象,下面分别解释下路由对象的属性信息。

  1. fullPath字符串:包含查询参数和hash的完整路径
  2. hash字符串:当前路由的hash值
  3. matched数组:包含当前路由的所有嵌套路径片段的路由记录(假如我们目前的路由是为''/about/company'',matched匹配到的会是一个数组,包含 '/about',''/about/company',这两个path的路由信息)
  4. meta对象:路由元信息 (用来判断当前路由是否需要进一步处理)
  5. name字符串:当前路由的名称
  6. params对象:动态路由参数
  7. path字符串:对应当前的路由的路径
  8. query对象:查询参数

下面我们会主要用到params对象和query对象,上图打印出来的params对象为'{id: '123'}', 那么是不是可以通过当前路由对象中的params对象获取到path中的':id',所以一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params上,获取到id后可以找出userList对象的id信息

<template>
  <div>
    <p>user</p>
    <div v-for="list in userList" :key="list.id" class="user">
      <router-link exact :to="{name: 'user',params: { id: list.id }}"
        >{{ list.name }}
      </router-link>
    </div>
    <div v-if="user" class="userInfo">
      <p>姓名:{{ user.name }}</p>
      <p>性别:{{ user.sex }}</p>
      <p>年龄:{{ user.age }}</p>
    </div>
  </div>
</template>
<script>
let data = [
  {
    id: "123",
    name: "张三",
    sex: "男",
    age: 18,
    sprot: "running",
    skill: "javascript",
  },
  {
    id: "345",
    name: "李四",
    sex: "男",
    age: 28,
    sprot: "footabll",
    skill: "java",
  },
  {
    id: "456",
    name: "小丽",
    sex: "女",
    age: 20,
    sprot: "swimming",
    skill: "node.js",
  },
];
export default {
  name: "User",
  data() {
    return {
      userList: data,
      user: {},
    };
  },
  created() {
    this.getUser(this.$route.params.id);
  },
  methods: {
    getUser(id) {
      let item = this.userList.find((use) => {
        return use.id == id;
      });
      this.user = item;
    },
  },
};
</script>
<style scoped>
.user {
  display: inline-block;
  margin: 20px;
}
.userInfo {
  margin: 60px;
}
</style>

执行之后发现当点击别的用户的时候,还是张三的数据,数据没有更新
在这里插入图片描述
因为三个路由都渲染同个组件,所以组件的生命周期钩子created函数不会再被调用,那么我们用watch来监听下

export default {
  name: "User",
  data() {
    return {
      userList: data,
      user: {},
    };
  },
  //监听当前路由的变化
  watch: {
    $route() {
      this.getUser(this.$route.params.id);
    },
  },
  created() {
    this.getUser(this.$route.params.id);
  },
  methods: {
    getUser(id) {
      let item = this.userList.find((us) => {
        return us.id == id;
      });
      console.log(this.user);
      this.user = item;
    },
  },
};

或者使用的 beforeRouteUpdate 导航守卫(后面导航守卫会详细说)它的作用在当前路由改变,但是该组件被复用时,这个钩子函数会被调用

export default {
  name: "User",
  data() {
    return {
      userList: data,
      user: {},
    };
  },
  beforeRouteUpdate(to, from, next) {
    //to:将要进入的目标 路由对象
    //from: 当前导航正要离开的路由
    this.getUser(to.params.id);
    //执行next()进入当前导航
    next();
  },
  created() {
    this.getUser(this.$route.params.id);
  },
  methods: {
    getUser(id) {
      let item = this.userList.find((us) => {
        return us.id == id;
      });
      console.log(this.user);
      this.user = item;
    },
  },
};

用watch或者beforeRouteUpdate 导航守卫都可以得到一样的效果
在这里插入图片描述
如果每个用户还有其他的信息(sprot,skill),我们想通过这两个属性去查询用户的,怎么做?我们只需要从this.$route.query来获取查询对象就可以,原理和params一样,这里就不细说了,看下面代码修改

<template>
  <div>
    <p>user</p>

    <div v-for="list in userList" :key="list.id" class="user">
      <router-link exact :to="{name: 'user',params: { id: list.id }, query: { search: 'sprot' }}">{{ list.name }}
      </router-link>
    </div>
    <div v-if="user" class="userInfo">
      <p>姓名:{{ user.name }}</p>
      <p>性别:{{ user.sex }}</p>
      <p>年龄:{{ user.age }}</p>
    </div>
    <div v-if="user">
      <router-link exact :to="{ path: '', query: { search: 'sprot' } }"
        >sport</router-link
      >
      <router-link exact :to="{ path: '', query: { search: 'skill' } }"
        >skill</router-link
      >
    </div>
    <div class="userInfo">
      <p v-if="$route.query.search == 'sprot'">运动: {{ user.sprot }}</p>
      <p v-if="$route.query.search == 'skill'">技能:{{ user.skill }}</p>
    </div>
  </div>
</template>
<script>
let data = [
  {
    id: "123",
    name: "张三",
    sex: "男",
    age: 18,
    sprot: "running",
    skill: "javascript",
  },
  {
    id: "345",
    name: "李四",
    sex: "男",
    age: 28,
    sprot: "footabll",
    skill: "java",
  },
  {
    id: "456",
    name: "小丽",
    sex: "女",
    age: 20,
    sprot: "swimming",
    skill: "node.js",
  },
];
export default {
  name: "User",
  data() {
    return {
      userList: data,
      user: {},
    };
  },
  beforeRouteUpdate(to, from, next) {
    this.getUser(to.params.id);
    next();
  },
  created() {
    this.getUser(this.$route.params.id);
  },
  methods: {
    getUser(id) {
      let item = this.userList.find((us) => {
        return us.id == id;
      });
      console.log(this.user);
      this.user = item;
    },
  },
};
</script>
<style scoped>
.user {
  display: inline-block;
  margin: 20px;
}
.userInfo {
  margin: 60px;
}
</style>

看下面效果,仔细看上面的路径
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值