vue-router

vue-router

1 介绍

Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能有:

  • 嵌套的路由/视图表
  • 模块化的、基于组件的路由配置
  • 路由参数、查询、通配符
  • 基于 Vue.js 过渡系统的视图过渡效果
  • 细粒度的导航控制
  • 带有自动激活的 CSS class 的链接
  • HTML5 历史模式或 hash 模式,在 IE9 中自动降级
  • 自定义的滚动条行为

安装脚手架

vue init webpack roter-demo

安装路由

npm install vue-router

2 基本使用

用 Vue.js + Vue Router 创建单页应用,是非常简单的。使用 Vue.js ,我们已经可以通过组合组件来组成应用程序,
当你要把 Vue Router 添加进来,我们需要做的是,将组件 (components) 映射到路由 (routes),
然后告诉 Vue Router 在哪里渲染它们

1 将vue-router注册为vue的插件
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter) //vue的全局可能会增加很多api
2 创建路由组件

在src下定义pages ,并配置别名,在pages定义路由组件

alias:{
	 "pages":resolve('src/pages'),
     "router":resolve('src/router'),
     "routes":resolve('src/routes')
}

在pages下创建两个组件名为a.vue b.vue

3 创建路由数组 main.js
import  a from "pages/a"
import  b from "pages/b"
const routes = [
  { path: '/a', component: a }, //当前对象中的配置称为路由配置
  { path: '/b', component: b}
]
4 创建路由器 并注册路由
const router = new VueRouter({  //当前对象中的配置称为路由器配置
  routes // (缩写) 相当于 routes: routes
})
5 注册路由器
new Vue({
    router:router
})
6 编写路由导航 路由占位 app.vue
<router-link to="path"></routrt-link>   // to:往哪个路由进行跳转
<routrt-view></routrt-view>             // 路由组件最终渲染的位置

图标放在打包后的目录下,npm run build

3 重定向

重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

4 嵌套路由

将路由路由器分开写 ,src下新建两个文件夹:router routes

router.js

import Vue from "vue";
import VueRouter from "vue-router";
Vue.use(VueRouter)

import routes from "routes/routes"
export default new VueRouter({  //当前对象中的配置称为路由器配置
  routes
})

routes.js

import a from "pages/a"
import bfrom "pages/b"
// 一级路由的占位编写在app组件中
// 二级路由的占位编写在其父路由组件中
export default [
  {path:"/about",component:about},
  {path:"/home",component:home},
  {
    path:"/home",
    component:home,
    children:[
      {path:"news",component:news},
      {path:"message",component:message},
      {path:"",redirect:"news"}  //后面跟path
    ]
  },
]

要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。

/user/foo/profile                     /user/foo/posts
+------------------+                  +-----------------+
| User             |                  | User            |
| +--------------+ |                  | +-------------+ |
| | Profile      | |  +------------>  | | Posts       | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+

5 动态路由匹配

我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 `User` 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。(公共内容)
    /user/1
    /user/2
    /user/3    都要匹配到user组件
那么,我们可以在 `vue-router` 的路由路径中使用“动态路径参数”(dynamic segment) 来达到这个效果:
const User = {
  template: '<div>User</div>'
}

const router = new VueRouter({
  routes: [
    // 动态路径参数 以冒号开头 后面的是一个占位。写什么都可以
    { path: '/user/:id', component: User }
  ]
})

现在呢,像 /user/foo/user/bar 都将映射到相同的user路由。

注意点

1 分清楚动态路由什么时候该和嵌套路由结合使用(动态路由之间有公共部分的时候)

2 单独使用动态路由的时候,怎么保证不带占位符也能访问到对应的组件(:占位)

面试题:vue-roter中path中使用,?是干嘛的

动态路由在使用时 如果/user和/user/:id要命中同一个组件的;在配置路由时要使用?來进行修饰
通过注入路由器,我们可以在任何组件内通过 this.$route 访问当前路由
通过注入路由器,我们可以在任何组件内通过this.$route访问当前路由

路由对象

url: http://127.0.0.1:8080/a/b/c?name=damu&wife=dy#123

url: 协议://主机:端口/path?query#hash; (path中可能会包含params)

  • $route.fullpath

完整路径:path?query#hash

  • $route.path

    • 类型: string

      字符串,对应当前路由的路径,总是解析为绝对路径,如 "/foo/bar"

  • $route.params(:占位)

    • 类型: Object

      一个 key/value 对象,包含了匹配片段,如果没有路由参数,就是一个空对象。

      当拿到用户的id后,可以把id作为请求参数传递给后台接口,接口会返回当前用户的信息

  • $route.query

    • 类型: Object

      一个 key/value 对象,表示 URL 查询参数。例如,对于路径 /foo?user=1,则有 $route.query.user == 1,如果没有查询参数,则是个空对象。

  • $route.hash

    • 类型: string

      当前路由的 hash 值 (带 #) ,如果没有 hash 值,则为空字符串。

路径参数

一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到
this.$route.params,可以在每个组件内使用。于是,我们可以更新 User 的模板,输出当前用户的 ID:

const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}

