Skip to content

导航守卫

配合 - 文档一起看

day 05-20

全局守卫

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文件中, 组件内配置的守卫,只对当前组件生效

  1. beforeRouteEnter 进入路由组件之前
  2. beforeRouteUpdate 动态路由切换,组件复用 ( /iphone/1 => /iphone/2 )
  3. 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回调

全局前置守卫-拦截到登录

image.png

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下载地址

https://gitee.com/vrfe/vue-28.git

bash
git clone https://gitee.com/vrfe/vue-28.git    
# day520是今天的内容 src是已经写完的目录,可以对比看看自己的代码

项目

https://www.yuque.com/u158868/ng9lzm/dpnfzvl31qep5yty?singleDoc# 《项目PC+移动》 密码:an1k

开发文档 :http://geek.itheima.net/document