Vue基础-day05

一、前端路由

1、URL

URL:格式 scheme://host:port/path?query#hash

Hash 的特点:

Hash 地址发生变化,不会导致浏览器页面的刷新

Hash 地址发生变化,会形成浏览器历史记录

2、应用类型与路由的概念

多页应用与后端路由

MPA(Multi Page Application):在多页应用中,想要切换页面就是跳转到一个新的 html 页面。

路由工作是由后端完成的。

单页应用与前端路由

SPA(Single Page Application):在单页应用中,只有唯一的一个 HTML 页面,所以想要切换页面就只能切换

组件来达到切换页面的效果。

这种切换组件达到页面切换的效果,就是前端路由。

路由工作是由前端来完成。

前端路由:访问不同的 Hash 地址,显示不同的组件。(vue-cms效果)

3. 前端路由的工作方式

① 用户点击了页面上的路由链接,会导致 URL 地址栏中的 Hash 值发生变化

② 前端路由监听到 Hash 地址的变化

③ 把当前 Hash 地址对应的组件渲染到浏览器中

强调:前端路由,指的是 Hash 地址与组件之间的对应关系!

二、Vue-router

1. 什么是 vue-router

vue-router 是 vue.js 官方给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目

中组件的切换。

vue-router 的官方文档地址:https://router.vuejs.org/zh/

2、vue-router 的使用步骤**

① 安装 vue-router

② 创建路由模块

③ 挂载路由模块

④ 配置路由规则

⑤ 声明路由链接和占位符

2.1 在项目中安装 vue-router

1. 项目准备:

资料/router-demo -> code/router-demo

npm i

npm run serve

2. 在 vue2 的项目中,安装 vue-router:

npm i vue-router@3.5.2 -S(注意版本)

2.2创建路由模块

在 src 源代码目录下,新建 router/index.js 路由模块,并初始化如下的代码:

//1.导入vue和vue-router
import Vue from 'vue'
import VueRouter from 'vue-router'

//2.使用Vue.use,将vue-router安装为vue插件
Vue.use(VueRouter)

//3.创建路由模块,即实例对象
const router = new VueRouter({
})

//导出路由模块
export default router

2.3挂载路由模块

在 src/main.js 入口文件中,导入并挂载路由模块。示例代码如下:

import Vue from 'vue'
import App from './App.vue'

// 导入 bootstrap 样式
import 'bootstrap/dist/css/bootstrap.min.css'
// 全局样式
import '@/assets/global.css'

// 1. 导入路由模块
import router from './router/index.js'

Vue.config.productionTip = false

new Vue({
    render: h => h(App),
  //2.挂载路由模块
    router
}).$mount('#app')

2.4配置路由规则

在 src/router/index.js 路由模块中,通过 routes 数组声明路由的匹配规则。示例代码如下:

// 导入组件
import Home from '../components/Home.vue'
import Movie from '../components/Movie.vue'
import About from '../components/About.vue'
const router = new VueRouter({
        // routes 用于配置路由规则
        // 路由规则:hash 地址和组件的对应关系
        routes: [
            // 每一条路由规则是一个对象
            // 语法:{ path: 'hash地址', component: 组件 }
            {path: '/home', component: Home},
            {path: '/movie', component: Movie},
            {path: '/about',component: About,}
        ]
    }
)

2.5声明路由链接和路由占位符

在 src/App.vue 组件中,使用 vue-router 提供的 声明路由链接和路由占位符:

<template>
  <div class="app-container">
    <h1>App 根组件</h1>

    <!-- router-link 是路由链接,用于页面跳转的链接 -->
    <!-- to 用于指定要调整的路由的 hash 地址 -->
    <router-link to="/home">首页</router-link>
    <router-link to="/movie">电影</router-link>
    <router-link to="/about">关于</router-link>

    <!-- 路由占位符 -->
    <router-view></router-view>
  </div>
</template>

3.Vue-router的常见用法

1、路由重定向

路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示地址 C 对应的组件页面。

通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:

index.js
const router = new VueRouter({
        // routes 用于配置路由规则
        // 路由规则:hash 地址和组件的对应关系
        routes: [
           //重定向
            // {path:'当前地址','实际想访问的地址'}
            {path: '/', redirect: '/home'},
            // 每一条路由规则是一个对象
            // 语法:{ path: 'hash地址', component: 组件 }
            {path: '/home', component: Home},
            {path: '/movie', component: Movie},
            {path: '/about',component: About,}
        ]
    }
)

