vue--router

2023-11-09

什么是vue-router?

网址:起步 | Vue Router

前端路由有哪些?

SPA单页面应用:

实现SPA(single page application)单页面应用:一个项目只有一个页面,我们基于路由,控制页面显示不用的代码片段(或组件),当展示的内容改变的时候,页面并不会刷新! !

  • 移动端大部分都是(追求原生app的操作体验)
  • pc端的管理系统

MPA多页面应用:

MPA(multi page application)多页面应用:一个项目有很多页面,我们做的是页面之间的跳转,每一次跳转都是打开一个新的页面(相当于)页面刷新了)! !

  • pc端的非管理系统的产品
  • 搜索引擎优化:基于JS动态绑定的内容(Vue框架),在页面源代码中是看不到内容的,不利于SEO搜索引擎优化,想做SEO,需要服务器渲染(前后端不分离,现在主流的服务器渲染SSR:node + vue(nuxt.js) + react(next.js))

        1. 首屏服务器渲染 「骨架屏」

        2. 其余屏幕还是交给客户端渲染

单页面&&多页面对比图

前端 路由的两大模式

哈希路由(hash router):监听URL地址后面的HASH值

  • 样式:http://www.xxx/com/index.html#home
  • 原理:改变url的哈希值不会刷新页面,通过指定的哈希值使用window.onhashchange事件来控制更改页面渲染的内容

浏览器路由(browser/history router):基于h5中的history api实现的

  • 样式:http://www.xxx.com/home
  • 缺点:需要服务器的支持:(当手动刷新的时候,这个页面不存在,不要返回404,依然返回主页面内容)
  • 原理:通过history.pushState()方法改变网址,页面不会刷新,再通过window.onpopstate事件监听网址的改变
  • vue脚手架基于webpack-dev-server启动服务(开发环境,已经完成了服务器应对history 模式支持的相关操作:但是打包到服务器上(生产环境),没有webpack-dev-server,此时服务器需要基于nginx做相同的配置支持)

区别

1 hash路由地址不好看,browser路由好看一点

2. 实现机制不同

  • HASH路由每次跳转,修改的是URL的hash值(页面不刷新),基于监听onhashchange事件,获取最新的hash值,去路由规则表中,找到相对应的组件拿过来渲染,
  • browser路由每次跳转修改的是url地址(页面不刷新history.pushState);监听popstate事件根据最新的地址找到相对应的组件渲染
  • browser路由跳转的地址并不真实存在,所以页面手动刷新,会出现404,此时需要服务器的配合「后台:如果访问的地址不存在,返回主页内容」

vue-router使用

第一步: $ npm i vue-router

第二步:创建路由规则表

  • 在src/router文件下创建路由存放的路径以及index.js 和routes.js文件:main.js注册使用路由
  • 挂载之后每个组件都会有this.$router(实现路由跳转)&&this.$route(拿到路由信息)

方案一:写在index.js

import Home from '@/views/Home.vue';
import login from '@/views/login.vue';
import order from '@/views/order.vue';
//创建index.js文件并配置vue-router的模式和路由规则表
//src/router/index.js    :作用是创建vue路由实例,并配置路由
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter)//导入vue vue-router,注册使用
const router = new VueRouter({
    mode: 'hash', //设置路由模式 hash history(默认)
    //设置路由匹配规则[路由表]
    routes: [{
        path: '/', //指定hash值(pathname值)
        component: Home//渲染的组件
    }, {
        path: '/iogin',
        component: login
    }, {
        path: '/order',
        component: order
    }, {
        path: '*',//如果没有匹配项,
        redirect: '/'//重定向到首页(也可以渲染一个404组件)
    }]
});
export default router

 方案二 写在routes.js里

1.把路由表抽离出来 ,放到routes.js里单独管理,并且导出

