【vue(一))路由】
路由
- 一、前端路由的理解
- 1.SPA单页面应用
- 2.路由
- 二、路由实现本质
- 1.浏览器的两种路由模式
- (1)hash模式
- (2)history模式
- 2.避开浏览器默认行为
- 三、几种跳转的对比
- 1.location.href = '/url'
- 2. history.pushState('/url')
- 3.router.push('/url') -- Vue Router
- 4.对比
- 四、创建路由
- 1.安装 Vue Router
- 2.创建路由配置文件
- 3.在main.js中注册路由
- 4.在App.vue中显示路由组件
- 五、`$route`和`$router` 的区别
- 六、动态路由匹配
- 七、嵌套路由,命名路由,路由重定向
- 八、路由跳转的两种方式
- 九、路由传递参数写法
- 1.params参数
- 2.query参数
- 3.params和query传参的三种写法
- 4.props传递
- 十、路由元信息
- 十一、路由导航守卫
- 1.全局前置守卫
- 十二、路由懒加载
- 1.使用箭头函数+import动态加载
- 2.使用箭头函数+require动态加载
- 3.使用webpack的require.ensure技术
一、前端路由的理解
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就可以监听事件的改变
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模式,就要进行以下配置(后端也要进行配置)
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
//vue2推荐@3.x
//vue3推荐@4.x
npm install vue-router@4
2.创建路由配置文件
- 可以把路由规则放在routers.js文件,到时候在这引入就行
路由插件
:
- 1.全局注册RouterView 和 RouterLink 组件
- 2.添加全局 $router 和 $route 属性
- 3.启用 useRouter() 和 useRoute() 组合式函数
- 4.触发路由器解析初始路由。
// 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中注册路由
// 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 和 $routerrouter,// ✅ 注册 store(如果使用 Vuex)store}).$mount('#app');
4.在App.vue中显示路由组件
使用
<router-view>
显示组件内容
五、$route
和$router
的区别
注册完路由后,不管是
路由组件
还是非路由组件
身上都有这两个属性
项目 | 本质 | 属性 |
---|---|---|
$route | 当前路由的信息对象 | path,params,hash,query,fullPath,matched,name 等路由信息参数 |
$router | 路由实例对象 | 路由的跳转 方法,钩子函数 等 |
六、动态路由匹配
- 指
在定义路由时,允许某些部分是动态变化的
- 大多情况下,我们需要将给定匹配模式的路由映射到同一个组件,例如user组件的渲染会有用户id的不同
import User from './User.vue'// 这些都会传递给 `createRouter`
const routes = [// 动态字段以冒号开始,称之为路径参数{ path: '/users/:id', component: User },
]
- 当一个路由被匹配时,它的 params 的值将在每个组件中以
route.params
的形式暴露出来- 通过更新 User 的模板来呈现当前的用户 ID
<template><div><!-- 当前路由可以通过 $route 在模板中访问 -->User {{ $route.params.id }}</div>
</template>
七、嵌套路由,命名路由,路由重定向
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}]}]}
]
<!-- 使用命名路由跳转 -->
<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反正我有点绕。等用到了再说吧。
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只能namename: 'sousuo',params: { keyword: this.keyword },query: { k: this.keyword.toUpperCase() },})
}
4.props传递
props可以写成布尔值,对象模式,函数模式
{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 }},
//子组件接收
prop:[ 'keyword3','keyword4']
十、路由元信息
- 你可能希望
将任意信息附加到路由上
,如过渡名称
、谁可以访问路由等
。- 这些事情可以通过接收属性对象的
meta属性
来实现,并且它可以在路由地址和导航守卫上都被访问到- 暂时没用到多少,后面用多了再说
一个例子:
//例如在home首页,我希望显示footer组件,但是在其他页面不想显示footer组件,我就可以写meta元信息{path: '/',component: () => import('@/pages/Home'),meta: {isHideFooter: true}},
<!-- 利用路由元信息解决当前问题好处:一行代码就可以解决 --><Footer v-show="$route.meta.isHideFooter" />
十一、路由导航守卫
全局前置/钩子
:beforeEach、beforeResolve、afterEach路由独享的守卫
:beforeEnter组件内的守卫
:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave- 暂时只用过全局前置守卫来进行页面跳转守卫
1.全局前置守卫
// GOOD
router.beforeEach((to, from, next) => {//to:获取到要跳转到的路由信息//from:获取到从哪个路由跳转过来来的信息// next();直接调用,代表直接放行next()
})
十二、路由懒加载
非懒加载
import List from '@/components/list.vue'
const router = new VueRouter({routes: [{ path: '/list', component: List }]
})
1.使用箭头函数+import动态加载
方案一(常用):
const List = () => import('@/components/list.vue')
const router = new VueRouter({routes: [{ path: '/list', component: List }]
})
2.使用箭头函数+require动态加载
const router = new Router({routes: [{path: '/list',component: resolve => require(['@/components/list'], resolve)}]
})
3.使用webpack的require.ensure技术
// r就是resolve
const List = r => require.ensure([], () => r(require('@/components/list')), 'list');
// 路由也是正常的写法 这种是官方推荐的写的 按模块划分懒加载
const router = new Router({routes: [{path: '/list',component: List,name: 'list'}]
}))