你可以在一个路由中设置多段“路径参数”,对应的值都会设置到 $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

组件复用

动态路由组件的生命周期只会在第一次被访问时执行

当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。
因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象
    watch:{
      $route:{
        handler(route){    //存在类型准换     
     this.user = users.filter(user=> +$route.params.id === +route.params.id)[0]
        },
        deep:true,   //深度监听
        immediate:true //初始化时执行一次handler
      }
    }
    watch:{
      id:{
        handler(id){
          this.user = users.filter(user=> +user.id === +id)[0]
        },
        deep:true,   //深度监听
        immediate:true //初始化时执行一次handler

6 路由组件传参

在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。

使用 props 将组件和路由解耦:

取代与 $route 的耦合

const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User }
  ]
})

通过 props 解耦

const User = {
  props: ['id'],
  template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },
  ]
})

布尔模式

如果 props 被设置为 trueroute.params 将会被设置为组件属性。类似于父组件通过props进行数据传递,只能自动传递params数据

对象模式

如果 props 是一个对象,它会被按原样设置为组件属性。灵活度不高

{path:":id",component: userDetail,props: {id:0,name:"aa",msg:"xxxxxx"}}

函数模式

你可以创建一个函数返回 props。函数的参数是当前route对象 ,灵活度高,数据全。

{path:":id",component: userDetail,props:route=>{return {id:route.params.id,test:route.query.test}}}   

7 编程式的导航

注意点

通过注入路由器,我们可以在任何组件内通过 this.$router 访问当前路由器

除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,
通过编写代码来实现。

router.push

注意:在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)

声明式编程式
<router-link :to="...">router.push(去的地址)

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 带查询参数,变成 /register?plan=private
this.$router.replace({
        // path:`/user`,
        name:"userDetail",
        params:{
          id:this.id
        },
        query:{
          a:"a",
          b:"b"
        },
        hash:"#abc"
      })
    }

注意:使用编程时路由,push方法的参数是一个对象,path和params不能同时出现

解决:使用命名路由 需要配置路由name

面试题:在什么时候使用命名式路由

答:在使用编程式导航时,编程式导航的API时用对象做参数,而且还带上params参数。

router.replace

router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

声明式编程式
<router-link :to="..." replace> 开销大,组件渲染router.replace(...)

router.go(n)

这个方法的参数是一个整数,意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n)

例子

// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)

// 后退一步记录,等同于 history.back()
router.go(-1)

// 前进 3 步记录
router.go(3)

// 如果 history 记录不够用,那就默默地失败呗
router.go(-100)
router.go(100)

连续点击编程式导航会报错 解决方法 :在router中设置

//获取原型对象上的push replace函数
const originalPush = Router.prototype.push
const originalReplace = Router.prototype.replace
//修改原型对象中的push replace方法
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}
Router.prototype.replace = function replace(location) {
  return originalReplace.call(this, location).catch(err => err)
}

8 相关配置

路由配置

path
component
children
props
name

路由器配置

routes: 注册路由
mode
    类型: string
    默认值: "hash" (浏览器环境):
    可选值: "hash" | "history":
    配置路由模式:
        hash: 与其他生态的对接不完美,有兼容性问题,如微信支付
        history: 兼容性强,但是使用时有注意点
linkActiveClass
    类型: string
    默认值: "router-link-active"
    全局配置 <router-link> 的默认“激活 class 类名”。

linkExactActiveClass
    类型: string
    默认值: "router-link-exact-active"
    全局配置 <router-link> 精确激活的默认的 class。

注意点如下:

1.静态资源一定要使用绝对路径  使用mode时记得修改stasic下的样式目录为绝对路径
2.为了保障整个路由机制在history模式下可以正常运转 静态资源服务器遇到的所有404请求都应该要返回首页,在前端的开发环境下;使用的webpack-dev-server这个服务器已经帮我们做好了这个配置,可是在项目上线的时候;使用的不是开发环境;我们需要提醒后台或运维在静态资源服务器中将,所有的404请求配置成首页;  如果静态资源服务器有转发功能的话;遇到不带后缀的路径统一转发到后台接口服务器的话;那后台接口服务器也要将404请求配置为首页; 如果转发到后台发现这次请求不是个404请求(前端路由的命名 和 后台路由的命名一致了);那前端路由就会失效;所以我们还要保证前后端路由的命名不能冲突;一般后台路由的命名都会以/api开头!前端路由不能使用/api开头!

综上所述;最佳实践: 如果使用了路由;而且路由是history模式;公司上线时;不管是
静态资源服务器 还是 接口服务器 统一要配404!(所有的404请求配置成首页)
后台接口最好以/api开头!

9 配置404路由

routes.js中配置 *代表任意字符 要在最后配置

新建404组件,在router中引入,并进行配置
在路由数组最后配置   {path:"*",component: notFound}

10 路由守卫

① 全局前置守卫

