单页应用程序与路由
SPA - 单页应用程序
- SPA:
Single Page Application
单页面应用程序 - MPA :
Multiple Page Application
多页面应用程序
优势
- 传统的多页面应用程序,每次请求服务器返回的都是一整个完整的页面
- 单页面应用程序只有第一次会加载完整的页面
- 以后每次请求仅仅获取必要的数据,减少了请求体积,加快页面响应速度,降低了对服务器的压力
- SPA更好的用户体验,运行更加流畅
缺点
- 开发成本高 (需要学习路由)
- 不利于 SEO 搜索引擎优化
ssr: server side rendering : 服务端渲染 大前端 nodejs
路由介绍
- 路由 : 是浏览器 URL 中的哈希值( # hash) 与 展示视图内容(组件) 之间的对应规则
- 简单来说,路由就是一套映射规则(一对一的对应规则), 由前端开发人员制定规则.-
- 当 URL 中的哈希值(
#
hash) 发生改变后,路由会根据制定好的规则, 展示对应的视图内容(组件)
- 为什么要学习路由?
- 渐进式 =>vue => vuer-router (管理组件之间的跳转)
- 在 web App 中, 经常会出现通过一个页面来展示和管理整个应用的功能.
- SPA 往往是功能复杂的应用,为了有效管理所有视图内容,前端路由 应运而生.
- vue 中的路由 : 是 hash 和 component 的对应关系, 一个哈希值对应一个组件
vue-router(官方提供)
安装
npm install vue-router@4
具体步骤
实现vue的具体步骤
- 步骤1:引入路由文件,引入vue-router一定要在vue.js后面,vue-router是vue的插件
<script src="./node_modules/vue/dist/vue.global.js"></script>
<script src="./node_modules/vue-router/dist/vue-router.global.js"></script>
- 步骤2:准备路由规则中使用到的组件
const Index = {
template: `
<div>我是首页模块组件</div>
`
}
const User = {
template: `
<div>这是用户模块组件</div>
`
}
- 步骤3: 配置路由的规则,每个路由都需要映射到一个组件
routes=[
// path: 路径
// component: 组件
{path: '/index', component: Index},
{path: '/user', component: User}
]
- 步骤4:创建路由对象,并传递
routes
配置
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes // `routes: routes` 的缩写
})
- 步骤5: 指定vue实例的路由对象,使整个应用支持路由
app.use(router)
- 步骤6: 设置路由的出口 匹配到的组件的显示的位置
<router-view></router-view>
参考示例:
<body>
<div id="app">
<p>hello vue</p>
<!-- 步骤6: 设置路由的出口 匹配到的组件的显示的位置 -->
<router-view></router-view>
</div>
<!-- 步骤1:引入路由文件,引入vue-router一定要在vue.js后面,vue-router是vue的插件-->
<script src="./node_modules/vue/dist/vue.global.js"></script>
<script src="./node_modules/vue-router/dist/vue-router.global.js"></script>
<script>
// 步骤2:准备路由规则中使用到的组件Index,User
// 一个对象其实就可以是一个组件,这个组件默认是用不了
// 注册组件的3种方式:
// 方式1: 全局注册 app.component('index', Index) 组件'
// 方式2: 局部注册 components: {index: Index}
// 方式3: 配置到路由的规则中
const Index = {
template: `
<div>我是首页模块组件</div>
`
}
const User = {
template: `
<div>这是用户模块组件</div>
`
}
// 步骤3: 配置路由的规则
// 路由规则: 每个路由都需要映射到一个组件。
routes = [
// path: 路径
// component: 组件
// redirect: 路由的重定向
{path: '/', redirect: '/index'},
{path: '/index', component: Index},
{path: '/user', component: User}
]
// 步骤4:创建路由对象,并传递 `routes` 配置
// 引入了vue-router.js,全局暴漏了VueRouter
// router 是整个路由对象,一个项目就一个路由对象
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes // `routes: routes` 的缩写
})
const app = Vue.createApp({})
// 步骤5: 指定vue实例的路由对象,使整个应用支持路由
app.use(router)
const vm = app.mount('#app')
window.vm = vm
</script>
</body>
路由导航
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/index">首页</router-link>
<router-link to="/user">用户页</router-link>
导航高亮
- 点击导航 => 元素里添加了两个类
router-link-active
,router-link-exact-active
<a href="#/index" class="router-link-active router-link-exact-active" aria-current="page">首页</a>
<a href="#/user" class="">用户页</a>
- 高亮方式1 : 直接修改类的样式
.router-link-exact-active,
.router-link-active {
color: red;
}
- 高亮方式2 : 使用存在过的类样式 => 修改默认高亮类名
<style>
.highlight {
color: red;
font-size: 30px;
}
</style>
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes,
// 修改默认高亮的a标签的类名
// highlight 是已经存在过的样式
linkActiveClass: 'highlight',
linkExactActiveClass: 'highlight'
})
精确匹配和模糊匹配
- 精确匹配 : router-link-exact-active 类名 : 只有当
浏览器地址栏中的哈希值 与 router-link 的 to 属性值,完全匹配对,才会添加该类
- 模糊匹配: router-link-active 类名 : 只要
浏览器地址栏中的哈希值
包含 router-link 的 to 属性值,就会添加该类名 - 一般同时使用两个类,特别是在路由嵌套时,需要同时匹配一级路由和二级路由。如果某个router-link不想模糊匹配,可以在该router-link上 加个 exact
<router-link to="/" exact>
One
</router-link>
- 注意 : 精确匹配和模糊匹配,只对添加类名这个机制有效,与路由的匹配规则无关!!!
路由重定向
- 解释:将
/
重定向到/index
{ path: '/', redirect: '/index' }
嵌套路由
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样的,URL中各段动态路径也按某种结构对应嵌套的各层组件。
例如,用户模块 /user
中,嵌套着“登录”,“注册” 模块。
- 我们可以在一级路由下,通过属性
children
注册二级路由如下:
{
path: '/user',
component: User,
// 配置二级路由,path前,主要不要'/'
children: [
// 匹配路由 /user/login
{path: 'login', component: Login},
// 匹配路由 /user/register
{path: 'register', component: Register}
]
}
- 在组件中,添加路由出口
const User = {
template: `
<div>这是用户模块组件
<h3> 二级路由:
<router-link to="/user/login">登录</router-link> |
<router-link to="/user/register">注册</router-link>
</h3>
<hr>
<router-view></router-view>
</div>
`
}
参考代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>嵌套路由例子</title>
<style>
.router-link-active, .router-link-exact-active {
color: red;
}
</style>
</head>
<body>
<div id="app">
<h1>一级路由:
<router-link to="/index">首页</router-link>
|
<router-link to="/user">用户页</router-link>
</h1>
<hr>
<!-- 设置路由的出口 -->
<router-view></router-view>
</div>
<!-- 引入路由文件 -->
<script src="./node_modules/vue/dist/vue.global.js"></script>
<script src="./node_modules/vue-router/dist/vue-router.global.js"></script>
<script>
// 准备路由规则中使用到的组件Index,User,Login,Register
const Index = {
template: `
<div>我是首页模块组件</div>
`
}
const User = {
template: `
<div>这是用户模块组件
<h3> 二级路由:
<router-link to="/user/login">登录</router-link> |
<router-link to="/user/register">注册</router-link>
</h3>
<hr>
<router-view></router-view>
</div>
`
}
const Login = {
template: `
<div>这是用户-登录组件</div>
`
}
const Register = {
template: `
<div>这是用户-注册组件</div>
`
}
// 配置路由的规则
routes = [
{path: '/', redirect: '/index'},
{path: '/index', component: Index},
{
path: '/user',
component: User,
// 配置二级路由(某个路由的子路由),path前注意不要'/'
children: [
// 匹配路由 /user/login
{path: 'login', component: Login},
// 匹配路由 /user/register
{path: 'register', component: Register}
]
}
]
// 创建路由对象
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes // `routes: routes` 的缩写
})
const app = Vue.createApp({})
// 指定vue实例的路由对象
app.use(router)
const vm = app.mount('#app')
window.vm = vm
</script>
</body>
</html>
编程式导航
router-link
属于声明式导航,相当于a链接。
但是在使用场景中,有时候需要进行一些逻辑判断,如登录按钮,需动态导航到不同的组件中,这个时候就不适合使用router-link
.
在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push
当你点击 <router-link>
时,这个方法会在内部调用$router.push
,所以说,点击 <router-link :to="...">
等同于调用 this.$router.push(...)
修改上方例子中的User组件内容如下:
const User = {
template: `
<div>这是用户模块组件
<h3> 二级路由:
<button @click="login">登录</button> |
<router-link to="/user/register">注册</router-link>
</h3>
<hr>
<router-view></router-view>
</div>
`,
methods:{
login(){
this.$router.push('/user/login')
}
}
}
动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。
比如:用户详情的展示,用户的id不同,展示的用户内容就不同,但是组件是同一个
/user/1 User
/user/2 User
/user/10086 User
- 动态路由参数的使用
// 创建路由对象,配置路由的规则
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes: [
{path: '/', redirect: '/index'},
{path: '/index', component: Index},
// 动态路径参数 以冒号开头,冒号后面的名称可自定义,这里设置为id,将来在$route.params中对应的key就为id
// 可以匹配: /user/1 /user/2 /user/xxx
// 如果在动态的路由参数中使用了?,表示该路由参数可选,可以匹配 /user
{path: '/user/:id?', component: User}
]
})
- 动态路由参数的获取
// 动态路由参数的获取
const User = {
template: `
<div>
<p>当前用户id为:{{$route.params.id}}</p>
</div>
`,
created() {
// 动态路由参数的获取
console.log('用户id:', this.$route.params.id)
console.log('$route 完整信息:', this.$route)
}
}
响应路由参数的变化
使用带有参数的路由时需要注意的是,当用户从 /user/1 导航到 /user/2 时,相同的组件实例将被重复使用。
因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用
要对同一个组件中参数的变化做出响应的话,你可以简单地 watch $route
对象上的任意属性,如 $route.params
created() {
// 动态路由参数的获取
console.log('用户id:', this.$route.params.id)
console.log('$route 完整信息:', this.$route)
// 响应路由参数的变化
this.$watch(
() => this.$route.params,
(newParams, oldParams) => {
// 对路由变化做出响应...
console.log('watch:当前用户id:', newParams.id)
console.log("watch:newParams:", newParams)
}
)
}
今日学习总结
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。