import Home from '@/views/Home.vue';
import login from '@/views/login.vue';
import order from '@/views/order.vue';
const routes = [{
    path: '/',
    component: Home
}, {
    path: '/iogin',
    component: login
}, {
    path: '/order',
    component: order
}, {
    path: '*',
    redirect: '/'
}]
export default routes

2.在index导入挂载

import routes from './routes.js'
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter)
const router = new VueRouter({
    mode: 'hash',
    routes
});
export default router

第三步:实现路由跳转(改变URL地址 或者哈希值)

注意:每一次的路由切换,都是把上一个渲染的组件释放(销毁beforeDestory-->destroyed) ,把新匹配的组件进行渲染(beforeCreate-->created-->beforeMount-->mounted)

第四步:设置一个容器,可以在指定的位置,把基于路由规则匹配的组件进行渲染

  • 以下的<router-view></router-view>就是我们指定的组件渲染的位置
<template>
  <div id="app">
    <div class="navBox">
      <!-- 内置组件(路由跳转.切换组件),
          点击实现路由跳转,基于"to"属性指定跳转地址
          页面渲染的时候会把router-link渲染为a标签
       -->
      <router-link to="/">首页</router-link>
      <router-link to="/login">登录页</router-link>
      <router-link :to="{ path: '/order' }">订单页</router-link>
    </div>
    <main class="mainBox">
      <!-- 
        内置组件(路由容器),渲染"基于"路由规则匹配的组件 
        当页面刷新或路由跳转后,vue都会拿URL最新地址(或哈希值)去路由表中进行匹配,
        把匹配到的组件,放到router-view容器中渲染    
      -->
      <router-view></router-view>
    </main>
  </div>
</template>

router-link

  • ==router-link 内置组件(路由跳转/路由切换)==
  • 实现页面跳转:点击实现路由跳转,基于“to”属性指定跳转地址
  • 页面渲染的时候,会把router-link渲染为a标签
  <router-link to="/">首页</router-link>
  <router-link to="/analyse">分析页</router-link>
  <router-link :to="{ path: '/order' }">订单页</router-link>

router-view

  • ==router-view 内置组件(路由容器)==:用来渲染“基于路由规则匹配组件的” :
  • 当页面刷新或者路由跳转后,vue都会拿url最新的地址(或者hash值)去路由表中进行匹配,把匹配到的组件,放到router-view容器中渲染
  • 而每一次的路由切换,都是把上一个渲染的组件释放(或销毁 beforeDestroy=>destroyed),把新匹配的组件进行渲染(beforeCreate->created->beforeMount->mounted)
    <main class="main-box">
      <router-view></router-view>
    </main>

 精准匹配/非精准匹配/完全不匹配:

页面刷新或路由切换:都会拿最新的地址(或哈希值)与每一个router-link中to属性的值(或者path属性值)进行匹配;完全“精准”匹配的会给A标签设置:router-link-exact-active router-link-acive 两个样式名;非精准匹配的只设置:router-link-active这个样式类,一点都没匹配的,啥样式都不设置!!=>==我们后期可以基于这个特点,给当前匹配的导航设置选中特殊样式==

 页面地址:/order
  to的地址:
		/ 非精准匹配 (包含一个完整的)
    	/  : 任何地址都包含一个完整的斜杠
     		/home2 VS /home 不算(完全不匹配)
     		/home/list VS /home 算(前面router-link的值的包含后面路由表里的值)
		/analyse    完全不匹配
		/order 精准匹配(一毛一样)
//------------------------------------------------
   根据精准匹配设置样式
    &.router-link-exact-active {
      color: red;
    }

二级路由

二级路由也是路由表的形式,只是里面的路径:

  • 路由会对路由表中祖先级和父级,进行==非精准匹配==,对最低级做==精准匹配==
  • 二级路由可以直接在一级路由表中配置,但这样会显得代码嵌套层数太多,我们可以将二级路由抽出来,放在一个js文件中,再进行导入引用:
