❤️vue路由

小明的学习圈子2023-11-28前端路由

vue路由官方文档地址:https://router.vuejs.org/zh/

接口地址参考:https://developer.mozilla.org/zh-CN/docs/Web/API

导航守卫地址参考:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E8%B7%AF%E7%94%B1%E7%8B%AC%E4%BA%AB%E7%9A%84%E5%AE%88%E5%8D%ABopen in new window

1.前端路由的核心

1.1URL的hash

  • URL的hash也就是锚点(#), 本质上是改变window.location的href属性.
  • 我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新

1.2.HTML5的history模式:pushState

  • history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.
  • history.pushState(state, title[, url])
  • 使用pushState()方法 可以控制浏览器自带的返回按钮,有时候我们想让用户点击浏览器返回按钮时,不返回,或执行其他操作,这时,就用到history.pushState()方法

1.3.HTML5的history模式:replaceState

  • history.replaceState(stateObj, title[, url]);
两个方法的区别:
    pushState添加一个最新的历史记录。    
    replaceState则是把当前的页面的历史记录替换掉。他们最大的特点是添加或替换历史记录后,浏览器地址栏会变成你传的地址,而页面并不会重新载入或跳转。
例如,假设当前的页面地址是temp.com/A.html,且history中此时只有一条当前页面的记录。
    当你执行history.pushState(null,null,"B.html")后,浏览器的地址栏会显示temp.com/B.html,但并不会跳转到B.html,甚至并不会去检查B.html存不存在,只是加入一个最新的历史记录项。此时history中会有两个记录。假如你此时点击页面上的link跳转到另外一个页面后,点击浏览器的后退按钮,则url会变成temp.com/B.html,如果此前的A.html的页面浏览器有缓存的话会显示A.html的内容,否则会发起请求temp.com/B.html。如果再次点后退,url会变成temp.com/A.html。
   而如果执行history.replaceState(null,null,"B.html")的话,浏览器的地址栏也会显示temp.com/B.html,区别是history中只有当前B.html的记录,而A.html的记录已被替换掉。
利用这些特性,可以用来修改当前页面的URL来达成某些目的,比如可以用来记住搜索条件。

1.4HTML5的history模式:go

  • window.history.go(delta);
  • go()方法从会话历史记录中加载特定页面。你可以使用它在历史记录中前后移动,具体取决于delta参数的值。

1.5HTML5的history模式:back

  • window.history.back()
  • back()方法会在会话历史记录中向后移动一页。如果没有上一页,则此方法调用不执行任何操作。

1.6HTML5的history模式:forward

  • 在会话历史中向前移动一页。它与使用delta参数为1时调用 history.go(delta)的效果相同。

  • window.history.forward();

  • history.back() 等价于 history.go(-1)

  • history.forward() 则等价于 history.go(1)

2.vue路由

2.1安装和使用vue-router

步骤一: 安装vue-router   npm install vue-router --save 步骤二: 在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能)   第一步:导入路由对象,并且调用 Vue.use(VueRouter)   第二步:创建路由实例,并且传入路由映射配置   第三步:在Vue实例中挂载创建的路由实例

2.1.1创建路由组件

<template>
  <div>
    <h2>我是首页</h2>
    <p>我是关于内容, 呵呵呵</p>
  </div>
</template>
<script>
    export default {
      name: "Home",
      data() {
        return {
          message: '你好啊',
        }
      },
      created() {
        console.log('home created');
      },
    }
</script>

2.1.2index.js创建路由,并配置组件和路径的映射关系

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
// 1.通过Vue.use(插件), 安装插件
Vue.use(Router)
// 2.配置路由和组件直接的应用关系
const routs = [
  {
    path: '/',
    name: 'HelloWorld',
    component: HelloWorld
  }
]
// 创建路由 
const router = new VueRouter({
  routs
})
// 3.将router对象传入到Vue实例
export default router

2.1.3main.js挂载路由

import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

2.1.4使用路由(这里有两种跳转方式,一个是通过router-link结合router-view方式,另一个是通过按钮方式跳转,通过按钮方式需要定义methods方法,通过$router方式跳转)

<template>
  <div id="app">
    <h1>我是主页面</h1>

