09. 路由

2022-10-12 15:11:32发布
98

路由的基础搭建

路由指的是:根据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>

路由独享守卫

指的是在路由声明里写守卫函数

提示,在不同的地方写守卫函数,函数的名字也不一样