import Home from '@/views/Home.vue';
import login from '@/views/login.vue';
import order from '@/views/order.vue';
import hoomeRoutes from './homeRoutes';
// 路由根据路由表时自上而下匹配的,只要有一个匹配就直接跳转,所以path:"*"不能在最前面匹配
const routes = [{
    path: '/',
    redirect: '/home'
}, {
    path: '/home',
    component: Home,
    children: hoomeRoutes,//二级路由
},
{
    path: '/login',
    component: login
}, {
    path: '/order',
    component: order
}, {
    path: '*',
    redirect: '/'
}]
export default routes
-------------------------------------------------------------
  • 如果不以斜杠开头,则表示拼接到父级路由后面
  • 如果以斜杠开头,则表示井号#后面的全部路径,不会拼接父路由路径
//管理home模块的路由表(二级/三级路由)
import CustomList from '../views/Home/CustomList.vue';
import CustomAdd from '../views/Home/CustomAdd.vue';
import VisitList from '../views/Home/VisitList.vue';
const hoomeRoutes = [{
//如果/home后面没有跟路径,则默认将它重定向到'/home/customerlist',也就是让他第一次打开home就在custmerlist页面
    path: '',
    redirect: '/home/customlist'
}, {
    path: 'customlist',//-->/Home/CustomList
    component: CustomList
}, {
    path: 'customadd',
    component: CustomAdd
}, {
    path: 'visitlist',
    component: VisitList
}];
export default hoomeRoutes;

路由懒加载

vue前端性能优化之一:路由懒加载 分割打包按需导入

问题:如果在编写路由表的时候,事先导入了所有组件,根据规则渲染不同的组件,这样最后build打包的时候,会把所有组件全部打包到一个js中,js文件较大,页面第一次渲染请求js时间过长,延长了白屏事件~~~