<!--<router-link to="/HelloWorld" tag="button" replace  >首页</router-link>
    <router-view></router-view>-->

    <button @click="homeClick">首页</button>
  </div>
</template>
<script>
export default {
  name: 'App',
  data(){
    return {
      userId:'zhangsan'
    }
  },
  methods:{
    homeClick(){
      // 通过代码的方式修改路由 vue-router
      // push => pushState
      // this.$router.push('/HelloWorld')
      this.$router.replace('/HelloWorld').catch(err=>err)
    } 
  }
}
</script>
  • 创建VueRouter对象,并指定路由参数
  • routes:路由规则的数组,可以指定多个对象,每个对象是一条路由规则,包含以下属性:
  • path:路由的路径
  • component:组件名称
  • router-link: 该标签是一个vue-router中已经内置的组件, 它会被渲染成一个a标签.
  • router-view: 该标签会根据当前的路径, 动态渲染出不同的组件.
  • 网页的其他内容, 比如顶部的标题/导航, 或者底部的一些版权信息等会和router-view处于同一个等级.
  • 在路由切换时, 切换的是router-view挂载的组件, 其他内容不会发生改变.

2.2router-link补充

在前面的router-link中, 我们只是使用了一个属性: to, 用于指定跳转的路径.router-link还有一些其他属性:

<router-link to='/home' tag='li'>
  • tag: tag可以指定router-link之后渲染成什么组件, 比如上面的代码会被渲染成一个li元素, 而不是a
  • replace: replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
  • active-class: 当router-link对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.
  • 在进行高亮显示的导航菜单或者底部tabbar时, 会使用到该类.
  • 但是通常不会修改类的属性, 会直接使用默认的router-link-active即可.

3.路由的默认路径,路径的方式

让路径默认跳到到指定页面, 并且router-view渲染首页组件,我们只需要配置多配置一个映射就可以了.

img

配置解析:

  • 我们在routes中又配置了一个映射.
  • path配置的是根路径: /
  • redirect是重定向, 也就是我们将根路径重定向到/home的路径下, 这样就可以得到我们想要的结果了.

上边说过改变路径的方式有两种:

  • URL的hash(浏览器的url会有一个#)
  • HTML5的history

默认情况下, 路径的改变使用的URL的hash. 如果希望使用HTML5的history模式, 非常简单, 进行如下配置即可

// 配置路由和组件之间的应用关系
const router = new VueRouter({
  routes,
  mode: 'history',//hash模式浏览器url会有#,所有改成history模式
  linkActiveClass:"active"//设置选中的按钮的样式
})

代码示例:

index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../components/Home'
import About from '../components/About'
// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)

//多次点击同一button出现问题的解决方案satrt
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}
//多次点击同一button出现问题的解决方案end
// 2.创建VueRouter对象
const routes = [
  {
    path: '/',
    //name: 'HelloWorld',
    redirect:'/home'
  },
  {
    path: '/home',
    //name: 'HelloWorld',
    component: Home
  },
  {
    path: '/about',
    //name: 'HelloWorld',
    component: About
  }
]
// 配置路由和组件之间的应用关系
const router = new VueRouter({
  routes,
  mode: 'history',//hash模式浏览器url会有#,所有改成history模式
  linkActiveClass:"active"//设置选中的按钮的样式
})
// 3.将router对象传入到Vue实例
export default router

App.vue

<template>
  <div id="app">
    <h1>我是主页面</h1>
<!--    <router-link to="/home" tag="button" replace active-class="active">首页</router-link>
    <router-link to="/about" tag="button" replace active-class="active">关于</router-link>-->
<!--    <router-link to="/home" tag="button" replace  >首页</router-link>
    <router-link to="/about" tag="button" replace >关于</router-link>-->

    <button @click="homeClick">首页</button>
    <button @click="aboutClick">关于</button>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods:{
    homeClick(){
      // 通过代码的方式修改路由 vue-router
      // push => pushState
      // this.$router.push('/home')
      //多次点击同一button出现问题的解决方案
      this.$router.replace('/home').catch(err=>err)
    },
    aboutClick(){
      // this.$router.push('/about')
      //多次点击同一button出现问题的解决方案
      this.$router.replace('/about').catch(err=>err)
    }
  }

}
</script>