2、嵌套路由

在本身就是通过路由展示的组件中,再使用路由去控制其他组件的展示,就会形成嵌套路由。

index.js
const router = new VueRouter({
        // routes 用于配置路由规则
        // 路由规则:hash 地址和组件的对应关系
        routes: [
            //重定向
            // {path:'当前地址','实际想访问的地址'}
            {path: '/', redirect: '/home'},
            // 每一条路由规则是一个对象
            // 语法:{ path: 'hash地址', component: 组件 }
            {path: '/home', component: Home},
            {path: '/movie', component: Movie},
            {
                path: '/about',
                component: About,
                //在哪个组件下面嵌套展示其他路由,就在哪个路由下面声明children
                children: [
                    //声明子路由规则
                    //写法1
                    // {path: '/about/tab1', component: Tab1},
                    // {path: '/about/tab2', component: Tab2}

                    //写法2
                    //子路由地址前不能加/
                    {path: 'tab1', component: Tab1},
                    {path: 'tab2', component: Tab2}
                ]
            }
        ]
    }
)
<template>
  <div class="about-container">
    <h3>About 组件</h3>
    <!--    声明子路由的路由链接-->
    <router-link to="/about/tab1">tab1</router-link>
    <router-link to="/about/tab2">tab2</router-link>
    <!--    声明子路由的占位符-->
    <router-view></router-view>
  </div>
</template>

3、动态路由

动态路由指的是:把 Hash 地址中动态变化的部分定义为一个动态的参数项,从而提高路由规则的复用性。

在 vue-router 中使用英文的冒号(:)来定义路由的参数项。示例代码如下:

index.js
const router = new VueRouter({
    routes: [
						//动态路由
            //将路由地址变化的部分改成动态参数项
            {path: '/movie/:id', component: Movie},
      ]
                             })
App.vue
<router-link to="/movie/1">电影1</router-link>
    <router-link to="/movie/2">电影2</router-link>
    <router-link to="/movie/3">电影3</router-link>

动态获取路由参数

About.vue
    <!--    通过编程式导航的第一种传参方式获取到的参数 this.$route.query.参数-->
    <p>通过编程式导航的第一种传参方式获取到的id参数---{{ $route.query.id }}</p>
    <p>通过编程式导航的第一种传参方式获取到的id参数---{{ $route.query.name }}</p>

    <!--    通过编程式导航的第一种传参方式获取到的参数 this.$route.query.参数  this.$route.params.参数-->
    <p>通过编程式导航的第一种传参方式获取到的id参数---{{ $route.query.id }}</p>
    <p>通过编程式导航的第一种传参方式获取到的id参数---{{ $route.params.name }}</p>
export default {
  name: 'About',
  created(){
    // this.$route 里面存放的是当前页面的路由信息,获取当前路由的 hash 地址,query 参数,params 参数
    console.log(this.$route)
    // this.$router 是路由的实例对象,它里面有一些常用的方法 push go addRoutes beforeEach ...
    console.log(this.$router)
  }
}

4、编程式导航

声明式导航 & 编程式导航:

在浏览器中,点击链接实现路由跳转的方式,叫做声明式导航。例如:

普通网页中点击 链接、vue 项目中点击 都属于声明式导航

在浏览器中,调用 JS API 方法实现路由跳转的方式,叫做编程式导航。例如:

普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航

4.1this.$router.push
Home.vue
<template>
  <div class="home-container">
    <h3>Home 组件</h3>
    <button @click="toAbout">去关于页</button>
  </div>
</template>

<script>
export default {
  name: 'Home',
  methods: {
    toAbout() {
      // this.$router.push('要跳转的页面的hash地址')
      this.$router.push('/about')
          }
  }
}
</script>

<style lang="less" scoped>
.home-container {
  min-height: 200px;
  background-color: pink;
  padding: 15px;
}
</style>
编程式导航路由传参
<template>
  <div class="home-container">
    <h3>Home 组件</h3>
    <button @click="toAbout">去关于页</button>
  </div>
</template>

