服务端路由
服务端路由时指的是服务器根据用户访问的 URL 路径返回不同的响应结果。
在传统的服务端渲染的 web 应用中点击一个链接时,浏览器会从服务端获得全新的 HTML页面,然后重新加载整个页面。
然而,在单页面应用中,客户端的 JavaScript 可以拦截页面的跳转请求,动态获取新的数据,无需重新加载的情况下更新当前页面。
这样通常可以带来更顺滑的用户体验,尤其是在更偏向“应用”的场景下,因为这类场景下用户通常会在很长的一段时间中做出多次交互。
在这类单页应用中,“路由”是在客户端执行的。一个客户端路由器的职责就是利用诸如 History API 或是 hashchange 事件这样的浏览器 API 来管理应用当前应该渲染的视图。
路由的应用场景:
单页面应用(SPA应用):
- 顾名思义导航栏不变,内容栏改变的应用。
- 内容栏根据导航栏的选择变化的同时,页面不会跳转,也就是说不会产生新的请求。
- js拦截页面的跳转请求,动态的获取新的数据,路径也会随之变化。
- 数据需要通过ajax的请求获取。
路由
- 路由就是多个key-value的对应关系。
- 每一个路由都需要路由器的支持。
监听浏览器 hashchange 事件实现路由
如果你只需要一个简单的页面路由,而不想为此引入一整个路由库,你可以通过动态组件的方式,监听浏览器 hashchange 事件或使用 History API 来更新当前组件。
<script>
import About from "./components/About.vue";
import Home from "./components/Home.vue";
import NotFound from "./components/Not Found.vue";
const routes = {
//路由默认跳转到Home组件
'/': Home,
'/about': About
}
export default {
data() {
return {
currentPath: window.location.hash
}
},
computed: {
//计算属性的方法,响应式的,缓存
currentView() {
//过滤#,匹配路由
return routes[this.currentPath.slice(1) || '/' ] || NotFound
}
},
mounted() {
window.addEventListener('hashchange', () => {
//只要window变化了就,把当前的路径给App组件下的属性this.currentPath
this.currentPath = window.location.hash
})
}
}
</script>
<template>
<a href="#/">Home</a> |
<a href="#/about">About</a> |
<a href="#/non-existent-path">Broken Link</a>
<br>
<component :is="currentView" />
</template>
使用Vue Router+Vue2实现路由
用 Vue + Vue Router 创建单页应用非常简单:通过 Vue.js,我们已经用组件组成了我们的应用。当加入 Vue Router 时,我们需要做的就是将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们。
1.安装路由
npm i vue-router@3
注意:
vue3对应的vue-router版本是4
vue2对应的vue-router版本是3
我这里是vue2
2.导入VueRouter插件(main.js)
import Vue from 'vue'
import App from './App.vue'
import VueRouter from "vue-router"
import './assets/main.css'
//关闭vue的生产提示
Vue.config.productionTip = false
//使用路由器插件
Vue.use(VueRouter)
new Vue({
render: (h) => h(App),
}).$mount('#app')
3.编写创建路由器router(并暴露)的js文件(router/index.js)
import VueRouter from "vue-router";
import Home from "../components/Home";
import About from "../components/About";
import NotFound from "../components/Not Found";
//创建路由器并暴露
export default new VueRouter({
//多个路由
routes:[
{
path:'/about',
component:About
},
{
path:'/Home',
component:Home
}
]
})
4.引入router(main.js)
import router from "./router";
new Vue({
render: (h) => h(App),
router:router
}).$mount('#app')
5. 使用router-link激活路由以及路由视图的展示
<template>
<div>
<h1>Router Demo</h1>
<router-link to="/home">Home</router-link>||
<router-link to="/about">About</router-link>
<router-view></router-view>
</div>
</template>
注意点
- 当路由组件进行切换的时候,当前组件会被销毁,然后挂载新的组件。
- 通过路由器配置并且渲染的组件,我们称路由组件
- 多个路由共用的是同一个路由器
路由嵌套
现在定制一个需求
当我点击Home路由组件的时候,我希望它再来一个嵌套的路由。因此我们就需要对Home组件也进行路由的激活和视图展示:
<template>
<div class="row">
<div class="row">
<div class="col-xs-offset-2 col-xs-8">
<div class="page-header"><h2>Home</h2></div>
</div>
</div>
<div class="col-xs-offset-2 col-xs-2">
<div class="list-group">
<router-link class="list-group-item" active-class="active" to="/home/message">message</router-link>
<router-link class="list-group-item" active-class="active" to="/home/news">news</router-link>
</div>
</div>
<div class="col-xs-6">
<div class="panel">
<div class="panel-body">
<router-view></router-view>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Home",
mounted() {
console.log("Home组件被挂载了",this);
}
}
</script>
<style scoped>
*{
margin: 0;
}
</style>
那么新增了两个子路由,对应的,就必须去路由器添加:
export default new VueRouter({
//多个路由
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[
{
path:'message',
component:Message
},
{
path:'news',
component:News
}
]
}
]
})
children属性作为子路由的拓展,值得注意的是子路由的path下不能像父级路由那样添加/,因为vue-router底层的只要匹配到children属性,就会帮我们省略这一步骤。
路由传参
假如我现在有这么一个需求
当我点击哪个message,就显示哪个message的信息。
于是我们需要在嵌套一个三级子路由,并且由于对象格式相同,我们可以放到一个新的组件里,然后利用路由传参实现。
1.定义一个新的组Details.vue
2. 配置路由
export default new VueRouter({
//多个路由
routes:[
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
children:[
{
path:'message',
component:Message,
children:[
{
path :'details',
component:Details
}
]
},
{
path:'news',
component:News
}
]
}
]
})
3.Message组件下以对象格式传参
<template>
<div>
<ul>
<li v-for="m in messageList" :key="m.id">
<router-link :to="{
path:'/home/message/details',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
</li>
</ul>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Message",
data() {
return {
messageList: [
{id: 1, title: "message01"},
{id: 2, title: "message02"},
{id: 3, title: "message03"},
]
}
}
}
</script>
在Message组件下创建属性messageList集合,然后通过v-for遍历它并且显示在列表标签内,最后用router-link :to可以携带一个对象,path:url路径,query:参数。注意query的值也必须是一个对象格式的。
4.Details.vue取参数进行回显
<template>
<ul>
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>
</ul>
</template>
路由优化
路由命名name
<router-link :to="{
path:'/home/message/details',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
在开发环境中,如果我们真的用path属性去表示路由的路径,这实在过于冗余,因此可以在路由器的配置下做手脚:每一个路由可以有一个唯一的名字,供name属性引用
{
path:'message',
component:Message,
children:[
{
name:'link_detail',
path :'details',
component:Details
}
]
},
那么routeLink下的:to的值便不再需要path属性,而是用name来表示路由的名字。
<router-link :to="{
name:'Link_detail',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
利用路由params实现ResultFul风格请求
由于query属性是一对key-value的形式作为携带的参数。
如果想要实现ResultFul的请求方式,也就是说在url上隐藏key的显示,只显示value,也没有?和&作为分割。
<router-link :to="{
name:'link_detail',
params:{
id:m.id,
title:m.title
}
}">
更为简洁的写法::to表示里面只能是对象 ``用表示里面的只能是字符串,因此结合起来就可以表示字符串的对象了。
<router-link :to="`/home/message/details/${m.id}/${m.title}`">
以:id 的形式作为占位符,id是params下的属性。
children:[
{
name:'link_detail',
path :'details/:id/:title',
component:Details
}
]
那么在回显的时候,不再是$route.query.id,而是 $route.params.id
<template>
<ul>
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>
</ul>
</template>
路由props
props:{}
children:[
{
name:'link_detail',
path :'details/:id/:title',
component:Details,
//props的值会传给Details组件的props
props:{a:"1",b:"2"}
}
]
<template>
<ul>
<li>消息编号:{{ id }}</li>
<li>消息标题:{{ title }}</li>
a:{{ a }}
b:{{ b }}
</ul>
</template>
<script>
export default {
name: "Details",
mounted() {
console.log("Details被挂载了", this)
},
props: ['a', 'b']
}
</script>
这种做法可以在某个路由下通过props:对象 的形式去携带一个对象给路由组件,那么路由组件可以通过props:数组 的形式表示这些对象属性。缺点是只能携带定值。
props:true(最常用)
children:[
{
name:'link_detail',
path :'details/:id/:title',
component:Details,
props:true
}
]
props:true表示该路由组件携带的params参数,以props的形式传给当前路由组件
<template>
<ul>
<li>消息编号:{{id}}</li>
<li>消息标题:{{title}}</li>
</ul>
</template>
<script>
export default {
name: "Details",
mounted() {
console.log("Details被挂载了",this)
},
props:['id','title']
}
</script>
props(){}(插一句,用的不是很多)
由于上一种方式只能把组件携带的params参数传递给props,如果你想组件携带的query参数传递给props,那么这里演示一个通过props函数:将携带的query参数传递给props,但是不要忘记把请求的方式改回query参数。
children: [
{
name: 'link_detail',
path: 'details',
component: Details,
props($routes) {
return {
id: $routes.query.id,
title:$routes.query.title
}
}
}
]