<style>
.active{color:red;}
</style>

Home.vue

<template>
  <div>
    <h2>我是首页</h2>
    <p>我是关于内容, 呵呵呵</p>
  </div>
</template>

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

<style scoped>

</style>

About.vue

<template>
  <div>
    <h2>我是关于</h2>
    <p>我是关于内容, 呵呵呵</p>
  </div>
</template>

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

<style scoped>

</style>

main.js

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

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

4.页面的title显示问题,让页面显示自定义的title,可以使用导航守卫router.beforeEach方式。

如何改变网页的标题

  • 网页标题是通过title来显示的, 但是SPA只有一个固定的HTML, 切换不同的页面时, 标题并不会改变.
  • 但是我们可以通过JavaScript来修改title的内容.window.document.title = '新的标题'.

普通的修改方式:

  • 我们比较容易想到的修改标题的位置是每一个路由对应的组件.vue文件中.
  • 通过mounted声明周期函数, 执行对应的代码进行修改即可.
  • 但是当页面比较多时, 这种方式不容易维护(因为需要在多个页面执行类似的代码).
  • 有没有更好的办法呢? 使用导航守卫即可.
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../components/Home'
import About from '../components/About'
import User from '../components/User'

const HomeNews = () => import('../components/HomeNews')
const HomeMessage = () => import('../components/HomeMessage')
const Profile = () => import('../components/Profile')

// 1.通过Vue.use(插件), 安装插件
Vue.use(VueRouter)

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

// 2.创建VueRouter对象
const routes = [
  {
    path: '/',
    //name: 'HelloWorld',
    redirect:'/home'
  },
  {
    path: '/home',
    //name: 'HelloWorld',
    meta:{
      title:'首页home'
    },
    component: Home,
    children:[
/*      {
        path:"",
        redirect:'news'

      },*/
      {
        path:"news",
        component:HomeNews,
        // meta:{
        //   title:'新闻'
        // },
      },{
        path:"msg",
        component:HomeMessage,
        // meta:{
        //   title:'消息'
        // },
      }
    ]
  },
  {
    path: '/about',
    //name: 'HelloWorld',
    component: About,
    meta:{
      title:'关于'
    },
  },
  {
    path: '/user/:userId',
    //name: 'HelloWorld',
    component: User,
    meta:{
      title:'用户'
    },
  },
  {
    path:'/profile',
    component:Profile,
    meta:{
      title:'我'
    },
  }
]
// 配置路由和组件之间的应用关系
const router = new VueRouter({
  routes,
  mode: 'history',//hash模式浏览器url会有#,所有改成history模式
  linkActiveClass:"active"//设置选中的按钮的样式
})
router.beforeEach((to, from, next)=> {
  document.title= to.meta.title
  //document.title= to.matched[0].meta.title
  next()
})

// 3.将router对象传入到Vue实例
export default router

5.动态路由

在某些情况下,一个页面的path路径可能是不确定的,比如我们进入用户界面时,希望是如下的路径:

  • /user/aaaa或/user/bbbb
  • 一个“路径参数”使用冒号 : 标记。当匹配到一个路由时,参数值会被设置到 this.$route.params,可以在每个组件内使用

除了有前面的/user之外,后面还跟上了用户的ID 这种path和Component的匹配关系,我们称之为动态路由(也是路由传递数据的一种方式)。

img

6.路由的懒加载

当打包构建应用时,Javascript 包会变得非常大,影响页面加载。 如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了

存在问题: 首先, 路由中通常会定义很多不同的页面. 一般情况下, 这个页面最后被打包是放在一个js文件中. 但是, 页面这么多放在一个js文件中, 必然会造成这个页面非常的大. 如果我们一次性从服务器请求下来这个页面, 可能需要花费一定的时间, 甚至用户的电脑上还出现了短暂空白的情况. 如何避免这种情况呢? 使用路由懒加载就可以了.

懒加载作用: 路由懒加载的主要作用就是将路由对应的组件打包成一个个的js代码块. 只有在这个路由被访问到的时候, 才加载对应的组件

img

懒加载的方式

方式一: 结合Vue的异步组件和Webpack的代码分析.

