目录
vue中跳转页
面
有两种方式:
路由导航
和
编程式导航
9.路由导航
这种方式会多添加一层标签,相当于原生html中外面包了一个a标签,有可能导致样式不对
<li v-for="item in goodslist" :key="item.id">
<router-link to="/detail">
<img :src="item.img" :alt="item.name" />
<div>
<p>商品名称:{{ item.name }}</p>
<p>商品价格:{{ item.price }}</p>
<button>抢购</button>
</div>
</router-link>
</li>
10.编程式导航
编程式导航,就是在函数中,编写js逻辑,实现跳转
这里主要用的是
$router
这个对象原型上面的一些方法,如:push() replace() go() back()
-
push(url地址)
push() 往历史记录中添加一条记录 参照原生history API中的pushState()
-
replace(url地址)
replace() 替换当前这条历史记录 参照原生history API中的replaceState()
-
go(整数)
go(n) n是一个整数,代表页码 0代表当前页 -1代表上一页 1代表下一页
-
back()
回退一页
-
视图
<li v-for="item in goodslist" :key="item.id" @click="goDetail">
<img :src="item.img" :alt="item.name" />
<div>
<p>商品名称:{{ item.name }}</p>
<p>商品价格:{{ item.price }}</p>
<button>抢购</button>
</div>
</li>
-
逻辑代码
goDetail() { this.$router.push("/detail"); // this.$router.replace('/detail') }
11.动态路由
跳转页面并传值有两种方式,动态路由和query
首页的商品跳商品详情:用动态路由并传参
通过以下三步实现:改路由地址 -> 导航并传参 -> parmas中取值
导航分为两种方式:
路由导航
和
编程式导航
两种。但是取值都是用
parmas
一、将固定路由改成动态路由
router => index.js
{
path:'/地址/:变量'
}
如:
{
path: '/detail/:id',
component: vDetail,
}
二、修改导航并携带参数
它有两种不同的方式
-
方式1:路由导航跳转并携带参数
<router-link :to='"/地址/"+要传递的数据参数'>
内容
</router-link>
-
方式2:编程式导航跳转并携带参数
视图部分
<li @click="goDetail(要传递的数据参数)">
内容
</li>
逻辑部分
goDetail(形参) {
this.$router.push("/地址/"+形参);
}
三、取值
this.$route.parmas.变量
12. query传参
分类跳转到分类列表,用 query 参数
注意点:不需要修改路由地址
也分为两种方式:
路由导航
和
编程式导航
两种。但是取值是一样的,用
query
-
路由导航跳转并携带参数
<router-link :to='"/地址?参数名="+要传递的数据参数'>
</router-link>
如:
<li v-for="item in sortlist" :key="item.id">
<router-link :to="'/list?id=' + item.id">
{{ item.name }}
</router-link>
</li>
-
编程式导航跳转携带参数
-
视图部分,如
<li v-for="item in sortlist" :key="item.id" @click="goList(item.id)"> {{ item.name }} </li>
-
逻辑部分
goList(形参) { // 方式一:直接拼接 this.$router.push('"/地址?参数名="+形参'); // 方式二:写成对象。如果要传多个参数,可以用以下这种写法 this.$router.push({ path:'/地址', query:{ 参数名1:形参1, 参数名2:形参2 } }) }
如
goList(id) { this.$router.push("/list?id=" + id); }
-
-
取值
this.$route.query.参数名
总结
页面之间跳转有两种方式:路由导航和编程式导航
所谓的路由导航就是使用标签
<router-link to="地址">
所谓的编程式导航就是在方法中写js逻辑实现跳转
this.$router.push('地址')
页面之间传值有两种方式:动态路由和query参数
动态路由就是在路由地址上加:/地址/:id 在parmas里面取
所谓query参数,就是在地址后面加 ?参数名=值 在query里面取
13.路由懒加载
单页应用有一个最大的缺点,首次加载过慢,因为第一次加载,要加载全部的css、js和HTML 从路由着手去优化,没有触发的路由,第一次不加载
router=>index.js
// 方法1:
const userCenter = () => import('../views/usercenter');
{
path: "/user",
component: userCenter ,
},
// 方法2:建议使用
{
path: "/user",
component:()=> import('../views/usercenter')
},
检测:打开网络,每打开一个路由,就会有新的js加载,说明已经懒加载了
14. 路由模式
-
设置
const router = new VueRouter({
routes,
// mode: 'hash' // 默认模式,地址上会有#号
mode: 'history' // 历史模式,地址上没有#号
})
-
路由两种模式(hash和history)的区别
在开发模式下:就只有带#号 和 不带# 美观的问题 但是在生产环境下: hash打包的生产物在服务器上,前进后退刷新都没有问题。因为它是模拟完整的url地址,当地址发生变化,不会重新加载(#后面的不算url地址,#号后面的东西发生变化,不会请求服务器) history的生产物在服务器上,前进后退都没有问题,但刷新出现了问题,因为它是完整的URL地址,当地址发生变化时,会请求服务器,这个时候,服务器就一定要有配置 history利用的就是原生window.history.pushState()方法
在服务器环境下,测试两种模式的区别
vue项目生产环境打包,放到express的服务器中,可以查看这两种的差异
1、分别打包两种生产物
npm run build
2、创建express服务器,hash和history创建的生产物分别放入static文件夹下
npm init -y npm i express
创建app.js
const express = require('express');
const app = express();
app.listen(3000, () => {
console.log('http://localhost:3000');
});
const path = require('path');
app.use(express.static(path.join(__dirname, './static'))); // 开启static文件夹静态资源
启动项目
nodemon app.js
访问
http://localhost:3000
以上,就可以看到history模式下,刷新会有问题,而hash模式下,都没有问题
解决history模式在node服务器出错的问题
对于 Node.js/Express,请考虑使用 [connect-history-api-fallback 中间件]
npm上的中间件:
connect-history-api-fallback – npm
安装: npm i connect-history-api-fallback
const express = require('express');
const app = express();
app.listen(3000, () => {
console.log('http://localhost:3000');
});
// 引入中间件
// 解决history在服务器上刷新出错的问题,使用connect-history-api-fallback中间件
var history = require('connect-history-api-fallback');
app.use(history());
const path = require('path');
app.use(express.static(path.join(__dirname, './static'))); // 开启static文件夹静态资源
这样,在node的服务器下,history刷新就不会出错了
15. 路由导航守卫
其实就是访问路由不同阶段的拦截。它本质还是函数。
注意:路由导航守卫是做权限管理的,axios是管理请求头和过滤请求数据的
路由导航守卫分为三个阶段:
全局路由导航守卫(2个)
->
路由独享守卫(1个)
->
组件守卫(3个)
-
全局路由导航守卫,写在路由器上
全局路由导航守卫-前置
路由器.beforeEach((to,from,next)=>{}) 常用
全局路由导航守卫-后置
路由器.afterEach((to,from)=>{}) (很少用),它没有next
-
路由独享守卫,写在某条路线上
beforeEnter(to,from,next){} 常用
-
组件守卫,写在某个组件内部的配置对象上(就类似于methods和生命周期样)
进入组件之前
beforeRouteEnter(to,from,next){} 常用,它的函数中没有this,因为没有还没有进入组件
组件内部更新之前
beforeRouteUpdate(to, from, next) {} 不常用,要在hash模式测试
离开组件之前
beforeRouteLeave(to, from, next) {} 常用
一、全局路由导航守卫
分前置和后置,写在router=>index.js中的 router 路由器上,在默认导出之前
前置
-
前置导航钩子函数,它接收一个回调函数,该函数有三个参数,to去哪里,from从哪来。to和from它们下面分别有一个path属性,next即执行下一步
路由器.beforeEach((to,from,next)=>{
})
// 前置导航钩子函数
router.beforeEach((to, from, next) => {
console.log(to, '全局路由导航守卫-前置 到哪去');
console.log(from, '全局路由导航守卫-前置 从哪来');
next();
});
前置导航钩子函数的适用场景?
最常用的
登录拦截
,有一些项目一定要强制登录,不登录无法访问任何页面
方法步骤:
-
一、创建登录页面并设置路由 login.vue (一级路由)
-
二、设置登录状态(当登录成功之后,向本地存储中添加一个标识,并跳转到首页)
<template>
<div>
<h1>登录</h1>
<div class="m20 p20">
<p class="mb20">
<input
type="text"
class="p10"
placeholder="请输入用户名"
v-model="user.username"
/>
</p>
<p class="mb20">
<input
type="text"
class="p10"
placeholder="请输入密码"
v-model="user.password"
/>
</p>
<p>
<button class="p10" @click="login">登录</button>
</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
user: {
username: "",
password: "",
},
};
},
methods: {
login() {
// console.log(this.user);
// 登录时的验证工作
// 这里固定了用户名为admin 密码为123
// 当登录成功了以后(跳到首页),并向本地会话中存一个状态,方便全局路由导航守卫使用
// 非空的判断
if (!this.user.username || !this.user.password) {
alert("用户名和密码必须填写");
return;
}
// 如果是admin和123,则登录成功
if (this.user.username === "admin" && this.user.password === "123") {
alert("登录成功");
sessionStorage.setItem("isLogin", true);
this.$router.push("/home");
} else {
alert("用户名和密码错误");
return;
}
},
},
};
</script>
<style scoped>
</style>
-
三、根据登录状态进行拦截 router->index.js
实现拦截的思路 1、如果它去的页面是登录页,我们就next 2、如果它去的是有
权限要求的
并
有了登录状态
,我们就next 3、如果以上都不符合,我们就强制它去登录
// router就是路由器,全局路由导航守卫就加在它上面
// 全局路由导航守卫-前置
router.beforeEach((to, from, next) => {
console.log(to, '全局路由导航守卫-前置 到哪去');
console.log(from, '全局路由导航守卫-前置 从哪来');
// 1、如果它去的页面是登录 我们就next
if (to.path === '/login') {
next();
return;
}
// 2、如果它有了登录状态 我们就next
if (sessionStorage.getItem('isLogin')) {
next();
return;
}
// 3、如果以上都不符合 我们就强制它去登录
next('/login');
});
// 全局路由导航守卫-后置 (用得少)
router.afterEach((to, from) => {
console.log(to, '全局路由导航守卫-后置 到哪去');
console.log(from, '全局路由导航守卫-后置 从哪来');
});
后置
-
后置导航钩子函数(很少用),它没有next
路由器.afterEach((to,from)=>{
})
// 后置导航钩子函数
router.afterEach((to, from) => {
console.log(to, '全局路由导航守卫-后置 到哪去');
console.log(from, '全局路由导航守卫-后置 从哪来');
})
二、路由独享守卫
所谓的路由独享,就是每一条路由上面的单独的配置属性,它是一个函数。它要写在每一个 path 地址上面
在这里可以做一些拦截,如果符合,我就next,否则我可以让它跳转到某一个页去(如用户权限等)
beforeEnter(to,from,next){
}
这里以list页面为例,想进到list页面,如果你是从分类来的,我就让你进,否则到首页去
{
path: '/list',
component: () => import('../pages/list'),
beforeEnter(to, from, next) {
console.log(to, '路由独享守卫 到哪去');
console.log(from, '路由独享守卫 从哪来');
/*
可以配置一些权限的控制
比如:如果你是从分类来,就next 否则就回到首页
比如:list这个页面只能从分类进,如果从其它的进,我们就让它到home
*/
if (from.path === '/sort') {
next();
} else {
next('/home');
}
},
},
三、组件守卫
所谓组件守卫,就是写在组件的配置对象上
以detail详情的组件为例
-
进入组件之前
// 进入组件之前
beforeRouteEnter(to, from, next) {
// 注意点:这个函数中没有this,因为还没有进入组件,组件的实例还没有创建(还没有执行生命周期)
console.log(this, "组件this"); // undefined
console.log(to, "进入组件之前 到哪去");
console.log(from, "进入组件之前 从哪来");
// next();
// 如果你是从home进到详情的,我就让你进,从其它的不让进
if (from.path == "/home") {
next();
} else {
next("/home");
}
},
-
组件内部更新之前
什么是组件内部更新呢?就是当url地址改变的时候(如:/detail/1 –> /detail/2),组件重新渲染,就是更新
注意:如果是history模式,这里会不好用,必须用hash模式(使用比较少)
// 组件内部更新之前
beforeRouteUpdate(to, from, next) {
// 这个函数中有this
console.log(to, "组件更新之前 到哪去");
console.log(from, "组件更新之前 从哪来");
next();
},
-
离开组件之前
// 离开组件之前
beforeRouteLeave(to, from, next) {
// 这个函数中有this
console.log(to, "离开组件之前 到哪去");
console.log(from, "离开组件之前 从哪来");
next();
},
总结:导航守卫,用在权限管理上
-
全局路由导航守卫,可以用于全站的权限
-
路由独享:可以控制某个页面的权限(超级管理员,有所有的权限,如果是客服,就只有客服的权限)
-
组件守卫:可以更精细化到某一个小块的权限
16. alias别名
在某条路线的属性上加
作用:就是说原来要通过
/cart
访问这个路由,现在通过
/别名
也能访问这个路由
可以在url地址中输入
/别名
也可以访问
{
path: "/cart",
component: () => import("../views/cart"),
alias: "/gouwuche", // 别名,就是说原来要通过/cart访问这个路由,现在通过/gouwuche也能访问这个路由
},
17. 命名路由
在某条路线的属性上加name属性
-
路由文件
{
path: "/user",
component: () => import("../views/usercenter"),
name: "个人中心", // 命名路由
},
-
视图中to时使用
<!-- 路由导航:to属性,除了path地址之外,你也可以通过name属性进行跳转 -->
<!-- <router-link activeClass='active' to="/user">个人中心</router-link> -->
<!-- 我们也可以通过命名路由去访问 -->
<router-link activeClass='active' :to="{name:'个人中心'}">个人中心</router-link>
18. 命名视图
即访问一个路径,可以渲染多个视图。其中一个是默认视图,其它的都是命名视图
-
router=>index.js
{
path: "/login", // 访问这一个路径
// component: () => import("../pages/login.vue"),
components: {
// 设置多个视图
default: () => import("../pages/login.vue"), // 默认视图
view1: () => import("../pages/test.vue"), // 命名视图
},
},
-
app.vue
<template>
<div>
<!-- 第一个出口: 默认视图出口 -->
<router-view></router-view>
<!-- 第二个出口:命名视图出口,name属性必须要和命名视图的key值一致 -->
<router-view name="view1"></router-view>
</div>
</template>
<script>
export default {
data() {
return {};
},
};
</script>
<style scoped>
</style>
19.路由元信息(常用)
相当于给这条路线添加一些自定义属性,然后在视图中可以取得这些自定义属性(常用)
{
path: "/user",
component: () => import("../views/usercenter"),
meta: {
// 自定义。你可以在这里设置很多属性,然后在$route.meta对象中取
title:'个人中心111'
},
},
取值:$route.meta.变量名