09. 路由
路由的基础搭建
路由指的是:根据url地址,跳转到对应的vue文件。比如:
htts://www.xxx.com/user // 跳转到user页面
htts://www.xxx.com/login // 跳转到login页面
要实现这个功能,需要先安装路由模块
npm i vue-router
下面通过一个例子来介绍怎么使用路由,这个例子是用户输入xxxx/test就跳到test页面
第1步,在src目录新建一个文件夹router,然后在里面新建2个文件,index.js和test.js,代码如下:
test.js (这里之所以用数组包裹json,是因为后续在index.js比较方便)
export default [{
path: '/test', // 路径
name: 'test',
component: () => import('@/views/test.vue'), // 路由懒加载
meta: {
title: '测试页', // 用于修改document.title
}
}]
index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import testRouter from './test'
Vue.use(VueRouter)
let routeList = [
{
path: '*',
redirect: '/404', // 路由重定向,找不到对应的路由页面都跑到这里来
}
];
routeList = routeList.concat( // 因为testRouter返回的是一个数组,所以可以直接使用concat合并,这就是为什么在test.js用数组包裹json的原因
testRouter
)
const router = new VueRouter({
mode: 'hash',
scrollBehavior: () => ({ x: 0, y: 0 }), // 路由跳转置顶
routes: routeList
})
// 路由全局守卫
router.beforeEach((to, from, next) => {
document.title = to.meta.title
next()
})
export default router
第2步,在main.js中注册
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app');
第3步,把App.vue加上router-view
<template>
<div id="app">
<router-view/>
</div>
</template>
目录结构如下 :
路由跳转
路由的跳转在模板上可以用router-lin标签进行跳转 ,相当于html的a标签,如 :
<router-link to="/index">xxx</router-link>
to后面带的是路由的path
如果不是在模板上跳转,vue提供了3种方式来跳转路由,分别是 :
this.$router.push
跳转到指定url路径,并向history栈中添加一个记录,点击后退会返回到上一个页面
this.$router.replace
跳转到指定url路径,但是history栈中不会有记录,点击返回会跳转到上上个页面 (就是直接替换了当前页面)
this.$router.go(n)
向前或者向后跳转n个页面,n可为正整数或负整数
// push
this.$router.push({
path : '/index'
})
// replace
this.$router.replace({
path : '/index'
})
// go
this.$router.go(-1);
如果路由的path太长,也可以用name属性来进行路由跳转
this.$router.push({
name: 'test'
})
上面的name需要和路由的name一致
export default [{
path: '/test',
name: 'test', // 这里的name
component: () => import('@/views/test.vue'),
meta: {
title: '测试页'
}
}]
路由跳转时传参
// 传参
this.$router.push({
path : '/index',
query : {
name : 'tom',
age : 18
}
});
// 获取参数
this.$route.query.name
this.$route.query.age
需要注意的是,这是是 this.$route,不是this.$router。没有r
动态路由
比如有一个路由,他的路径是这样的 :https://www.baidu.com/news/321,后面的321是不固定的,则我们在定义路由的时候,path属性可以这样定义 :
{
path: '/news/:id', // 这里的 /:id 就是定义动态路由的方法,不一定要写id,写成什么都可以
name: 'news',
component: () => import('@/views/news.vue'),
}
如果要获取这个参数,则 :
this.$route.params.id // 这里的id必须和path:'/news/:id'这里的id是同一个名称
跳转的时候这样写 :
this.$router.push({
path : /news/' + id,
})
子路由
比如有一个"个人中心"的页面,个人中心的页面中有2个子页面 :"我的收藏"、"我的足迹"。这时候就可以使用子路由
export default [{
path: '/personal',
name: 'personal',
component: () => import('@/views/personal/index.vue'),
meta: {
title: '个人中心'
},
children: [{
path: 'collection',
name: 'collection',
component: () => import('@/views/personal/collection.vue'),
meta: {
title: '我的收藏'
},
}, {
path: 'history',
name: 'history',
component: () => import('@/views/personal/history.vue'),
meta: {
title: '我的足迹'
},
}]
}]
index.vue
<template>
<div>
<router-link to="/personal/collection">我的收藏</router-link>
<router-link to="/personal/history">我的足迹</router-link>
<router-view/> <!--子路由需要一个router-view来显示-->
</div>
</template>
collection.vue
<template>
<div>这是我的收藏页面</div>
</template>
history.vue
<template>
<div>这是我的足迹页面</div>
</template>
文件目录
路由的Hash模式和History模式
router有两种模式:hash模式(默认)、history模式(需配置mode: 'history')
在浏览器上输入http://localhost:8080/访问该项目,地址栏上的地址突然变成http://localhost:8080/#/login,很丑。如果要改成http://localhost:8080/login这种模式,则只需要在
即可。此时在访问项目,是没问题的。但是如果把项目打包出来然后放到服务器上运行,你会发现运行项目时,一片空白。这是因为服务器匹配不到路由是什么玩意(因为此时的静态文件只有一个index.html,并没有什么/login、/index)
解决的方法是修改服务器的代码,这里以node.js为例 :
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
const httpPort = 3000; //端口号
const staticPath = './www'; //项目的存储目录
//获取文件类型,用以写入文件头
function getFileMime(suffix) {
return new Promise((resolve, reject)=>{
fs.readFile('./data/mime.json', (err, data)=>{
if(err){
reject(err);
}else{
resolve( JSON.parse(data.toString())[suffix] );
}
});
});
}
http.createServer((req, res) => {
let urlPath = url.parse(req.url).pathname;
//路径中包含/static/css、/static/js、/static/img的,则是资源文件
if (urlPath.startsWith('/static/css') || urlPath.startsWith('/static/js') || urlPath.startsWith('/static/img')) {
fs.readFile(staticPath + urlPath, async(err, data) => {
if (err) {
console.log(err);
}
let type = await getFileMime(path.extname(urlPath));
res.writeHead(200, {'Content-Type': type + ';charset="utf-8"'});
res.end(data);
})
}
//否则是其它路径,则全部读取index.html
else {
fs.readFile(staticPath + '/index.html', 'utf-8', (err, content) => {
if (err) {
res.end(404);
console.log(err);
}
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
res.end(content)
})
}
}).listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort);
})
/static/css、/static/js、/static/img这里指的是vue打包后的文件夹名字。我的是这样的 :
运行效果 :
这个例子的下载地址 :https://download.csdn.net/download/QQ408896436/12659140
该方法有bug,如果路由是动态路由,则会报错。本以为是我代码上有问题,所以我用express + connect-history-api-fallback 的方法来测试,也是一样,会报错。知道的大大麻烦告知一下,3Q
路由守卫
所谓的守卫,指的是我们是否能从一个页面跳转到另外一个页面。比如现在用户在逛一个商城的网站,他现在是没有登录的,此时他访问个人中心的页面,则不能让他进入,需要重定向到登录页。守卫就是做这样的事情
vue的守卫有3种,分别是全局守卫、组件内的守卫、路由独享守卫,其中全局守卫用得比较多,后面两个比较少,也不推荐使用
全局守卫
/**
* to 去哪个页面,包含去那个页面的路由信息。比如我要去a路由,则to就是a路由的信息
* from 来自哪个页面,包含来自那个页面的路由信息。比如我来自b路由,from就是b路由的信息
* next 是否通行或者重定向,默认需要执行该方法,如果不执行,都不通过,则访问所有路由都会被拒绝,该函数可以传递一个path来重定向
*/
router.beforeEach((to, from, next) => {
// 如果有登录
if (this.$store.state.token) {
next()
} else {
next('/login')
}
})
组件内的守卫
指的是在组件里面写守卫的函数,在组件里面写的话就有3个
<template>
<div>test</div>
</template>
<script>
export default {
beforeRouteEnter(to, from, next) {
next();
},
beforeRouteUpdate(to, from, next) {
next();
},
beforeRouteLeave(to, from, next) {
next();
}
};
</script>
路由独享守卫
指的是在路由声明里写守卫函数
提示,在不同的地方写守卫函数,函数的名字也不一样