const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};

方式二: AMD写法

const About = resolve => require(['../components/About.vue'], resolve);

方式三: 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.

const Home = () => import('../components/Home.vue')

7.嵌套路由

const router = new VueRouter({
  routes: [
    {
      path: '/user/:id',
      component: User,
      children: [
        {
          // 当 /user/:id/profile 匹配成功,
          // UserProfile 会被渲染在 User 的 <router-view> 中
          path: 'profile',
          component: UserProfile
        },
        {
          // 当 /user/:id/posts 匹配成功
          // UserPosts 会被渲染在 User 的 <router-view> 中
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]
} 

img

配置子路由默认页面

  {
    path: '',//注意和父路由配置默认路径不一样没有/
    redirect:'/home'
  },

8.传递参数的方式

传递参数主要有两种类型: params和query

params的类型:

  • 配置路由格式: /router/:id
  • 传递的方式: 在path后面跟上对应的值
  • 传递后形成的路径: /router/123, /router/abc

query的类型:

  • 配置路由格式: /router, 也就是普通配置
  • 传递的方式: 对象中使用query的key作为传递方式
  • 传递后形成的路径: /router?id=123, /router?id=abc
  • 取值方式:

代码示例:

两种方式,一种是通过router-like方式传递,一种是通过按钮方法用query传递。

<template>
  <div id="app">
    <h1>我是主页面</h1>
    <!--<router-link :to="/profile" tag="button" replace  >档案</router-link>-->
    <!--<router-link :to="{path:'/profile',query:{name:'lufei'}}" tag="button" replace  >档案</router-link>-->
    <button @click="userClick">用户</button>
    <button @click="profileClick">档案</button>
    <keep-alive>
      <router-view/>
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: 'App',
  data(){
    return {
      userId:'zhangsan'
    }
  },
  methods:{
    homeClick(){
      // 通过代码的方式修改路由 vue-router
      // push => pushState
      // this.$router.push('/home')
      this.$router.replace('/home').catch(err=>err)
    },
    aboutClick(){
      // this.$router.push('/about')
      this.$router.replace('/about').catch(err=>err)
    },
    userClick(){
      this.$router.replace('/user/'+this.userId).catch(err=>err)
    },
    profileClick(){
      this.$router.replace({
        path:'/profile',
        query:{name:"zhangsan"}
      }).catch(err=>err)
    }
  }

}
</script>

<style>
.active{color:red;}
</style>

取值:

  • 获取参数通过$route对象获取的.
  • 在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this.$route ,并且当路由切换时,路由对象会被更新。
<template>
  <div>我是 档案
  <h2>{{$route.query.name}}</h2>

  </div>
</template>

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

<style scoped>

</style>

9.$route和$router的区别

  • $router为VueRouter实例,想要导航到不同URL,则使用$router.push方法(VueRouter实例)
  • $route为当前router跳转对象里面可以获取name、path、query、params等 (VueRouter里的每个path,处于活跃的,也就是点击的那个)

10.keep-alive和vue-router

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。activated和beforeRouteLeave和keep-alive一起使用才有效。 它们有两个非常重要的属性:

  • include - 字符串或正则表达,只有匹配的组件会被缓存
  • exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存

router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:

<div id="app" class='wrapper'>
    <keep-alive>
        <!-- 需要缓存的视图组件 --> 
     <router-view"></router-view>
</div>

通过activated和beforeRouteLeave和keep-alive一起使用来实现记录之前的路径。

    <keep-alive exclude="Profile,User">
      <router-view/>
    </keep-alive>
 export default {
        name: "Home",
        data() {
          return {
            message: '你好啊',
            path: '/home/news'
          }
        },
        created() {
          console.log('home created');
        },
        destroyed() {
          console.log('home destroyed');
        },
        // 这两个函数, 只有该组件被保持了状态使用了keep-alive时, 才是有效的
        activated() {
          this.$router.push(this.path);
          console.log('activated');
        },
        deactivated() {
          console.log('deactivated');
        },
        beforeRouteLeave(to, from, next) {
          console.log(this.$route.path);
          this.path = this.$route.path;
          next()
        }
      }
Last Updated 2024/4/6 11:55:17