Vue基础-Day05
前端路由
SPA介绍
spa 是 single page application 简写,意思是单页面应用程序。vue适合开发spa类型的项目。 单页面应用体验
优点:
- 业务场景的切换,性能很好。
- 集中维护一个网站的功能。
- 完全的前后端分离(前后端可以并行开发,提供系统开发效率)
缺点:
- 所有的功能集中的一个页面,依赖的资源是非常多的,加载第一次的时候很慢(首屏加载)。
- 业务复杂度很高(解决方案:vue组件,前端路由)
前端路由介绍
目标:熟悉前端路由的概念
前端路由:根据不同的url地址,页面上展示不同的内容(根据url地址的不同分发到不同的组件。)
注意:浏览的历史支持浏览器历史回退和前进按钮操作(Ajax本身的局部更新是不支持) 前端路由模式解决了SPA应用开发的复杂性
前端路由原理
目标:基于哈希hash的路由原理(通过地址栏hash字符串的改变,去更新页面的内容。)
- http://localhost:3000?username=lisi#abc
- http://localhost:3000?username=lisi#hello
- 锚点(hash)作用:跳转到网页的某一个位置
- 锚点的变化是否会导致浏览器重新向服务器发送一个请求?不会
<!-- 菜单 -->
<div>
<a href="#/">发现音乐</a>
<a href="#/my">我的音乐</a>
<a href="#/friend">朋友</a>
</div>
<div id="content">
<!-- 内容 -->
</div>
<!-- 前端路由:根据hash值得改变,去更新页面内容。-->
<!--
1. 监听hash值得改变
- window.onhashchange = function() { console.log('ok') }
2. 改变后渲染对应内容
- 获取hash location.hash 然后去判断 渲染不同内容
-->
<script>
// 内容容器
const content = document.querySelector('#content')
// 监听hash改变
window.onhashchange = function () {
// 改变后的hash
const hash = location.hash
// path 值可能是:/ /my /friend
const path = hash.replace('#', '')
// 判断路径
switch (path) {
case '/':
content.innerHTML = '发现音乐的网页内容'
break;
case '/my':
content.innerHTML = '我的音乐的网页内容'
break;
case '/friend':
content.innerHTML = '朋友F的网页内容'
break;
default:
break;
}
}
</script>
总结:
- url地址的hash的变化不会导致浏览器发送请求
- hash的变化会触发事件函数onhashchange
vue-router
基本介绍
目标:熟悉vue-router基本信息
vue-router是基于vue的js插件,实现了前端路由功能。
下载地址:https://unpkg.com/vue-router/dist/vue-router.js
基本使用
目标:基于url地址的变化实现组件的切换
- 准备组件配置对象
- 定义路由映射规则(什么路径对应什么组件)
- 初始化 vue-router 实例
- 需要把路由实例挂载到vue中
- 路由跳转链接
<router-link />
- 路由显示组件的位置
<router-view />
<div id="app">
<!-- 菜单 -->
<div>
<!-- 5. 路由连接组件 -->
<router-link to="/">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/friend">朋友</router-link>
</div>
<div>
<!-- 内容 -->
<!-- 6. 显示当前路由规则对应的组件 -->
<router-view></router-view>
</div>
</div>
<script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 1. 准备组件配置对象
const Home = { template: `<div>发现音乐COMPONENT</div>` }
const My = { template: `<div>我的音乐COMPONENT</div>` }
const Friend = { template: `<div>朋友COMPONENT</div>` }
// 2. 定义路由规则(vue-router提供)
const routes = [
// path 是路径 component 对应的组件配置对象
{ path: '/', component: Home },
{ path: '/my', component: My },
{ path: '/friend', component: Friend }
]
// 3. 路由初始化
// const router = new VueRouter({
// // 传入路由规则
// routes: routes
// })
const router = new VueRouter({ routes })
// 根实例
new Vue({
el: '#app',
// 4. 在vue的根实例下进行 路由实例 挂载
router
})
</script>
总结:点击不同的按钮显示不同的组件
- 按钮由vue-router提供
- 组件显示到哪里由控制
- 路由按钮的跳转路径和组件的关系由谁维护?路由映射
- 要想这些配置生效,需要把router对象挂载到Vue实例中
动态路由基本使用
目标:熟悉动态路由的用法
动态路由:根据不同的URL地址,映射到同一个组件,这样做主要是为了节省组件(简化代码)
<div id="app">
<router-view></router-view>
</div>
<script src="./vue.js"></script>
<script src="./vue-router.js"></script>
<script>
// 用户列表组件配置对象
const UserList = {
template: `
<ul>
<li><router-link to="/user/1001">tom</router-link></li>
<li><router-link to="/user/1002">tony</router-link></li>
<li><router-link to="/user/1003">lucy</router-link></li>
</ul>
`
}
// 用户详情组件配置对象
const UserDetail = {
template: `<div> 用户详情组件 用户的ID是:{{$route.params.id}}</div>`
// 模板获取路径传参:$route.params.id
// 组件的函数中获取:this.$route.params.id
}
const routes = [
// 动态路由
{ path: '/user/:id', component: UserDetail }
]
const router = new VueRouter({ routes })
// 根实例
new Vue({
el: '#app',
router
})
</script>
总结:
- 路由映射规则
{ path: '/user/:id', component: UserDetail }
- 获取路径传参:
{{$route.params.id}}
<li><router-link to="/user/1001">tom</router-link></li>
动态路由的问题和解决方案
问题:动态路由的组件默认会进行复用,从而导致生命周期不会重新触发,所以需要通过watch侦听$route的变化。从而得到动态路由参数的最新值
<body>
<div id="app">
<!-- 2、准备点击按钮 -->
<!-- to表示要跳转到哪个路径 -->
<router-link to='/users/123'>张三</router-link>
<router-link to='/users/456'>李四</router-link>
<router-link to='/users/789'>王五</router-link>
<!-- 3、需要告诉前端路由组件显示在哪里 -->
<hr>
<!-- 路由组件填充位置 -->
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// vue-router基本使用
// 前端路由要解决的问题:不同的url地址(点击不同按钮)显示不同的组件
// 1、定义基本的组件结果
const UserScore = {
data() {
return {
// 当前用户的id
currentId: -1,
list: [{
id: 123,
uname: '张三',
score: 100
}, {
id: 456,
uname: '李四',
score: 99
}, {
id: 789,
uname: '王五',
score: 98
}]
}
},
template: `
<div>
<!-- 获取动态路由参数的id -->
<!-- <div>学生的成绩{{$route.params.id}}</div> -->
<div>学生的成绩{{currentId}}</div>
<div>学生姓名:{{userDetail.uname}}</div>
<div>学生成绩:{{userDetail.score}}</div>
</div>
`,
computed: {
// 根据list和currentId计算当前用户的信息
userDetail() {
const info = this.list.find((item) => {
// console.log(item.id, this.currentId)
return item.id === parseInt(this.currentId)
})
return info ? info : {}
// if (info) {
// return info
// } else {
// return {}
// }
}
},
watch: {
$route(to, from) {
// to 要跳转到哪里去
// from 从哪里跳过来
this.currentId = to.params.id
}
}
// created() {
// console.log(this.$route.params.id)
// }
}
// 4、点击按钮时,如何根据跳转的路径找到对应的组件呢?需要配置路由映射(路由的路径和组件的关联关系)
const routes = [
// path表示路由的路径
// component表示对应的组件
{ path: '/users/:id', component: UserScore },
]
// 使上述路由映射生效
const router = new VueRouter({
routes: routes
})
// 5、把路由实例对象挂载到Vue实例中(把vue-router和Vue关联到一块)
new Vue({
router: router,
el: '#app',
data: {}
})
</script>
</body>
总结:
- 动态路由参数的获取
- 动态路由参数的更新
- 侦听器用法
- 计算属性用法
注意:动态路由可以把多个路由链接映射到同一个组件,这样可以节省很多组件代码。
- 如何保证侦听器第一次就触发?
watch: {
// $route(to) {
// console.log('--------------')
// // to.params.id 和 this.$route.params.id 类似
// this.userId = to.params.id
// }
// -------------------------------------------
// 默认情况下,侦听器首次加载时不会触发
// 如果希望首次就触发,那么需要按照如下的方式进行配置
$route: {
// 触发的函数
handler: 'handleUserId',
// 控制首次触发
immediate: true
}
}
总结:immediate: true用于控制侦听器的首次触发函数。
路由参数传递方式
目标:熟悉路由的参数传递的props方式
- props值设置为true
const routes = [
{ path: '/users/:id', component: UserInfo, props: true }
]
// 如下的组对路由的API有依赖
const UserInfo = {
// 获取路由参数
props: ['id'],
template: `
<div>
<div>用户信息{{id}}</div>
<user-msg></user-msg>
</div>
`,
components: {
UserMsg
}
}
如果配置路由映射时,props值设置为true,那么在路由组件中可以直接通过
props:['参数名称']
方式获取参数。
- props值设置为对象
const routes = [
{
path: '/users/:id',
component: UserInfo,
// props的值设置为对象形式,那么组件中可以直接获取对象里面的信息
// 这种方式传递参数一般用于向组件传入固定的数据。
props: {
info: 'hello',
msg: 'nihao',
pi: 3.14
}
}
]
// 如下的组对路由的API有依赖const UserInfo = { // 获取路由参数 props: ['info', 'msg', 'pi'], template: ` <div> <div>用户信息{{info}}{{msg}}{{pi}}</div> <user-msg></user-msg> </div> `, components: { UserMsg }}
总结:
- props值设置为对象后,组件中可以通过props直接获取对象中所有的属性值
- 应用场景:向组件传入固定的数据
- props值设置为函数
const routes = [ { path: '/users/:id', component: UserInfo, // props的值设置为对象形式,那么组件中可以直接获取对象里面的信息 // 这种方式传递参数一般用于向组件传入固定的数据。 props: (route) => { // 这里的形参route其实就是之前所使用的$route // 函数中return的对象中的属性会直接提供路由组件使用 // 基于函数的方式既可以处理动态参数也可以同时处理静态数据 return { uid: route.params.id, pi: 3.14 } } }]
// 如下的组对路由的API有依赖const UserInfo = { // 获取路由参数 props: ['uid', 'pi'], template: ` <div> <div>用户信息{{uid}}{{pi}}</div> <user-msg></user-msg> </div> `, components: { UserMsg }}
总结:
- props值为true表示可以通过props处理路由的动态参数
- props值为对象表示可以通过props处理路由静态的数据
- props值为函数表示既可以处理路由的动态参数也可以处理静态参数
- 动态路由参数可以有多个
const routes = [ // 动态路由的路径格式是自定义的 // 并且动态参数可以有多个(由实际的需求决定) { path: '/users/:id/articles/:aid', component: UserInfo, props: true }]
<router-link to='/users/789/articles/222'>张三</router-link>
总结:动态路由的路径自定义,可以有多个动态路由参数,具体由实际需求决定。
路由重定向
目标:熟悉路由重定向的配置方式
路由重定向:访问某一个路径,自动跳转到另外一个路径。
<div id="app"> <!-- 打开页面:默认访问的路由地址 `/` --> <!-- 我们的 '/home' 才是首页路由的路径 --> <!-- 当默认打开页面的时候 重定向到 首页 --> <router-view></router-view></div><script src="./vue.js"></script><script src="./vue-router.js"></script><script> const router = new VueRouter({ // 指定路由规则 routes: [ // redirect 重定向的路由地址 { path: '/', redirect: '/home' }, { path: '/home', component: { template: `<h1>首页</h1>` } } ] }) new Vue({ el: '#app', router })</script>
总结:访问A路径自动跳转到B路径(我们希望两个路径显示同一个组件)
编程式导航
目标:熟悉编程式导航实现路由跳转的方式
编程式导航:通过js的方式触发路由的跳转。
<body> <div id="app"> <!-- 只有一个路由填充位 --> <router-view></router-view> </div> <script src="lib/vue.js"></script> <script src="lib/vue-router.js"></script> <script> // vue-router 编程式导航:使用js方法实现路由跳转 const Login = { template: ` <div> <div>登录功能</div> <hr> <div> 用户名:<input type="text"> </div> <div> 密码:<input type="password"> </div> <div> <button @click='handleLogin'>登录</button> </div> </div> `, methods: { handleLogin() { // 实现登录功能 // 获取表单数据,调用接口传递参数验证用户信息是否正确 // 假设如下的ret是登录接口返回的结果,status值为1表示登录成功 const ret = { status: 1 } if (ret.status === 1) { // 登录成功,跳转到主页面(切换路由组件) // 如下的this.$router由vue-router提供 // push方法用于实现路由跳转,参数表示路由的路径 this.$router.push('/home') } } } } const Home = { template: ` <div> 主页 </div> ` } const routes = [ { path: '/', redirect: '/login' }, { path: '/login', component: Login }, { path: '/home', component: Home } ] const router = new VueRouter({ routes }) new Vue({ router, el: '#app', data: {} }) </script></body>
总结:编程式导航就是通过js方式控制路由的跳转
$route
获取路由相关信息(路由传参 this.$route.params)$router
提供路由相关函数 ( 跳转方法 this.$router.push() )
嵌套路由
目标:熟悉嵌套路由的用法
要进行路由的嵌套,只需要在一级路由规则下,加上一个属性 children,即可定义二级路由规则。
<body>
<div id="app">
<!-- 只有一个路由填充位 -->
<router-view></router-view>
</div>
<script src="lib/vue.js"></script>
<script src="lib/vue-router.js"></script>
<script>
// vue-router 嵌套路由:在路由组件中再次配置路由填充位
const Login = {
template: `
<div>
<div>登录功能</div>
<hr>
<div>
用户名:<input type="text">
</div>
<div>
密码:<input type="password">
</div>
<div>
<button @click='handleLogin'>登录</button>
</div>
</div>
`,
methods: {
handleLogin() {
// 实现登录功能
// 获取表单数据,调用接口传递参数验证用户信息是否正确
// 假设如下的ret是登录接口返回的结果,status值为1表示登录成功
const ret = {
status: 1
}
if (ret.status === 1) {
// 登录成功,跳转到主页面(切换路由组件)
// 如下的this.$router由vue-router提供
// push方法用于实现路由跳转,参数表示路由的路径
this.$router.push('/home')
}
}
}
}
const Home = {
template: `
<div>
<div>主页内容</div>
<hr>
<router-link to='/home/tech'>科技</router-link>
<router-link to='/home/edu'>教育</router-link>
<router-link to='/home/jinrong'>金融</router-link>
<hr>
<router-view></router-view>
</div>
`
}
const Tech = {
template: '<div>Tech</div>'
}
const Edu = {
template: '<div>Edu</div>'
}
const Jinrong = {
template: '<div>Jinrong</div>'
}
const routes = [
{ path: '/', redirect: '/login' },
{ path: '/login', component: Login },
{
path: '/home',
component: Home,
// 这里可以通过重定向方式控制二级路由组件的显示
redirect: '/home/tech',
// 通过children属性配置嵌套路由
children: [
{ path: 'tech', component: Tech },
{ path: 'edu', component: Edu },
{ path: 'jinrong', component: Jinrong }
]
}
]
const router = new VueRouter({
routes
})
new Vue({
router,
el: '#app',
data: {}
})
</script>
</body>
总结:嵌套路由,在路由组件中再次配置路由填充位,支持多级嵌套
- 二级路由的路径不要添加斜杠
- 一级路由的路径需要添加斜杠
- 二级路由的映射也支持重定向