# 前端路由

# 直接操作DOM元素

  • 直接操作DOM元素流程

    • ref属性给DOM元素起一个别名
    • this.$refs.别名
  • 直接操作组件实例

    • ref属性给组件元素起一个别名
    • this.$refs.别名
Vue.component('my-test', {
  template: `
    <div>
      <div>测试ref操作组件实例</div>
      <div>{{msg}}</div>
    </div>
  `,
  data () {
    return {
      msg: 'ref测试信息'
    }
  },
  methods: {
    handleClick () {
      console.log(this.msg)
    }
  }
})
<my-test ref='mytestcom'></my-test>
// 从组件外部访问组件内部的信息,所以必须得到组件实例
const mytest = this.$refs.mytestcom
// 只要能够获取组件的实例对象,那么就可以通过该对象获取组件的内部成员(包括属性和方法)
mytest.handleClick()

# SPA介绍

spa 是 single page application 简写,意思是单页面应用程序。vue适合开发spa类型的项目。

单页面应用体验 (opens new window)

优点:

  • 业务场景的切换,性能很好。
  • 集中维护一个网站的功能。
  • 完全的前后端分离(前后端可以并行开发,提供系统开发效率)

缺点:

  • 所有的功能集中的一个页面,依赖的资源是非常多的,加载第一次的时候很慢(首屏加载)。
  • 业务复杂度很高(解决方案:vue组件,前端路由---支持浏览器地址栏的回退功能)

# 前端路由介绍

目标:熟悉前端路由的概念

前端路由要实现的效果:支持SPA项目的页面切换,并且支持浏览器地址栏的回退和前进功能

前端路由:根据不同的url地址,页面上展示不同的内容(根据url地址的不同显示不同的组件。)

image-20210221092143032

点击菜单A/B/C,会导致浏览器地址栏链接的改变,以及组件显示区域内容的变化

菜单A链接 ------> 菜单A组件

菜单B链接 ------> 菜单B组件

菜单C链接 ------> 菜单C组件

# 前端路由原理

目标:基于hash的路由原理(通过地址栏hash字符串的改变,去更新页面的内容。)

  • http://localhost:3000?username=lisi#abc
  • http://localhost:3000?username=lisi#hello
  • 锚点(hash 哈希)作用:跳转到网页的某一个位置
  • 锚点的变化是否会导致浏览器重新向服务器发送一个请求?不会
  • 但是我们可以利用这个特点,实现锚点的变化导致页面的更新
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div>
    <a href="#/top">头条</a>    
    <a href="#/tech">科技</a>    
    <a href="#/edu">教育</a>    
  </div>
  <div id="area">
    <!-- 显示组件的位置 -->
  </div>
  <script>
    // 我们希望点击a标签时,实现页面内容的变化
    // 我们如何知道点击了哪个菜单?
    const div = document.getElementById('area')
    window.onhashchange = function () {
      // 该事件是原生js提供的事件,hash发生变化时自动触发
      // 获取对应的hash值,从而判断要显示什么内容
      switch(location.hash) {
        case '#/top':
          // 显示头条频道内容
          div.innerHTML = '头条信息'
          break;
        case '#/tech':
          // 显示科技频道内容
          div.innerHTML = '科技信息'
          break;
        case '#/edu':
          // 显示教育频道内容
          div.innerHTML = '教育信息'
          break;
      }
    }
  </script>
</body>
</html>

# vue-router

# 基本介绍

目标:熟悉vue-router基本信息

vue-router提供一套完善的强大的前端路由控制逻辑,可以快速的基于它实现vue项目开发。

vue-router (opens new window)是基于vue的js插件,实现了前端路由功能。

下载地址:https://unpkg.com/vue-router/dist/vue-router.js

# 基本使用

