vue-router

本文详细介绍了vue-router的使用,包括后端路由、前端路由、单页面应用和HTML5的history模式。讲解了vue-router的安装、基本路径配置、动态路由、路由懒加载、嵌套路由和参数传递。同时,提到了路由守卫、keep-alive组件的使用,以及文件路径引用的处理,是全面掌握vue-router的参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

vue-router的基本使用

什么路由

路由:通过互联网把信息从源头传递到目的地的活动
路由表:本质上是一个映射表,记录了数据包的指向
路由器提供了两种机制:路由和转送

  • 路由决定数据包从来源到目的地的路径
  • 转送将输入端的数据转移到合适的输出端

vue-router的认识

  • vue-router是vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面
  • 我们可以其官方网站对其学习:https://router.vuejs.org/zh/
  • 在vue-router的单页面应用中,页面的路径的改变就是组件的切换

后端路由阶段

早期的网站开发整个HTML页面由服务器来渲染的

  • 服务器直接生产渲染好对应的HTML页面,返回给客户端进行展示

但是一个网站,这么多页面服务器如何处理

  • 一个页面有自己对应的网址,也就是URL
  • URL会发送到服务器,服务器会通过正则对该URL进行匹配,并且最后交给一个Controller进行处理
  • Controller进行各种处理,最终生成HTML或者数据,返回给前端
  • 这就是一个IO操作

上面的这种操作最后由后端路由

  • 当我们页面中需要请求不同的路径内容时,交给服务器来进行处理,服务器渲染好整个页面,并且将页面返回给客户端
  • 这种情况下渲染好的页面,不需要单独加载任何的js和css,可以直接交给浏览器展示,这样也有利于SEO的优化

后端渲染的缺点:

  • 一种情况是整个页面的模块由后端人员来编写和维护的
  • 另一种情况是前端开发人员如果要开发页面,需要通过PHP和JAVA等语言来编写页面代码
  • 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起,编写和维护会非常麻烦
    在这里插入图片描述

前端路由阶段

前后端分离阶段

  • 随着Ajax的出现,由了前后端分离的开发模式
  • 后端只提供API来返回数据,前端通过AJAX获取数据,并且可以通过JavaScript将数据渲染到页面中
  • 这样做最大的优点就是前后端的责任清晰,后端专注于数据上,前端专注于交互和可视化上
  • 并且当移动端(IOS/Android)出现后,后端不需要任何处理,依然使用之前的一套api即可
  • 目前很多的网站依然采用这种模式
    在这里插入图片描述

单页面阶段

  • 其实SPA最主要的特点就是在前后端分离的基础上加了一层前端路由
  • 也就是前端来维护一套路由规则

在这里插入图片描述

前端路由的核心是什么呢

  • 改变URL,但是页面不进行整体的刷新
  • 如何实现呢

HTML5的history模式:pushstate

浏览器窗口有一个history对象,用来保存浏览的历史

  • length
  • back() 移动到上一个访问页面,等同于浏览器的后退键
  • forward():移动到下一个访问页面,等同于浏览器的前进键
  • go():接受一个整数作为参数,移动到该整数指定的页面,移动到该整数指定的页面,比如go(1)相当于forward,go(-1)相当于back()
  • pushState()用来在浏览历史中添加记录,接受三个参数,
    • state :一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数,如果不需要这个参数,此处可以填null
    • title:新页面的标题,但是所有浏览器目前忽略这个值,因此这里填null
    • url:新的网址,必须与当前页面处在同一个域,浏览器的地址栏将显示这个网址
    • history.pushstate({},’’,‘foo’),浏览器地址栏立刻显示http://localhost:8080/foo;但并不会跳转到foo,甚至也不会检查foo是否存在,它只是成为浏览历史中的最新记录。总之,pushState方法不会触发页面刷新,只是导致history对象发生变化,地址栏会有反应

在这里插入图片描述