const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
  // to: 要去的路由
  // form:来自于哪个路由
  console.log(to,from)
  //放行到to这个路由
  if(to.path !== "/user"){
    //只要有路由的跳转就会别全局导航守卫钩子拦住
    next("/user")
  }else{
    //放行!!!!  不代表是路由跳转而是放行
    next()
  }
})

② 全局解析守卫

router.beforeResolve((to,fromm,next)=>{
  console.log("全局解析守卫   beforeResolve")
  next()
})

这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

③ 全局后置守卫

可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to,fromm)=>{
  console.log("全局后置守卫   afterEach")
})

④ 路由独享的守卫

可以在路由配置上直接定义 beforeEnter 守卫:

beforeEnter(to, from, next){
          console.log("路由独享的守卫 beforeEnter for userDetail")
          next()
        }

⑤ 组件内的守卫

最后,你可以在路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave
//组件内的守卫
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    console.log("组件内的守卫 beforeRouteEnter for about",this,"-----");
    next((vm)=>{
      console.log("组件内的守卫 beforeRouteEnter next for about",vm);
    })
  },

  //当前这个钩子一般情况下不会被调用! 除非钩子所在的组件被复用时才会调用
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
  // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
    console.log("组件内的守卫 beforeRouteUpdate for about");
    next()
  },


  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    console.log("组件内的守卫 beforeRouteLeave for about");
    const answer = window.confirm('Do you really want to leave? ')
  if (answer) {
    next()
  } else {
    next(false)
  }
  }

⑥ 完整的导航解析流程

  • A路由跳转到B路由(B路由是动态路由)
1.  触发导航(声明式 编程式)
2.  A路由中组件内的beforeRouteLeave守卫     
3.  全局前置守卫 beforeEach
4.  B路由独享的守卫 beforeEnter
5.  解析异步路由组件    
6.  B路由中组件内的 beforeRouteEnter守卫 
7.  全局解析守卫 beforeResolve 
8.  导航被确认
9.  全局后置钩子 afterEach
10. 触发 DOM 更新
11. beforeRouteEnter中的next(()=>{})回调被执行
  • B路由切换到B路由(动态路由切换)
1. 全局前置守卫 beforeEach
2. 在重用的组件里调用 beforeRouteUpdate
3. 全局解析守卫 beforeResolve 
4. 全局后置钩子 afterEach
  • this问题
beforeRouteEnter守卫 中的this指向的是undefined
beforeRouteEnter守卫 中next第一个参数是一个回调函数;此回调函数的第一个参数是vm实例对象   

11 路由守卫钩子 VS 生命周期钩子

生命周期钩子不能卡流程

路由守卫钩子可以卡流程

写法:把路由守卫钩子定义成async 函数,钩子内部的异步逻辑需要被promise包裹,而且要有await

12 路由小练习

下载配别名 --open chrome

搭建路由 router routes 在main.js中引入router 配置app.vue

搭建路由组件并进行配置 app.vue 设置样式 route-link calss为非promise样式

[引入重置样式 reset.css 放在stasic 在index.html中引入

用less npm i less less-loader 在app中进行进入(为了样式层叠性)]

定义组件home about

在home组件里有两个嵌套路由 news 和message

message.vue 组件有内容 在生命周期内发送请求 来个loading图标,放在stasic下

点击内容的时候显示详细信息,定义子组件messageDetail 并跳转为图片(带params,传id,prop接收,动态路由写在watch内,监听数据id)

13 路由元信息

路由配置:{meta:{a:"xxx"}}
访问:$route.meta.a 可以访问路由的元信息

14 路由懒加载

持有组件功能的promise对象,路由会使用懒加载

路由配置:{meta:{component:() => import("url")}}
		url:vue组件的地址
		import("url"): promise;该promise持有的值是当前的组件对象

魔法解释

 {path:"/a",component:() => import(/* webpackChunkName: "a" */ '@/pages/a.vue')}2.0脚手架中要多写一个配置:
    build/webpack.base.conf.js
    output:chunkFilename: "[name].js"

中引入

用less npm i less less-loader 在app中进行进入(为了样式层叠性)]

定义组件home about

在home组件里有两个嵌套路由 news 和message

message.vue 组件有内容 在生命周期内发送请求 来个loading图标,放在stasic下

点击内容的时候显示详细信息,定义子组件messageDetail 并跳转为图片(带params,传id,prop接收,动态路由写在watch内,监听数据id)

13 路由元信息

路由配置:{meta:{a:"xxx"}}
访问:$route.meta.a 可以访问路由的元信息

14 路由懒加载

持有组件功能的promise对象,路由会使用懒加载

路由配置:{meta:{component:() => import("url")}}
		url:vue组件的地址
		import("url"): promise;该promise持有的值是当前的组件对象

魔法解释

 {path:"/a",component:() => import(/* webpackChunkName: "a" */ '@/pages/a.vue')}2.0脚手架中要多写一个配置:
    build/webpack.base.conf.js
    output:chunkFilename: "[name].js"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值