目标:基于url地址的变化实现组件的切换

  • 准备组件(用于路由链接变化是切换显示)
  • 定义路由映射规则(什么路径对应什么组件)
  • 初始化 vue-router 实例(配置路由和组件映射的关系)
  • 需要把路由实例挂载到vue中(把路由和vue关联起来)
  • 路由跳转链接 <router-link />(跳转的链接需要使用vue-router提供的标签实现)
  • 路由显示组件的位置 <router-view /> (指定一个位置用于显示链接对应的组件)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <!-- 5、配置路由的跳转链接 -->
    <!-- to的值就是配置路由映射时,path属性的值 -->
    <router-link to="/top">头条</router-link>    
    <router-link to="/tech">科技</router-link>    
    <router-link to="/edu">教育</router-link>
    <div>
      <!-- 6、配置路由组件的显示位置 -->
      <router-view/>
    </div>
  </div>

  <!-- 0、导入相关的js库文件 -->
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    // Vue.component('my-test', {
    //   template: ``
    // })
    // 1、准备路由组件
    const Top = {
      template: `
        <div>
          头条信息
        </div>
      `
    }
    const Tech = {
      template: `
        <div>
          科技信息
        </div>
      `
    }
    const Edu = {
      template: `
        <div>
          教育信息
        </div>
      `
    }
    // 2、定义路由映射规则
    const routes = [
      // path表示路由链接的路径
      // component表示路由链接关联的组件
      // path和component属性名称是vue-router规定的
      { path: '/top', component: Top },
      { path: '/tech', component: Tech },
      { path: '/edu', component: Edu }
    ]
    // 3、初始化  vue-router 实例
    const router = new VueRouter({
      // (缩写) 相当于 routes: routes
      // 冒号左侧的routes是固定的吗?是的
      routes: routes
    })
    // 4、需要把路由实例挂载到vue中
    new Vue({
      el: '#app',
      router,
      // 冒号左侧的router是固定的吗?是的
      // router: router,
      data: {
        msg: 'hello vue-router'
      }
    })
  </script>
</body>
</html>

# 动态路由

目标:熟悉动态路由的用法

动态路由:根据不同的URL地址,映射到同一个组件,这样做主要是为了节省组件(简化代码)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <!-- 路由链接 -->
    <router-link to='/users/1'>张三</router-link>
    <router-link to='/users/2'>李四</router-link>
    <router-link to='/users/3'>王五</router-link>
    <hr>
    <div>
      <!-- 路由填充位 -->
      <router-view/>
    </div>
  </div>
  <!-- 0、导入相关的js库文件 -->
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    // 动态路由

    // 路由组件(获取动态路由参数)
    // 通过$route.params可以获取动态路由参数
    const UserInfo = {
      template: `
        <div>
          <div>用户信息{{$route.params.id}}</div>
          <div>用户信息{{userId}}</div>
          <hr />
          <ul>
            <li>{{userInfo.id}}</li>
            <li>{{userInfo.name}}</li>
            <li>{{userInfo.age}}</li>
          </ul>
        </div>
      `,
      data () {
        return {
          // 所有用户数据
          list: [{
            id:1,
            name: '张三',
            age: 12
          }, {
            id:2,
            name: '李四',
            age: 13
          }, {
            id: 3,
            name: '王五',
            age: 14
          }]
        }
      },
      computed: {
        userId () {
          // console.log(this.$route.params)
          return this.$route.params.id
        },
        userInfo () {
          // 根据路由参数查询data中该用户的信息
          // 根据路由参数中的id去数组list中查找id对应的用户信息(对象)
          // const user = this.list.filter(item => {
          //   return parseInt(this.userId) === item.id
          // })
          // // 短路运算 user = [{id: 1, name: '张三', age: 12}]
          // return user && user[0]
          // ----------------------------
          // 基于数组的find方法查询id匹配的数组中的结果
          const user = this.list.find(item => {
            return this.userId === item.id
          })
          return user
        }
      }
    }
    // 路由映射
    const routes = [
      // 如果path的路径这样写 /users/:id,那么可以匹配何种路径?
      // /users/1
      // /users/2
      // /users/3
      { path: '/users/:id', component: UserInfo }
    ]
    // 实例化路径组件
    const router = new VueRouter({
      routes
    })
    // 挂载路由
    new Vue({
      el: '#app',
      router
    })
  </script>
</body>
</html>

总结:

  1. 路由映射规则 { path: '/user/:id', component: UserInfo }
  2. 获取路径传参:
  3. <router-link to="/users/1">tom</router-link>

# 路由参数传递

目标:熟悉路由的参数传递方式