在这里插入图片描述

hash

location是javascript里边管理地址栏的内置对象,比如location.href就管理页面的url。
location.hash则可以用来获取或设置页面的标签值

location.hash='aaa'

在这里插入图片描述

location.href和location.hash的区别

  1. window.location.href
  1. window.location.hash

在这里插入图片描述

安装和使用vue-router

  • router-link该标签是一个vue-router中已经内置的组件,它会被渲染成一个a标签
  • router-view该标签会根据当前的路径,动态渲染出不同的组件
  • 网页的其他内容,比如顶部的标签/导航,或者底部的一些版权信息等会和router-view处于一个等级
  • 在路由切换时,切换的是router-view挂载的组件,其他内容不会发生改变

步骤:

  1. 安装vue-router, npm install vue-router --save

  2. 导入路由对象,并且调用vue.use(vuerouter)
    在这里插入图片描述
    use函数会调用VueRouter的install

  3. 创建路由实例,并且传入路由映射设置
    在这里插入图片描述

  4. 在vue实例中挂载创建的路由实例
    在这里插入图片描述
    在这里插入图片描述
    router/index.js

///router/index.js**
import VueRouter from 'vue-router'
import Vue from 'vue'
import Home from '../components/Home'
import About from '../components/About'


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

//2 创建vuerouter实例

const routes =[
    {
        path:'/home',
        component:Home
    },
    {
        path:'/about',
        component:About
    }


];
//需要传入RouterOptions对象
//在RouterOptions对象中需要传入routes属性值,是一个数组
const router = new VueRouter({
    routes
})

//3 将router实例传入到vue实例中
export default router

main.js

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
///main.js*
import Vue from 'vue'
import App from './App'
import router from './router/index'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  components: { App },
  router:router,
  template: '<App/>'
})

///App.vue

///App.vue
<template>
  <div id="app">
    <router-link to="/home">Home</router-link>
    <router-link to="/about">About</router-link>
    <!-- <Home></Home> -->
    <!-- 在那里显示 -->
    <router-view></router-view>

  </div>
</template>

<script>

import Home from './components/Home.vue'
import About from './components/About.vue'

