【vue(一))路由】

路由

一、前端路由的理解

1.SPA单页面应用

(1)在前端技术早期,一个url对应一个页面。

eg:早期博客网站(传统的多页应用MPA

  • 点击首页的一篇文章
  • 浏览器向服务器发送请求
  • 服务器收到请求后,生成一个完整的HTML页面
  • 服务器把这个完整的HTML文件返回给浏览器
  • 浏览器完全刷新页面,丢掉原来的首页内容,加载新的HTML页面

(2)ajax出现,允许在不刷新页面的情况下发请求,随之出现SPA

  • 在SPA初期,在内容切换前后,页面的URL是一样的。
  • 问题一:SPA不会记住你的操作
    • 在博客网站点击帖子内容,但是URL并没有变,点击刷新,又回到首页,刚才的帖子页
  • 问题二:SEO 不够友好
    • 早期爬虫抓取网页内容,但是并不执行js,所以当它们访问SPA时,只能看到那个几乎是空白的HTML文件,而无法获取任何通过JavaScript动态加载的内容。

(3)因此路由出现了

路由的作用就是将浏览器的URL和用户看到的内容绑定起来。当用户浏览不同页面时,URL随之更新,但不需要从服务器重新加载。

2.路由

路由要解决的两个问题

  • (1)问题一
    • 浏览器默认行为:看到URL改变就会向服务器发请求。所以必须避开这种行为。
  • (2)问题二:
    • 感知URL变化,实现"前进/后退":让用户看到URL变化,但不刷新页面,同时记录历史

二、路由实现本质

1.浏览器的两种路由模式

模式 描述 特性
hash模式 依赖 URL 的 哈希部分(# 后面的内容。当哈希值发生变化,触发hashchange事件。前端通过监听该事件,根据哈希值动态渲染页面内容。 兼容所有现代浏览器哈希部分不会被发送到服务器url不美观无法利用浏览器的前进、后退功能进行复杂的路由操作
history模式 基于 HTML5 History API 功能强大需要服务器支持

(1)hash模式

hash模式的主要原理就是onhashchange()事件:

  • 页面hash值发生改变,无需向后端发起请求,window就可以监听事件的改变
js 复制代码
window.onhashchange = function(event){
	console.log(event.oldURL, event.newURL);
	let hash = location.hash.slice(1);
	//location.hash返回当前 URL 中以 # 开头的 hash 部分
	//slice去掉#
}

(2)history模式

history 路由模式的实现,基于 HTML5 提供的 History 全局对象,它的方法有:

  • history模式需要后台配置支持。如果后台没有正确配置,访问时会返回404
  • 如果想要切换到history模式,就要进行以下配置(后端也要进行配置)
js 复制代码
const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

2.避开浏览器默认行为

  • 不使用 <a href> 进行跳转,而是用 JS 控制
  • 用 history.pushState() 替代直接跳转 → 不触发请求
  • 监听页面的 popstate 事件 → 捕捉"前进/后退"

三、几种跳转的对比

1.location.href = '/url'

(1)刷新页面,破坏SPA体验

(2)等同于点击 <a href="/about">,是传统的多页跳转方式

2. history.pushState('/url')

(1)无刷新页面

(2)浏览器提供的原生 API,属于 HTML5 History API 的一部分。

(3)只负责改变URL并更新浏览器的历史记录栈,不会自动处理视图更新

3.router.push('/url') -- Vue Router

(1)无刷新页面

(2)不仅可以改变 URL,还能够基于 URL 自动匹配并渲染相应的组件

(3)提供了诸如导航守卫、懒加载、参数解析等额外功能

4.对比

四、创建路由

1.安装 Vue Router

js 复制代码
//vue2推荐@3.x
//vue3推荐@4.x
npm install vue-router@4

2.创建路由配置文件

  • 可以把路由规则放在routers.js文件,到时候在这引入就行
  • 路由插件
    • 1.全局注册RouterView 和 RouterLink 组件
    • 2.添加全局 router 和 route 属性
    • 3.启用 useRouter() 和 useRoute() 组合式函数
    • 4.触发路由器解析初始路由。
js 复制代码
// src/router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';

// 1. 安装 VueRouter 插件(关键!)
Vue.use(VueRouter);

// 2. 引入需要路由控制的组件
import Home from '@/views/Home.vue';
import About from '@/views/About.vue';
import User from '@/views/User.vue';

// 3. 定义路由规则
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
  {
    path: '/user/:id', // 动态路由参数
    name: 'User',
    component: User,
    props: true // 将参数作为 props 传入组件
  },
  {
    path: '/login',
    redirect: '/auth' // 重定向
  },
  {
    path: '/auth',
    component: () => import('@/views/Auth.vue') // 懒加载(推荐)
  },
  {
    path: '*',
    component: () => import('@/views/NotFound.vue') // 404 页面
  }
];

// 4. 创建路由实例
const router = new VueRouter({
  mode: 'history', // 可选:'hash' 或 'history'
  base: process.env.BASE_URL,
  routes  // 相当于 routes: routes
});

// 5. 导出路由实例
export default router;

3.在main.js中注册路由

js 复制代码
// src/main.js
import Vue from 'vue';
import App from './App.vue';

// ✅ 引入路由
import router from '@/router';

// ✅ 引入并使用 Vuex(可选)
import store from '@/store';

new Vue({
  render: (h) => h(App),
  
  // ✅ 注册路由:让所有组件都能访问 $route 和 $router
  router,
  
  // ✅ 注册 store(如果使用 Vuex)
  store

}).$mount('#app');

4.在App.vue中显示路由组件

使用 <router-view> 显示组件内容

五、$route$router 的区别

注册完路由后,不管是路由组件还是非路由组件身上都有这两个属性

项目 本质 属性
$route 当前路由的信息对象 path,params,hash,query,fullPath,matched,name 等路由信息参数
$router 路由实例对象 路由的跳转方法,钩子函数

六、动态路由匹配

  • 在定义路由时,允许某些部分是动态变化的
  • 大多情况下,我们需要将给定匹配模式的路由映射到同一个组件,例如user组件的渲染会有用户id的不同
js 复制代码
import User from './User.vue'

// 这些都会传递给 `createRouter`
const routes = [
  // 动态字段以冒号开始,称之为路径参数
  { path: '/users/:id', component: User },
]
  • 当一个路由被匹配时,它的 params 的值将在每个组件中以 route.params 的形式暴露出来
  • 通过更新 User 的模板来呈现当前的用户 ID
html 复制代码
<template>
  <div>
    <!-- 当前路由可以通过 $route 在模板中访问 -->
    User {{ $route.params.id }}
  </div>
</template>

七、嵌套路由,命名路由,路由重定向

js 复制代码
const routes = [
  // 重定向:根路径跳转到首页
  { path: '/', redirect: { name: 'home' } },

  // 命名路由 + 嵌套路由
  {
    path: '/admin',
    name: 'admin',
    component: AdminLayout,
    children: [
      {
        path: 'user',
        name: 'admin-user',
        component: UserList,
        children: [
          {
            path: 'add',
            name: 'admin-user-add',
            component: UserAdd
          }
        ]
      }
    ]
  }
]
js 复制代码
<!-- 使用命名路由跳转 -->
<router-link :to="{ name: 'admin-user-add' }">新增用户</router-link>

八、路由跳转的两种方式

  • 声明式导航 router-link
  • 编程式导航 push | replace
  • 声明式导航能做的,编程式导航都能做;但是编程式导航除了能进行路由跳转,还可以做其他业务逻辑。

九、路由传递参数写法

1.params参数

  • 路径参数(是URL的一部分)
  • 在配置路由时需要占位
  • 给params后面加上代表参数可传可不传
  • 携带params参数时,使用to的对象写法只能用name配置!,不能用path配置

2.query参数

  • 不属于路径的一部分,
  • /home?k=v&k=v,配置路由的时候不需要占位

3.params和query传参的三种写法

使用router.push()

这里我感觉少用<router-link :to=''>传参吧,可以用它进行单纯的路由跳转?反正传参emmmm反正我有点绕。等用到了再说吧。

js 复制代码
goSearch() {
    //搜索按钮的回调函数,需要向search路由跳转并传参给search
    //第一种传参方式:字符串
    // this.$router.push('/search/' + this.keyword + '?k=' + this.keyword.toUpperCase());
    //第二种传参方式:模板字符串
    // this.$router.push(`/search/${this.keyword}?k=${this.keyword.toUpperCase()}`);
    //第三种传参方式:对象写法
    this.$router.push({
        // path: '/search',  和params一起时不能用path只能name
        name: 'sousuo',
        params: { keyword: this.keyword },
        query: { k: this.keyword.toUpperCase() },
    })
}

4.props传递

props可以写成布尔值,对象模式,函数模式

js 复制代码
    {
    name: 'search',  // 是当前路由的标识名称
    path: '/search/:keyword?',
    component: Search,
    // 将params参数和query参数映射成属性传入路由组件
    //写法一:布尔值,只能传递prams参数
   // props:true
   //写法二:对象写法-->死数据(额外给路由组件传递一些props)
   //props:{a:1,b:2}
   //写法三:函数 可以传params和query参数
    props: ($route)=> {
    return { 
    keyword3: $route.params.keyword, 
    keyword4: $route.query.keyword2 }
  },
js 复制代码
//子组件接收
prop:[ 'keyword3','keyword4']

十、路由元信息

  • 你可能希望将任意信息附加到路由上,如过渡名称谁可以访问路由等
  • 这些事情可以通过接收属性对象的meta属性来实现,并且它可以在路由地址和导航守卫上都被访问到
  • 暂时没用到多少,后面用多了再说

一个例子:

js 复制代码
//例如在home首页,我希望显示footer组件,但是在其他页面不想显示footer组件,我就可以写meta元信息
  {
    path: '/',
    component: () => import('@/pages/Home'),
    meta: {
      isHideFooter: true
    }
  },
js 复制代码
    <!-- 利用路由元信息解决当前问题好处:一行代码就可以解决 -->
    <Footer v-show="$route.meta.isHideFooter" />

十一、路由导航守卫

  • 全局前置/钩子:beforeEach、beforeResolve、afterEach
  • 路由独享的守卫:beforeEnter
  • 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
  • 暂时只用过全局前置守卫来进行页面跳转守卫

1.全局前置守卫

js 复制代码
// GOOD
router.beforeEach((to, from, next) => {
  //to:获取到要跳转到的路由信息
  //from:获取到从哪个路由跳转过来来的信息
  // next();直接调用,代表直接放行
  next()
})

十二、路由懒加载

非懒加载

js 复制代码
import List from '@/components/list.vue'
const router = new VueRouter({
  routes: [
    { path: '/list', component: List }
  ]
})

1.使用箭头函数+import动态加载

方案一(常用):

js 复制代码
const List = () => import('@/components/list.vue')
const router = new VueRouter({
  routes: [
    { path: '/list', component: List }
  ]
})

2.使用箭头函数+require动态加载

js 复制代码
const router = new Router({
  routes: [
   {
     path: '/list',
     component: resolve => require(['@/components/list'], resolve)
   }
  ]
})

3.使用webpack的require.ensure技术

js 复制代码
// r就是resolve
const List = r => require.ensure([], () => r(require('@/components/list')), 'list');
// 路由也是正常的写法  这种是官方推荐的写的 按模块划分懒加载 
const router = new Router({
  routes: [
  {
    path: '/list',
    component: List,
    name: 'list'
  }
 ]
}))
相关推荐
小小愿望几秒前
解锁前端新技能:让JavaScript与CSS变量共舞
前端·javascript·css
程序员鱼皮3 分钟前
爆肝2月,我的 AI 代码生成平台上线了!
java·前端·编程·软件开发·项目
天生我材必有用_吴用16 分钟前
一文搞懂 useDark:Vue 项目中实现深色模式的正确姿势
前端·vue.js
二闹21 分钟前
JS调用高德地图标注地点-简单呐
前端·javascript
鴆川傲23 分钟前
web前端第二次作业
前端·javascript·css
前端老鹰24 分钟前
HTML <link rel=“preload“>:提前加载关键资源的性能优化利器
前端·性能优化·html
用户849137175471627 分钟前
Access Token + Refresh Token 全解析:前后端分离架构的认证与安全方案
vue.js·spring boot·架构
ruokkk44 分钟前
一个困扰我多年的Session超时Bug,被我的新AI搭档半天搞定了
javascript·后端·架构
sTone8737544 分钟前
QuickJS 的核心概念和核心 API
前端·c++
coding随想1 小时前
揭秘前端开发的隐藏武器:DOM元素尺寸全解析!掌握这三大“尺子”,轻松征服响应式布局
前端