使用路由的时候,路由跳转的时候如何去进行传参?两种:

  • Restful路径传参 /user/1001 就是路径传参
  • 查询字符串传参 /user?id=10001 就是键值对传参
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <!--5、路由链接 -->
    <!-- 基于Restful风格的方式传递路由参数 -->
    <router-link to='/users/1'>张三</router-link>
    <router-link to='/users/2'>李四</router-link>
    <router-link to='/users/3'>王五</router-link>
    <!-- 如下的写法:动态绑定to的值 -->
    <!-- name需要在配置路由映射时设置 -->
    <!-- params表示传递的动态路由的参数 -->
    <!-- <router-link :to='{name: "user", params: {id: 4}}'>赵六</router-link> -->
    <router-link :to='obj'>赵六</router-link>

    <!-- ------------------------------------- -->

    <!-- 基于查询字符串方式传递路由参数 -->
    <!-- 这种形式的参数,在组件中通过$route.query获取 -->
    <router-link to='/user?id=123&age=12'>小明</router-link>
    <router-link :to='{path: "/user", query: {id: 456, age: 13}}'>小聪</router-link>
    <router-link :to='{name: "abc", query: {id: 456, age: 13}}'>小萌</router-link>
    
    <hr>
    <div>
      <!-- 6、路由填充位 -->
      <router-view/>
    </div>
  </div>
  <!-- 0、导入相关的js库文件 -->
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    // 路由的参数传递

    // 1、路由组件(获取动态路由参数)
    // 通过$route.params可以获取动态路由参数
    const UserInfo = {
      template: `
        <div>
          <div>用户信息{{$route.params.id}}</div>
        </div>
      `
    }
    const UserInfo1 = {
      template: `
        <div>
          <div>用户信息1:{{$route.query.id}}</div>
        </div>
      `
    }
    // 2、路由映射
    const routes = [
      // name表示给path路径起一个别名
      { path: '/users/:id', name: 'user', component: UserInfo },
      { path: '/user', name: 'abc', component: UserInfo1 }
    ]
    // 3、实例化路径组件
    const router = new VueRouter({
      routes
    })
    // 4、挂载路由
    new Vue({
      el: '#app',
      data: {
        obj: {
          name: "user", 
          params: {
            id: 4
          }
        }
      },
      router: router
    })
  </script>
</body>
</html>

总结:

  • Restful风格
<!-- 基于Restful风格的方式传递路由参数 -->
<router-link to='/users/1'>张三</router-link>
<router-link to='/users/2'>李四</router-link>
<router-link to='/users/3'>王五</router-link>
<!-- 如下的写法:动态绑定to的值 -->
<!-- name需要在配置路由映射时设置 -->
<!-- params表示传递的动态路由的参数 -->
<!-- <router-link :to='{name: "user", params: {id: 4}}'>赵六</router-link> -->
<router-link :to='obj'>赵六</router-link>
  • 查询字符串风格
<!-- 基于查询字符串方式传递路由参数 -->
<!-- 这种形式的参数,在组件中通过$route.query获取 -->
<router-link to='/user?id=123&age=12'>小明</router-link>
<router-link :to='{path: "/user", query: {id: 456, age: 13}}'>小聪</router-link>
<router-link :to='{name: "abc", query: {id: 456, age: 13}}'>小萌</router-link>

# 路由重定向

目标:熟悉路由重定向的配置方式

路由重定向:访问某一个路径,自动跳转到另外一个路径。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <!-- 如果这里只有一个路由填充位,那么可以通过浏览器的地址栏修改路径 -->
    <router-view/>
  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    // 路由重定向: 本来访问的是一个连接,然后立刻跳转到另一个链接
    // Login和Home组件之间是什么关系:互斥关系
    const Login = {
      template: '<div>登录</div>'
    }
    const Home = {
      template: '<div>主页</div>'
    }
    const router = new VueRouter({
      routes: [
        // 配置路由的重定向(如果浏览器访问/,那么自动跳转到/login)
        { path: '/', redirect: '/login'},
        { path: '/login', component: Login},
        { path: '/home', component: Home}
      ]
    })
    new Vue({
      el: '#app',
      router
    })
  </script>
</body>
</html>

# 编程式导航

目标:熟悉编程式导航实现路由跳转的方式

编程式导航:通过js的方式触发路由的跳转。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <router-view/>
  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    // 编程式导航:通过js方式控制路由的跳转
    const Login = {
      template: `
        <div>
          <form>
            <div>
              用户名:
              <input type="text" v-model='username'/>
            </div>
            <div>
              密码:
              <input type="text" v-model='password'/>
            </div>
            <div>
              <input @click='handleLogin' type="button" value="提交" />
            </div>
          </form>
        </div>
      `,
      data () {
        return {
          username: '',
          password: ''
        }
      },
      methods: {
        handleLogin () {
          // console.log(this.username, this.password)
          // 实现登录功能
          // 1、表单验证
          // 2、调用接口实现登录
          // 3、判断返回的状态,如果是成功的,跳转到主页面,否则,进行错误提示
          // 假设ret是第2步返回的结果
          const ret = {
            // 登录成功
            status: 0
          }
          if (ret.status === 0) {
            // 登录成功,跳转到主页面
            // this.$router.push方法是vue-router提供的,用于实现路由跳转
            // push方法的参数一表示要跳转到的路由的路径
            // 这里的$router不是$route
            // 跳转到指定的路由路径
            this.$router.push('/home')
            console.log(this.$router)
            // 向后跳转到之前的一个连接(相当于点击浏览器的回退按钮)
            // this.$router.back() 
            // 向后跳转到下一个连接
            // this.$router.forward()(相当于点击浏览器的前进按钮)
            // 跳转到上/下第n个链接
            // this.$router.go(-2)
          }
        }
      }
    }
    const Home = {
      template: '<div>主页</div>'
    }
    const router = new VueRouter({
      routes: [
        // 配置路由的重定向(如果浏览器访问/,那么自动跳转到/login)
        { path: '/', redirect: '/login'},
        { path: '/login', component: Login},
        { path: '/home', component: Home}
      ]
    })
    new Vue({
      el: '#app',
      router
    })
  </script>