export default {
  name: 'App',
  components: {
    Home
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

路由的默认路径

默认情况下,进入网站的首页,我们希望router-view渲染首页的内容。可以redirect实现

在这里插入图片描述
配置解析:
我们在router中又配置了一个映射
path配置的是根路径
redirect是重定向,也就是我们将路径重定向到/home的路径下

去除#改变路由和组件之间的应用关系

改变路径的两种方式:

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

在VueRouter实例中添加mode

const router = new VueRouter({
    routes,
    mode:'history'
})

在这里插入图片描述

改变router-link的标签

router-link中的属性

  • tag:tag可以指定router-link之后渲染成什么组件,比如下面的代码会渲染成button而不是a
    <router-link to="/home" tag="button">Home</router-link>
    <router-link to="/about" tag="button">About</router-link>

在这里插入图片描述

  • replace:replace不会留下history记录,所以指定replace的情况下,后退键返回不能返回到上一个页面中
    在这里插入图片描述
  • active-class :当router-link对应的路径匹配成功时,会自动给当前的元素设置一个router-link-active的class,设置active-class可以修改默认的名称
    • 在进行高亮显示的导航菜单或者底部tabbar时,会使用到该类
    • 但是通常不会修改类的属性,会直接使用默认的router-link-active即可

在这里插入图片描述

<template>
  <div id="app">
    <router-link to="/home" tag="button" active-class="active">Home</router-link>
    <router-link to="/about" tag="button" active-class="active">About</router-link>
    <router-view></router-view>
  </div>
</template>

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

设置active-class的两种方式:

  • 在router-link中设置
<router-link to="/home" tag="button" active-class="active">Home</router-link>
  • 在vuerouter实例中设置
const router = new VueRouter({
    routes,
    mode:'history',
    linkActiveClass:'active'
})

通过代码实现路由跳转

通过注入路由器,我们可以在任何组件内通过 this. r o u t e r 访 问 路 由 器 , 也 可 以 通 过 t h i s . router 访问路由器,也可以通过 this. router访this.route 访问当前路由
this.$route 表示当前路由对象,每一个路由都会有一个 route 对象,是一个局部的对象,可以获取对应的 name, path, params, query 等属性。
App.vue

<template>
  <div id="app">
    <button @click="homeClick">Home</button>
    <button @click="aboutClick">About</button>
    <router-view></router-view>
  </div>
</template>

<script>
import Home from './components/Home.vue'
import About from './components/About.vue'

export default {
  name: 'App',
  components: {
    Home
  },
  methods:{
    homeClick(){
      this.$router.replace('/home');

    },
    aboutClick(){
      this.$router.replace('/about');
    }
  }
}
</script>

动态路由设置

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

  • /user/aaa/bbb
  • 除了又前面的/uer之外,后面还跟上用户的id
  • 这种path和component的匹配关系,我们称之为动态路由(也是路由传递参数的一种的方式)
    注意:通过:to动态绑定id时,前面的字符串需要加单引号

index.js

const routes =[
    {
        path:'/user/:id',
        component:User
    }
];

App.vue

<template>
  <div id="app">
    <router-link :to="'/user/'+id">User</router-link>
    <router-view></router-view>
  </div>
</template>

User.vue
通过this.$route.params获取参数

//在template中不需要加this
<template>
    <div>
        <h2>User</h2>
        <p>我是User组件</p>
        <p>{{$route.params.id}}</p>
    </div>
</template>

路由的懒加载

  • 当打包构建应用时,JavaScript包会变得非常大,影响页面加载
  • 如果我们能把不同路由对应得组件分割成不同得代码块,然后当路由被访问得时候才加载对应组件,这样就更加高效了
  • 官方在说什么,我们知道路由中通常会定义很多不同得页面,这个页面最后被打包在那里呢?一般情况下,是放在一个js文件中。但是页面这么多放在一个js文件中,必然会造成这个页面非常得大。如果我们一次性从服务器中请求下来这个页面,可能需要花费一定得时间,甚至用户得电脑上还出现了短暂得空白情况。如何
    在这里插入图片描述
    app.js是当前应用程序开发得所有代码业务
    mainifest.js:为了打包代码得底层支撑
    vendor.js:提供第三方

懒加载实现方式

方式一:结合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中的实现方式

const Home = () => import('../components/Home')
const About = () => import('../components/About')
const User = () => import('../components/User')

const routes =[
    {
        path:'/',
        redirect:'/home',
    },
    {
        path:'/home',
        component:Home
    },
    {
        path:'/about',
        component:About
    },
    {
        path:'/user/:id',
        component:User
    }

];

vue-router嵌套路由

嵌套路由是一个很常见的功能:

  • 比如在home页面中,我们希望通过/home/news和/home/messages访问一些内容
  • 一个路径映射一个组件,访问这两个路径也会分别渲染两个组件
    实现嵌套路由有两个方案
  • 创建对应的子组件,并且在路由映射中配置对应的子路由
  • 在组件内部使用router-view

router/index.js

const routes =[

    {
        path:'/home',
        component:Home,
        children:[
             {
                path:'/',
                redirect:'/news'
            },
            {
                path:'/news',
                component:News,
            },
            {
                path:'/message',
                component:Message,
            }
        ]
    },
];

home.vue

<template>
    <div>
        <h2>Home</h2>
        <p>我是home组件</p>
        <router-link to="/news">News</router-link>
        <router-link to="/message">Message</router-link>
        <router-view></router-view>
    </div>
</template>

vue-router参数传递

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

  1. params的类型
  • 配置路由格式/router/:id
  • 传递的方式:在path后面跟上对应的值
  • 传递后形成的路径:/router/123,router/abc
  1. query的类型
  • 配置路由的格式;对象中使用query的key作为传递方式
  • 传递的方式,对象中使用的query的key作为传递方式
  • 传递后形成的路径:/router?id=123
<router-link :to="{path:'/news',query:{name:'aaa',age:'19'}}">News</router-link>

vue-router和route的由来

{
	$router:this._routerRoot._router
}
//以上写法跟上面是一致的,都是在对象中新增属性
Object.defineProperty(Vue.prototype,'$router',{
	get(){return this._routerRoot._router}
})

所有组件都继承自vue的原型

例如:在vue原型中增加方法

Vue.prototype.test=function(){
  console.log("test")
}

在任意组件中调用原型方法

    aboutClick(){
      this.test();
      //this.$router.replace('/about');
      this.$router.push({
        path:'/about',
        query: {
          name: 'kobe',
          age: 19
        }

      })
    }

结论:router就是vue原型中属性,又因为所有组件都继承自vue原型,所以所有组件拿到的router都是一样的,和main.js中导入的router是同一个

vue-router的导航守卫

学习url:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
需求:切换路由的时候,显示title
实现:通过钩子函数实现

    export default {
        name: "About",
        created(){
            document.title="About";
        },
    }
    export default {   
        name: "Home",
        created(){
            document.title="首页";
        },
    }

export type 取别名的写法
在这里插入图片描述
在这里插入图片描述

利用meta和beforeEach实现

const routes =[

    {
        path:'/home',
        component:Home,
        meta:{
            title:'home',
        },
        children:[
            {
                path:'/',
                redirect:'/news'
            },
            {
                path:'/news',
                component:News,
            },
            {
                path:'/message',
                component:Message,
            }
        ]
    },
    {
        path:'/about',
        component:About,
        meta:{
            title:'about',
        },
    },

//路由守卫

router.beforeEach((to,from,next)=>{
  next();//必须调用
  document.title=to.matched[0].meta.title;
})
  • to:即将要进入的目标的路由对象
  • from:当前导航即将要离开的路由对象
  • next:调用该方法后,才能进入下一个钩子
  1. beforeEach一般用来做一些进入页面的限制。比如没有登录,就不能进入某些页面,只有登录了之后才有权限查看某些页面。。。说白了就是路由拦截。
  2. afterEach,后置钩子,不需要主动的调用next函数
  3. 全局守卫
const router = new VueRouter({...})
router.beforeEach({to,from,next})=>{
	...
}
  1. 路由的独享守卫
const router = new VueRouter({
	path:'/foo',
	component:foo,
	beforeEnter:(to,from,next)=>{
		...
	}
})
  1. 组件内的守卫
const foo = {
	template:``,
	beforeRouterEnter(to,from,next){
	//再渲染该组件的对应路由被confirm前调用
	//不能获取组件实例
	//因为当守卫执行前,组件实列还没创建
	}
}

keep-alive

  • keep-alive是vue内置的一个组件,可以使被包含的组件保留状态,或避免重复创建和渲染

    • 它们有两个非常重要的属性
    • inculde 字符或正则表达式,只有匹配的组件会被缓存
    • exclude 字符串或正则表达式,任何匹配的组件都不会被缓存
  • activated和deactivated只有在keep-alive存在的情况下才能用

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

 `
 	  //逗号之间不能有空格
	 //Home使Home组件的name
     <keep-alive include = "Home" exclude= "About,User">
     //所有匹配到的视图组件都会被缓存
      <router-view></router-view>
    </keep-alive>
 `

//App.vue

    <keep-alive>
      <router-view></router-view>
    </keep-alive>
    export default {   
        name: "Home",
        created(){
            console.log("home被创建");
        },
        data: function () {
            return {
                path:'/message',

            }
        },
        activated:function(){
            console.log(this.path);
            this.$router.push(this.path);
        },
        //时机太晚,获取到的还是已经被激活的路由路径
        // deactivated:function(){
        //     console.log(this.$route.path);
        // },
        beforeRouteLeave(to,from,next){
            console.log(this.$route.path);
            this.path = this.$route.path;
            next();
        }


    }

案列

TabBar的实现思路
//topbar

<template>
    <div class="topbar">
        <top-bar-item path='/home' activeColor="blue">
            <div slot="item-icon" >img</div>
            <div slot="item-text" >首页</div>
        </top-bar-item>
        <top-bar-item path='/category'>
            <div slot="item-icon" >img</div>
            <div slot="item-text" >分类</div>
        </top-bar-item>
        <top-bar-item path="/cart">
            <div slot="item-icon">img</div>
            <div slot="item-text" >购物车</div>
        </top-bar-item>
        <top-bar-item path="/mine">
            <div slot="item-icon">img</div>
            <div slot="item-text" >我的</div>
        </top-bar-item>


    </div>
</template>


<script>

    import TopBarItem from './TopBarItem'
    export default {   
        name: "TopBar",
        data(){
            return{

            }
        },
        components: {
            TopBarItem,
        },
        computed:{


        }
    }

</script>

<style >
    .topbar{
        display: flex;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;  
        background-color: #f6f6f6;
        height: 49px;
    }

</style>

//topbaritem

<template>
    <div class="topbaritem" @click="itemclick">
        <slot name="item-icon"></slot>
        <div :style="acitveStyle" >
            <slot  name="item-text"></slot>
        </div>
    </div>
</template>

<script>
    export default {
        name: "TopBarItem",
        data(){
            return{
                
            }
        },
        props:
        {
            path:String,
            activeColor:{
                type:String,
                default:'red'
            }
        },
        computed:{
            isActive(){
                console.log("isActive"+this.$route.path)
                var ret = this.$route.path.indexOf(this.path)!=-1;
                return ret;
            },
            acitveStyle(){
                var ret =this.isActive? {color:this.activeColor}:{};
                return ret;
            }
        },
        methods:{
            itemclick() {
                console.log("itemclick"+this.$route.path);
                if (this.path != undefined&&this.$route.path.indexOf(this.path)==-1) {
                    this.$router.replace(this.path)
                }

            }
        }

    }

</script>

<style >
    .topbaritem{
        flex: 1;
        text-align: center;
    }
    .active{
        color: red;
    }
</style>

//router/index.js

import VueRouter from 'vue-router'
import Vue from 'vue'
const Home = ()=>import('../pages/home/Home')
const Cart = ()=>import('../pages/cart/Cart')
const Category = ()=>import('../pages/category/Category')
const Mine = ()=>import('../pages/mine/Mine')


Vue.use(VueRouter)
const routes=[

    {
        path:'/home',
        component:Home,
    },
    {
        path:'/cart',
        component:Cart
    },
    {
        path:'/category',
        component:Category
    },
    {
        path:'/mine',
        component:Mine
    }


]

const router = new VueRouter({
    routes,
    mode:'history',
})

export default router

//App.vue

<template>
  <div id="app">
    <router-view></router-view>
    <TopBar></TopBar>


  </div>
</template>

<script>

import TopBar from './components/topbar/TopBar'



export default {
  name: 'App',
  components: {
    TopBar,
  }
}
</script>

<style>

</style>

//main.js

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


Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router:router,

  render: h => h(App)
})

文件路径引用

修改build/webpack.base.conf.js

  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': resolve('src'),
      'components':resolve('src/components'),
    }
  },

用法:
如果文件是通过import引入,可以直接使用别名

import TopBarItem from 'components/topbar/TopBarItem'

如果文件不是通过import引入,需要在别名前面加一个~符号来使用,否则会找不到文件

<img src="~assets/img/tabbar/home.png" />

补充

关闭丑化

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值