导航守卫
配合 - 文档一起看
全局守卫
javascript
// 一 全局守卫
// 1. 全局前置守卫 router.beforeEach ==> 重要!!!
// 2. 全局解析守卫 router.beforeResolve
// 3. 全局后置守卫 router.afterEach => 没有next
const router = new VueRouter({ ... })
// 写在router实例后面
// path: '/' => path: '/goods'
// to :到哪儿去 route对象
// from : 从来哪儿的 route对象
// next : 是否放行 ,让不让你通过 function函数
// 1. next() 放行
// 2. next(false) 终止跳转
// 3. next('/路径') 拦截到某个路径去
router.beforeEach((to,from,next)=>{
console.log('beforeEach')
next()
})
// 在路由解析完成之前被调用
router.beforeResolve((to,from,next)=>{
console.log('beforeResolve')
next()
})
// 在路由导航完成后 注意, 这个守卫没有next函数。(都完成了,不存在放不放行了)
router.afterEach((to, from) => {
// to and from are both route objects.
console.log('afterEach')
// console.log(to, from)
})
路由独享守卫
beforeEnter((to,from,next) => {})
javascript
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{
path: '/goods',
component: Goods,
children: [
{ path: 'iphone/:id', component: Iphone },
{ path: 'mac', component: Mac }
],
// 路由独享,只在进入当前路由之前触发 拦住
// 在配置路由某条路由规则的时候定义
beforeEnter: (to, from, next) => {
// ...
console.log('beforeEnter')
next() // 注意next必须写
}
}
]
})
组件内守卫
.vue文件中, 组件内配置的守卫,只对当前组件生效
- beforeRouteEnter 进入路由组件之前
- beforeRouteUpdate 动态路由切换,组件复用 ( /iphone/1 => /iphone/2 )
- beforeRouteLeave 离开组件之前
javascript
const Foo = {
template: `...`,
// 当进入iPhone这个路由组件之前,会触发
// 1. beforeRouteEnter
// 1. 不能获取this
// - 组件实例还没有创建,因此无法直接访问组件实例的方法和数据
// 2. 只有 beforeRouteEnter的next方法,可以传回调,接收vm参数
// - 通过next(vm => {})的形式访问组件实例
beforeRouteEnter(to, from, next) {
console.log('beforeRouteEnter')
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
next((vm) => {
console.log(vm.msg)
})
}
// iphone/1 ==> iphone/2 都匹配的是iphone组件
// 在当前路由发生改变的时候,但是组件复用的情况下
// 1. 可以获取this的
// 2. 在动态路由切换的时候,可以在这里发送请求
beforeRouteUpdate(to, from, next) {
console.log(to, from)
console.log('路由改变了哟,可以发送请求')
next()
},
// 在离开当前路由组件的时候,触发
// 可以获取this
beforeRouteLeave(to, from, next) {
const res = confirm('你真的要离开我吗?')
if (res) {
next()
} else {
next(false)
}
}
}
小结
javascript
// 全局守卫
// 1. router.beforeEach 没有this
// 2. router.beoforeResolve
// 3. router.afterEach 没有 next
// 路由独享守卫
// beforeEnter(to, from , next )
// 组件内守卫
// beforeRouteEnter(to, from, next) 没有this,next(vm => vm.msg)
// beforeRouteUpdate(to, from, next) 动态路由切换时,重新发送请求
// beforeRouteLeave(to,from,next) 离开当前组件之前,确认是否要离开
// 导航解析流程 了解 ==>
// 1. beforeRouterLeave
// 2. beforeEach
// 3. beforeRouteUpdate
// 4. beforeEnter
// 5. beforeRouteEnter
// 6. beforeResolve
// 7. afterEach
// 8. DOM更新
// 9. beforeRouteEnter => next回调
全局前置守卫-拦截到登录
javascript
// 1. 判断用户有没有token, 有token, 直接放行 (有身份的人,想去哪就去哪~)
// 2. 没有token(游客),如果是白名单中的页面,直接放行
// 3. 否则,无token(游客),且在访问需要权限访问的页面,直接拦截到登录
import { getToken } from '@/utils/storage'
import { Toast } from 'vant'
// 设置了一个白名单
const whiteList = ['/login', '/register']
// 全局前置守卫,拦截,如果没有登录,没有token,拦截到 /login
router.beforeEach((to, from, next) => {
const token = getToken()
if (token) {
next() // 放行
} else {
if (whiteList.includes(to.path)) {
next() // 放行
} else {
// 提示,你还没有登录哟
Toast('你还没有登录哟~~')
next('/login')
}
}
})
封装接口-获取文章列表
新建 api/article.js
javascript
import request from '@/utils/request'
import { getToken } from '@/utils/storage.js'
export const getArticles = (obj) => {
const token = getToken()
return request.get('/h5/interview/query', {
params: {
current: obj.current, // 当前页
sorter: obj.sorter, // 数据按什么样的顺序给我,
pageSize: 10 // 每页显示几条数据
},
// 要携带请求头
headers: {
Authorization: `Bearer ${token}`
}
})
}
article.vue 导入使用
vue
import { getArticles } from '@/api/article.js'
export default {
name: 'article-page',
data() {
return {}
},
async created() {
const res = await getArticles({
current: 1,
sorter: 'weight_desc'
})
console.log(res)
},
methods: {}
}
</script>
请求拦截器-携带token
request.js
javascript
import { getToken } from './storage'
// 添加请求拦截器
instance.interceptors.request.use(
function (config) {
const token = getToken()
console.log(token)
// 在发送请求之前做些什么
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
function (error) {
// 对请求错误做些什么
return Promise.reject(error)
}
)
处理token过期
javascript
import { delToken, getToken } from './storage'
import router from '@/router/index.js'
instance.interceptors.response.use(
function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response
},
function (error) {
console.log(error)
if (error.response) {
// 401 token失效,或者过期
if (error.response.status === 401) {
// 注意,request.js中不能使用this.$router
// this.$router.push('/')
delToken()
router.push('/login')
} else {
// this.$toast() js文件中不能使用这个this.$toast this不是指向的vm
Toast(error.response.data.message)
}
}
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error)
}
)
分页功能-vant-list
javascript
// 1. v-model=loading:标记数据是否在加载中
// true在加载中, 此时不会重复触发加载
// false不在加载中,又可以加载更多数据了,加载下一页
// 2. :finished:数据是否全部加载已完成
// true数据已经加载完成,此时不会再次触发加载
// false数据还没加载完成,此时表示还有数据可以加载,可以加载更多
// loading和finished 一起控制是否可以触发load加载事件
// 3. load: 组件滚动到底部触发,加载更多
// ===>
// 1. List 组件通过 loading 和 finished 两个变量控制加载状态
// 2. 当组件滚动到底部时 或 进入页面数据没有撑满整个屏幕,会触发 load 事件,
// 并自动并将 loading 设置成 true。
// 此时可以发起异步操作并更新数据,数据更新完毕后,
// 我们需要手动将loading 设置成 false 即可。
// 3. 若数据已全部加载完毕,则直接将 finished 设置成 true 即可。
javascript
<van-list v-model="loading" :finished="finished" @load="onLoad">
<article-item
v-for="item in list"
:key="item.id"
:item="item"
></article-item>
</van-list>
data() {
return {
list: [],
current: 1,
sorter: 'weight_desc',
// vant-list
loading: false, // 表示当前是否在加载中
finished: false // 表示是否所有数据都加载完了
}
},
// 触发时机(onLoad会被触发多次!): ==> loading自动变为true
// 1. 一进入页面,如果数据没有撑满整个屏幕 list: [] 需要加载更多,触发load事件
// 2. 当用户往下滑动,触底时,需要加载更多,触发load事件
async onLoad() {
const { data } = await getArticles({
current: this.current,
sorter: this.sorter
})
// 1. this.list.push(...data.rows)
// 2. arr.concat()
this.list = this.list.concat(data.rows)
this.loading = false
// 让current+1, 保证下一次请求的时候,加载正确的页数
this.current++
// 数据渲染完后,需要
// 手动的让loading状态变为false,
// 一旦loading改为false,load事件可以再次触发
if (this.current > data.pageTotal) {
// 这个时候,即使触底,也不会再继续发送请求了
this.finished = true
}
}
列表切换-推荐和最新
javascript
<a
href="javascript:;"
:class="{ active: sorter === 'weight_desc' }"
@click="changeSort('weight_desc')"
>推荐</a
>
<a
href="javascript:;"
:class="{ active: sorter === null }"
@click="changeSort(null)"
>最新</a
>
javascript
changeSort(type) {
this.sorter = type
// 重置变量
this.list = []
this.finished = false
this.current = 1 // 页码重置为1
// 手动触发onload事件
this.loadding = true // 防止切换的时候,第一下,两次请求
this.onLoad()
}
打包配置
javascript
pnpm build 打包==> 生成 dist目录,
javascript
// vite.config.js
import vue from '@vitejs/plugin-vue2'
import { resolve } from 'path'
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'
export default {
base: '/', // 相对路径 => 都能找到资源
// 如果是 '/' ==> 绝对路径,必须要放到服务器根目录下,才能找到资源
// publicPath:'./', ==> Vue.config.js Vue-cli是这个配置
plugins: [
vue(),
// 配置的是vant组件的按需导入,vite才有的
Components({
resolvers: [VantResolver()]
})
],
resolve: {
alias: {
'@': resolve(__dirname, './src')
}
}
}
serve安装
bash
pnpm i serve -g
# 全局安装serve
想要以某个文件夹启一个服务器, cmd 到那个目录里下, => serve
路由懒加载
javascript
import Layout from '@/views/layout.vue'
// 将路由导入改为异步导入的这种形式,当匹配到路由的时候,才会加载对应路由模块的内容
const Detail = () => import('@/views/detail.vue')
const Register = () => import('@/views/register.vue')
const Login = () => import('@/views/login.vue')
const Article = () => import('@/views/second/article.vue')
const Collect = () => import('@/views/second/collect.vue')
const Like = () => import('@/views/second/like.vue')
const User = () => import('@/views/second/user.vue')
Vue.use(VueRouter)
gitee下载地址
bash
git clone https://gitee.com/vrfe/vue-28.git
# day520是今天的内容 src是已经写完的目录,可以对比看看自己的代码
项目
https://www.yuque.com/u158868/ng9lzm/dpnfzvl31qep5yty?singleDoc# 《项目PC+移动》 密码:an1k