<script>
export default {
  name: 'Home',
  methods: {
    toAbout() {

      //  编程式导航路由传参

      //  方式1
      //   this.$router.push({
      //     path:'要跳转的页面的hash地址',
      //     query:{
      //       参数1:参数1的值,
      //       参数2:'参数2的值'
      //     }
      //   })

      this.$router.push({
        path: '/about',
        query: {
          id: 123,
          name: 'zhangsan'
        }
      })

    //  方式2
    //   this.$router.push({
    //     name: '路由的名字',(在路由处定义名字)
    //     query: {
    //       参数1: 参数1的值,
    //     },
    //     params:{
    //       参数2:'参数2的值'
    //     }
    //   })
      this.$router.push({
        name: 'about',
        query: {
          id: 456,
        },
        params:{
          name:'lisi'
        }
      })
    }
  }
}
</script>

<style lang="less" scoped>
.home-container {
  min-height: 200px;
  background-color: pink;
  padding: 15px;
}
</style>

4.2 this.$router.go
 <button @click="goback">后退</button>
 methods: {
    goback() {
      this.$router.go(-1)
    }
  }

5、导航守卫

导航守卫可以对路由跳转进行拦截,本次跳转是否能够成功的进行,由导航守卫进行控制,从而达到控制路

由的访问权限的功能。

5.1全局前置守卫

每次发生路由的导航跳转时,会触发全局前置守卫,进行拦截。因此,我们可以在全局前置守卫中,对每个

路由进行访问权限的控制:

import Vue from 'vue'
import VueRouter from 'vue-router'

// 导入路由组件
import Home from '@/components/Home.vue'
import Login from '@/components/Login.vue'
import Main from '@/components/Main.vue'

// 安装插件
Vue.use(VueRouter)

// 创建路由对象
const router = new VueRouter({
    // 路由规则
    routes: [
        {path: '/', redirect: '/home'},
        {path: '/home', component: Home},
        {path: '/login', component: Login},
        {path: '/main', component: Main}
    ]
})

//全局前置守卫:每次发生路由跳转都会经过全局前置守卫
// router.beforeEach(()=>{})
router.beforeEach((to, from, next) => {
//  to:要访问的页面路由信息
//  from:即将离开的路由页面信息
//  next:它是一个函数,可以控制路由如何跳转,next函数必须调用,才能跳转
//  1.next():表示放行本次跳转
//  2.next(false):表示不允许本次跳转
//  3.next('hash地址'):表示跳转到指定的hash地址对应的页面
})
// 导出路由对象
export default router
5.2控制后台访问权限
import Vue from 'vue'
import VueRouter from 'vue-router'

// 导入路由组件
import Home from '@/components/Home.vue'
import Login from '@/components/Login.vue'
import Main from '@/components/Main.vue'

// 安装插件
Vue.use(VueRouter)

// 创建路由对象
const router = new VueRouter({
    // 路由规则
    routes: [
        {path: '/', redirect: '/home'},
        {path: '/home', component: Home},
        {path: '/login', component: Login},
        {path: '/main', component: Main}
    ]
})

//全局前置守卫:每次发生路由跳转都会经过全局前置守卫
// router.beforeEach(()=>{})
router.beforeEach((to, from, next) => {
    //登录权限控制
    //判断要去的是不是main页面,是的话需要做权限控制,需要判断登录状态
    if (to.path === '/main') {
        //获取token字段
        const token = localStorage.getItem('token')
        //判断是否存在token
        if (token) {
            //存在,则直接放行
            next()
            //    不存在,跳转到登录页
        } else {
            next('/login')
        }
        //去的不是main页面,直接放行
    } else {
        next()
    }
})
// 导出路由对象
export default router
5.3模拟登陆
<template>
  <div class="login-container">
    <h3>Login 组件</h3>
    <hr/>

    <div>
      <!-- 账号 -->
      <label>账号:<input type="text" class="form-control" v-model="username"/></label>
    </div>
    <div>
      <!-- 密码 -->
      <label>密码:<input type="password" class="form-control" v-model="password"/></label>
    </div>
    <br/>
    <!-- 登录按钮 -->
    <button class="btn btn-primary" @click="login">登录</button>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data() {
    return {
      username: '',
      password: ''
    }
  },
  methods: {
    login() {
      if (this.username === 'admin' && this.password === '123456') {
        //登陆成功,将token存在本地
        localStorage.setItem('token', 'Bearer xxx')
        //跳转到后台页
        this.$router.push('/main')
      } else {
        //登录失败,提示用户
        alert('用户名或密码错误')
      }
    }
  }
}
</script>

<style lang="less" scoped>
.login-container {
  min-height: 200px;
  background-color: #b0cdf3;
  padding: 15px;

  > a {
    margin-right: 10px;
  }
}
</style>

Q.E.D.