</body>
</html>

总结:

  • $route 获取路由相关信息(路由传参 this.$route.params, this.$route.query)
  • $router 提供路由相关函数 ( 跳转方法 this.$router.push() )

  • 动态路由参数传递
// 编程是导航跳转时,是否可能从A组件传递数据给B组件?
this.$router.push({
  path: 'home',
  // 组件中获取query参数的方式this.$router.history.current.query
  query: {
    id: 123,
    age: 12
  },
  // params是用于给【动态路由】传递参数
  // params: {
  //   id: 456,
  //   age: 13
  // }
})
  • 获取路由参数
this.$router.history.current.query

# 嵌套路由

目标:熟悉嵌套路由的用法

要进行路由的嵌套,只需要在一级路由规则下,加上一个属性 children,即可定义二级路由规则。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <router-view/>
  </div>
  <script src="lib/vue.js"></script>
  <script src="lib/vue-router.js"></script>
  <script>
    // 嵌套路由:在路由组件中再次配置路由的链接和路由填充位
    const Login = {
      template: `
        <div>
          <form>
            <div>
              用户名:
              <input type="text" v-model='username'/>
            </div>
            <div>
              密码:
              <input type="text" v-model='password'/>
            </div>
            <div>
              <input @click='handleLogin' type="button" value="提交" />
            </div>
          </form>
        </div>
      `,
      data () {
        return {
          username: '',
          password: ''
        }
      },
      methods: {
        handleLogin () {
          // console.log(this.username, this.password)
          // 实现登录功能
          // 1、表单验证
          // 2、调用接口实现登录
          // 3、判断返回的状态,如果是成功的,跳转到主页面,否则,进行错误提示
          // 假设ret是第2步返回的结果
          const ret = {
            // 登录成功
            status: 0
          }
          if (ret.status === 0) {
            // 登录成功,跳转到主页面
            this.$router.push('/home')
          }
        }
      }
    }
    // 一级路由组件
    const Home = {
      template: `
        <div>
          <div>主页</div>
          <hr />
          <router-link to='/home/tech'>科技</router-link>
          <router-link to='/home/edu'>教育</router-link>
          <div>
            <!-- 二级路由填充位 -->
            <router-view/>
          </div>
        </div>
      `
    }

    const Tech = {template: '<div>科技</div>'}
    const Edu = {template: '<div>教育</div>'}
    const router = new VueRouter({
      routes: [
        // 配置路由的重定向(如果浏览器访问/,那么自动跳转到/login)
        { path: '/', redirect: '/login'},
        { path: '/login', component: Login},
        { 
          path: '/home', 
          component: Home,
          // children属性用于配置嵌套路由
          children: [
            // path后面的路由不要添加 /
            { path: 'tech', component: Tech},
            { path: 'edu', component: Edu}
          ]
        }
      ]
    })
    new Vue({
      el: '#app',
      router
    })
  </script>
</body>
</html>

总结:

  • 二级路由的路径前面不要添加斜杠
  • 链接跳转的位置路径要写完整的 /home/tech
const router = new VueRouter({
  routes: [
    // 配置路由的重定向(如果浏览器访问/,那么自动跳转到/login)
    { path: '/', redirect: '/login'},
    { path: '/login', component: Login},
    { 
      path: '/home', 
      component: Home,
      // 通过路由重定向可以默认显示一个二级路由组件
      redirect: '/home/tech',
      // children属性用于配置嵌套路由
      children: [
        // path后面的路由不要添加 /
        { path: 'tech', component: Tech},
        { path: 'edu', component: Edu}
      ]
    }
  ]
})

# 总结

  • 直接操作组件的用法ref
  • 前端路由
    • 熟悉SPA应用的场景(介绍)
    • 熟悉前端路由的概念(介绍)
    • 理解前端路由的原理(理解)
  • vue-router
    • 熟悉vue-router的基本信息(介绍)
    • 基本用法(6个步骤)
    • 动态路由(多个路由链接映射到同一个组件,简化组件数量)
    • 路由的参数传递
      • Restful风格的参数(2种用法)
      • 查询字符串形式的参数(2种用法)
    • 路由重定向(本来访问一个连接,自动跳转到另一个链接)
    • 编程式导航(用js的方法实现路由跳转并可以携带参数)
    • 嵌套路由(实现更加复杂的页面结构并支持跳转)