解决:路由懒加载依托于ES6中提供的import函数

  • 1、==webpack打包的时候实现代码切割==:在路由表中已经导入的组件(一般只导入默认需要展示的,就是第一次访问页面需要展示的),打包到一个js中,其余的组件根据情况,分割为一个或多个js文件!
  • 2、最开加载页面,只把主JS文件导入,当路由匹配了某个规则,需要渲染某个组件,再把这个js文件加载
  • 在路由表中各项的component属性后面用ES6函数:()=>import (/* webpackChunkName: 'analyse' 的形式导入指定路径组件的就是==路由懒加载==
import Home from '@/views/Home.vue';
import hoomeRoutes from './homeRoutes';
const routes = [{
    path: '/',
    redirect: '/home'
}, {
    //首页不做懒加载
    path: '/home',
    component: Home,
    children: hoomeRoutes,//二级路由
},
//其余页面先不加载,匹配之后在加载
{
    path: '/login',
    component: () => import('@/views/login.vue')
}, {
    path: '/order',
    component: () => import('@/views/order.vue')
}, {
    path: '*',
    redirect: '/'
}]
export default routes

按组件打包js,按需导入

  • =问题==:我们每通过()=>import("@/pages/home/VisitList.vue") 的形式使组件路由懒加载,都会形成一个单独的js文件,这样就会导致项目中js文件很多。
  • 在import内,最前面加上一句/* webpackChunkName: 'analyse'*/这样,它以及它子孙组件就会打包成一个js组件,实现了按包分割的功效,减少了js文件数量
{
    path: '/login',
    component: () => import(/*  webpackChunkName: 'login' */'@/views/login.vue')
},

route与router的属性和方法

当我们"new Vue({router})",会给每一个组件(vue实例)注入两个私有属性

  • this.$route:存储路由匹配信息
  • this.$router: VueRoute的一个实例,可以调用VueRoute.prototype上的方法实现路由跳转

$route: 内部做了get劫持,可以监听到路由的变化

  • fullPath完整的路由地址(含问号传参信息)
  • path:不含问号号传参信息
  • query:{}以键值对的方式存储了问号传参信息
  • params:{}存储的是"路径传参"信息,或者是"隐式传参"信息
  • name:路由名字
  • meta:{}存储路由元信息
  • matched:[...]记录路由具体匹配的规则记录
  • hash:存储除hash路由内容外,单独设置的哈希值:#home?xxx#aaa,hash的值就是#aaa

$router:

私有属性方法

  • mode:路由模式
  • options.routes:路由表
  • app:new vue时创建的组件
  • matcher.getRoutes()路由表信息
  • matcher.addRoutes动态添加路由表信息

公有属性方法:

push 跳转到指定路由 (this.$router.push('/login')),等同于<router-link to='/login>

对象写法:可以传参了
this.$router.push({
    push.'/login'
})
  •  replace 也是跳转到指定的路由
  • push是新增一条历史记录.而replace是修改本条记录
  • go(n): 以当前历史记录为标准,前进或者后退, 基于n指定步数,go(-1)返回上一级, go(1)前进一步
  • back: go(-1)
  • forwarp: go(1)
  • addRote addRoutes 动态向路由表中加入新的规则记录(动态路由)
  • getRoutes获取现有的路由表

图解:

路由切换的传参方式

每一次路由切换想把一些信息传递给下一个组件

1.问号传参#/xxx?xxx=xxx[丑&可以在地址栏中看到传递信息]

//AZ组件向B组件传参
//A组件
this.$router.push({
        path:"/B",
        query:{
          lx:1,
          from:"weixin"
        }
      })
//B组件--------------------
  created() {
    console.log(this.$route.query);
  },
//基于this.$route.query获取即可->{lx:1,from:"weixin"}
------------
//因为地址栏中有,即使手动刷新B野蛮,信息还可以获取

2.隐式传参: #/xxx 地址栏中看不见,从内部把信息传递过去&&需要基于路由名字跳转,基于path跳转不可以

建议:后期在编写路由表时,给每一个路由设置一个名字'命名路由',后期实现路由跳转,可以不基于path跳转基于name也可以跳转

//A组件
this.$router.push({
		name:"B"//用name指定要跳转到那个路由
        path:"/B",
        params:{
          lx:1,
          from:"weixin"
        }
      })
 //B组件
 created() {
    console.log(this.$route.params);
  },
//基于this.$route.params获取即可->{lx:1,from:"weixin"}
------------
因为传递的信息在地址栏中不存在,所以在b页面刷新,传递的信息就没有了

3.路径参数:把需要传递的数据,当做理由地址的一部分 例如#/xxx/100[常用]因为不丑

第一步:设置路由表:xxx是设置动态规则

{//这种是不传参数时匹配的customadd路由路径
    name:"home_customadd",
    path: 'customadd',
    component: () => import(/* webpackChunkName: "home" */'@/pages/home/CustomAdd.vue')
}, 
{//这种是需要传参数id和name匹配的路由路径
    name:"home_customadd",
    path: 'customadd/:id/:name',
    component: () => import(/* webpackChunkName: "home" */'@/pages/home/CustomAdd.vue')
}

第二步:实现路由跳转

this.$router.push("/home/customadd/100/lisa");

第三步:获取

this.$router.params()->{id:"100",name:"lisa"};

 案例 客户管理系统新增客户与编辑客户是同一个路由

<template>
  <div class="CustomAdd">{{ title }}</div>
</template>

<script>
export default {
  name: "CustomAdd",
  //新增用户还是修改用户由title决定
  data() {
    return {
      title: "新增客户",
    };
  },
  //释放的上一个组件和即将渲染的下一个组件,是同一个组件,此时这个组件即不会被释放,也不会被重新渲染(也就是第一次渲染的逻辑就不会触发了,created就不会在执行了)
  created() {
    //存储路径传参或者隐式传参信息
    let { id } = this.$route.params;
    if (id) {
      this.title = "修改客户";
    }else {
        this.title = "新增客户";
      }
  },
};
</script>

路由跳转的特殊情况:路由切换的时候,要释放的上一个组件和即将渲染的下一个组件,是同一个组件,此时这个组件即不会被释放,也不会被重新渲染(也就是第一次渲染的逻辑就不会触发了,例如:created就不会在执行了) 

==解决方案一==:基于watch监听路由的变化监听路由的变化,从而做一些事情

  watch: {
    $route() {
      //监控vm实例中的私有$route属性只有改变时执行
      let { id } = this.$route.params;
      if (id) {
        this.title = "修改客户";
      } else {
        this.title = "新增客户";
      }
    },
  },
---------------------------------------
  watch: {
    $route: {
      handler() {
        let { id } = this.$route.params;
        if (id) {
          this.title = "修改客户";
        } else {
          this.title = "新增客户";
        }
      },
      immediate: true,//重新渲染组件(第一次渲染逻辑),让监听器处理
    },
  },

解决方案二:把需要依赖路由变化而变化的的信息,都设置为计算属性

  computed: {
    title() {
      let { id } = this.$route.params;
      return id ? "修改客户" : "新增课户";
    },
  },

VueRouter实例的属性

VueRouter的实例属性就是创建路由时可以添加的属性:

  • name:"名字":==命名路由==,设置路由表时必须加的属性,名字不能重复,后期实现路由跳转可以不基于path,基于name也可以跳转
  • path:"/home":表示在路由表中注册/home路径的路由信息,当请求的hash值或地址为/home时,就会去路由表中精准匹配到/home路径下指定的组件,并进行切换
  • component:[组件]:指定需要切换(渲染)的组件
  • redirect:"/list":将这个路径重定向到path为/list的路径下面
  • children:[数组]:二级路由,也如同组件,是数组形式一般二级路由会单独提出来,再导入
  • meta:{任何类型}:路由元信息:记录每一个路由表的一些基础信息this.$router.meta:获取

路由导航守卫

在每一次页面刷新(或者路由跳转),路由进行规则匹配的时候,也会触发一些钩子函数

A组件----B组件

  • 1.释放A组价,触发A组件的beforeRouterLeave钩子函数(写A在组件中)
  • .-----A组件释放,即将开启B组件的解析和渲染-----
  • 2.触发全局前置守卫函数beforeEach(写在创建路由处)
  • 3.如果A和B是相同组件则会触发组件的beforeRouterUpdate构子函数
  • 4.在触发匹配路由表中的独享守卫beforeEnter【写在路由表中】
  • -----b组件被激活,开始进入到b组件中进行渲染-----
  • 5.触发B组件的beforeRouterEnter的构子函数
  • 6.触发全局解析守卫beforeResolve
  • 7.触发全局后置守卫afterEach

真实项目中,我们最常用的是 全局前置守卫"beforeEach",每一次页面刷新(或路由跳转),也不论从哪跳到哪,beforeEach一定会被触发! !

例如:我们一般会在beforeEach前置守卫中,做登录状态的校验(只有不是进入登录页,都需要在这校验一下用户是否登录,如果登录了,正常进行后面的步骤,如果没有登录,直接让其返回登录页)

 代码

router.beforeEach((to, from, next) => {
    //to:去哪 from:从哪来
    //next:进行下一步[如果next不执行,卡在这结束了 后面啥都不做]
    //next()之前规划咋走则,继续往下走,
    // next('/login')直接走到某个指定的路由
    let title = to.meta.title;
    if (title) document.title = title
    next()
})

面包屑导航

//---路由表中
 {
    name: 'homeCustomlist',
    path: 'customlist',//-->/Home/CustomList
    component: CustomList,
    meta: {
        title: "首页--客户列表",
        nav: ['首页', '客户列表']
    },
},
页面中------
<template>
  <div class="CustomList">
    {{ $route.meta.nav.join("/") }}
  </div>
</template>

扩展

  • 场景切换动画
  • keep-alive:组件缓存
  • scrollBehavior(to,from,savePosition){return {x:0,y:0}}:滚动行为,组件跳转的时候规定组件显示 滚动的位置
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue--router 的相关文章

随机推荐