Vue是一个单页面应用程序框架,所有功能都在一个html页面上。内容根据需求按需加载组件,提升性能和开发效率。但是也带来了比较差的SEO(无法根据具体内容提供个性化的SEO)。
问题1 : Vue又是如何做到 按需加载 更新页面的呢?
首先,要确定的就是 组件 与 访问路径 之间的对应关系。
问题2 : 组件 与 访问路径 的对应关系是如何确定的呢?
这就要来看看我们今天的主题 路由 了。
路由
- 介绍
路径 与 组件 的 映射 关系。根据这个 映射关系,就可以知道访问的路径 应该 渲染 哪个组件。
- 作用
修改 地址栏路径时,切换显示匹配的组件。
本文我们由浅入深,先从VueRouter开始着手,了解路由,掌握VueRouter的基本使用。
VueRouter
- 使用
- 下载
VueRouter模板到项目中。例如:3.6.5版本
javascript
npm i vue-router@3.6.5
2、引入 + 注册 + 创建路由对象
javascript
// router/index.js文件
import Vue from 'vue'
import Router from 'vue-router' // 引入
Vue.use(Router) // 注册
const router = new Router()
3、注入,将路由对象注入到 new Vue 实例中,建立关联
javascript
import router from './router'
new Vue({
el: '#app',
router,
render: h => h(App)
})
4、创建需要的组件(views 目录),配置路由规则
javascript
const router = new Router({
routes: [
{
path: '/publish-manage',
component: Layout,
redirect: '/publish-manage/wechat', // 路由重定向
meta: { title: '发布管理', icon: 'guide' },
alwaysShow: true,
children: [
{
path: 'wechat', // 地址栏的路径
name: 'wechat',
component: () => import('@/views/publish-manage/wechat-graphic/index'), // 异步引入组件
meta: { title: '微信图文管理' }
},
{
path: 'weibo',
name: 'weibo',
component: () => import('@/views/publish-manage/weibo/index'),
meta: { title: '微博发布' }
},
]
}
]
})
- 路由模块:将路由按模块拆分,利于维护。
绝对路径:
@指代src目录,可以用于快速引入组件。而不是一层一层./../../的形式往上查找。
-
路由导航
- 声明式导航 ------ 导航链接
vue-router提供一个全局组件router-link,用于替换a标签-
router-link语法<router-link to='/路径值' />必须传入
to属性 来 指定路由路径值 -
router-link高亮 自动给当前导航添加 两个类名。router-link-active模糊匹配(一般用的多)router-link-exact-active精准匹配 -
跳转传参
1、传参数:
to='/path?参数名=值'2、接收参数:
$route.query.参数名 -
动态路由传参
1、配置动态路由
javascript{ path: '/search/:words?', // ? 表示参数可选 component: Search }2、配置导航链接:
to='/path/参数值'3、接收参数:
$route.params.参数名
-
路由
重定向 redirect
说明:
重定向 → 匹配path后,强制跳转 path 路径 到 redirect 指向的路径
语法:
javascript
{
path: 匹配路径,
redirect: 重定向到的路径
}
- 路由 404
作用:当路径找不到匹配时,给个提示页面。
位置:配在路由最后
语法 :path: '*'
- 表示 任意路径,当前面的路由都没有匹配到时,命中这个。从而展示给用户
自定义的404页面
路由模式
通过 hash 和 history 两种方式实现前端路由,更新视图但不重新请求页面 是前端路由原理的核心之一。
-
路由的两种模式
hash路由 :本质是利用URL中的hash,路径中携带#
原理: 监听路由变化(
onhashchange事件),只有#后面的地址发生变化,可以在windows对象上监听这个事件:javascriptwindow.onhashchange = function (e) { let hash = location.hash; // 通过location对象获取hash地址,地址从#开始 } // onhashchange 事件在当前url的锚部门(以#开始)因为
hash发生变化的url都会被浏览器记录下来,从而浏览器的前进后退都可以使用。hashchange只能改变#后面的url片段history路由
history api给了前端完全的路由,history api可以分为两大部分:切换和修改注意: 在
history模式下,可以自由的修改path,但是history模式最终的路由都体现在url的pathName中,这部分是会传到服务器的,因此需要服务端对每一个可能的path值都作相应的映射。当刷新时,如果服务端没有相应的响应或者资源,页面就会4041、
切换历史状态:包括back、forward、go三个方法javascripthistory.go(-2); // 后退两次 history.go(2); // 前进两次 histor.back(); // 后退 history.forward(); // 前进2、
修改历史状态:包括pushState、replaceState两个方法, 这两个方法接受三个参数:stateObj,title,url。- 两种路由模式的区别
1、前面的
hashchange只能修改#后面的Url 片段,而history,可以通过pushState或者replaceState设置与当前Url同源的任意Url。2、
history模式会将Url修改的和正常请求后端的Url一样,如果后端没有配置对应的路由处理,则会返回404错误,当用户刷新页面时,浏览器会向服务器发送请求,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。
路由跳转
有两种语法:path路径跳转 和 name命名路由跳转
javascript
// 不带参数跳转
this.$router.push('路由路径')
this.$router.push({ path: '路由路径' })
this.$router.push({ name: '路由名' }) // 适合path路径较长的场景
// 例如:路由配置 -> { name: '路由名', path: '路由路径', component: 组件 }
// 带参数跳转
//`path路径`跳转 通过 `query传参`
this.$router.push('路由路径?参数1=参数值1&参数2=参数值2')
this.$router.push({
path: '/路由路径',
query: {
参数1: 参数值1,
参数2: 参数值2
}
})
//取参:
模板中 -> `$route.query.参数名`
script行为中 -> `this.$route.query.参数名`
//`动态路由`跳转传参
this.$router.push('/路由路径/参数值')
this.$router.push({ path: '/路由路径/参数值' })
// 取参:
模板中 -> `$route.params.参数名`
script行为中 -> `this.$route.params.参数名`
//`name命名路由` 跳转传参
this.$router.push({
path: '路由名',
query: {
参数1: 参数值1,
参数2: 参数值2
}
})
// 取参:
模板中 -> `$route.query.参数名`
script行为中 -> `this.$route.query.参数名`
this.$router.push({ // 只有 name命名路由 跳转有
path: '路由名',
params: {
参数1: 参数值1,
参数2: 参数值2
}
})
// 取参:
模板中 -> `$route.params.参数名`
script行为中 -> `this.$route.params.参数名`
this.$router.replace() 路由替换,与 this.$router.push 用法一致。
区别:
this.$router.push():跳转到指定url路径,并向history栈中添加一个记录,点击回退会返回到上一个页面。
this.$router.replace():跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面(就是直接替换了的页面)
this.$router.go(n) : 向前或向后跳转n个页面,n可为正整数或负整数。正整数 前进,负整数后退
组件缓存keep-alive
问题: 从列表到详情,又返回列表,数据重新加载 → 希望回到原来的位置,不重新加载数据
原因: 路由跳转后,列表组件销毁了,返回回来组件又重建了,所以数据重新加载了。
解决方案: 利用keep-alive将组件缓存下来
1、keep-alive是什么?
keep-alive是 Vue内置组件,当它 包裹 动态组件 时,会 缓存不活动的组件实例,而不是销毁它们。
keep-alive它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。
2、keep-alive优点
在组件切换的过程中,把切换出去的组件保留在内存中,防止重新渲染DOM,可以减少加载时间喝及性能消耗,提高用户体验性。
3、keep-alive原理
在 created 函数调用时将需要缓存的 VNode 节点保存在 this.cache 中/在 render(页面渲染) 时,如果 VNode 的 name 符合缓存条件(可以用 include 以及 exclude 控制),则会从 this.cache 中取出之前缓存的 VNode 进行渲染。
VNode:虚拟DOM,其实就是一个JS对象
4、keep-alive使用
| 参数名 | 值 | 描述 |
|---|---|---|
| include | 字符串或正则表达式 | 只有名称匹配的组件才会被缓存 |
| exclude | 字符串或正则表达式 | 任何名称匹配的组件都不会被缓存 |
| max | 数字 | 最多可以缓存多少组件实例 |
include/exclude值 是组件中的 name 命名,而不是 路由名
javascript
// 缓存 组件名 为 test 的组件
<keep-alive include='test'>
<router-view />
</keep-alive>
// 缓存 组件名 为 a 或 b 的组件
<keep-alive include='a, b'>
<router-view />
</keep-alive>
// 使用正则表达式, 需要用v-bind
<keep-alive :include='/a|b/'>
<router-view />
</keep-alive>
// 动态判断
<keep-alive :include='/a|b/'>
<router-view />
</keep-alive>
// 与 transition 一起使用
<transition>
<keep-alive :include='/a|b/'>
<router-view />
</keep-alive>
</transition>
// 使用数组, 需要用v-bind
<keep-alive :include='["a", "b"]'>
<router-view />
</keep-alive>
5、keep-alive触发的生命周期
activated当组件被激活(使用)的时候触发 → 进入这个页面的时候触发deactivated当组件不被使用的时候触发 → 离开这个页面的时候触发
组件缓存后,就不会执行
created、mounted、destroyed等钩子函数
不同情况钩子函数执行情况:
javascript
不同情况钩子函数执行情况:
// 不使用 keep-alive
`beforeRouteEnter` → `created` → `mounted` → `destroyed`
// 使用 keep-alive
`beforeRouteEnter` → `created` → `mounted` → `activated` → `deactivated`
// 再次进入缓存的页面,
只会触发
`beforeRouteEnter` → `activated` → `deactivated` 。`created` 和`mounted`不会再执行。