【Vue3从零开始-实战】S10:Toast弹窗组件开发

1,698 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情

前言

实战已经开始了!前面几篇文章已经将登录注册和模拟接口请求的内容讲完了,但是在登录失败或者接口请求失败的时候,还缺少一个提示内容,虽然浏览器也给我们提供了alert之类的提示框,但是作为前端开发,不仅为了美观,更加为了以后可以复用,所以我们可以写一个组件专门用来做提示弹窗信息的。

补充

在上一章节中,可能在调用接口的时候,URL写错了或者接口API写错了,都会导致错误信息,但是当我们用async/await去请求接口的时候,会发现并不会执行下面的else语句的。

👉 比如我们把baseURL中的fastmock写错成fastmock111

export const post = (url, data = {}) => {
  return new Promise((resolve, reject) => {
    axios.post(url, data, {
      baseURL: 'https://www.fastmock111.site/mock/eb925863ecc46f2108cd43d75f96c1cd/pro'
    }).then((response) => {
      resolve(response)
    }, err => {
      reject(err)
    })
  })
}

👉 然后我们再去请求post的时候,就会发现并不会提示登录失败

const handleLogin = async () => {
  const result = await post('/api/user/login', {
    mobile: data.mobile,
    password: data.password
  })
  if (result.data.code === 0) {
    localStorage.isLogin = true
    router.push({ name: 'Home' })
  } else {
    console.log('登录失败')
  }
}

image.png

点击登录之后,浏览器控制台就会直接报错了。对于async/await这种写法要怎么去控制接口请求报错呢?可以用js里面的try/catch方法。

const handleLogin = async () => {
  try {
    const result = await post('/api/user/login', {
      mobile: data.mobile,
      password: data.password
    })
    if (result.data.code === 0) {
      localStorage.isLogin = true
      router.push({ name: 'Home' })
    } else {
      console.log('登录失败')
    }
  } catch (e) {
    console.log('请求失败')
  }
}

image.png

虽然控制台还是有接口报错,但是同样也将提示信息告诉我们了,这样也就方便我们定位问题所在了。

Toast组件

🌀 在开发组件之前,我们需要去新建一个vue文件,但是这种公共的组件就不能直接新建在views目录下面了,还记得最初创建项目时,有一个components文件夹嘛?这里面就是用来存放公共组件的地方。

👉 在cpmponents文件夹下面新建一个Toast.vue

image.png

👉 Toast组件中的DOM结构和样式也比较简单

<template>
  <div class="toast">内容</div>
</template>

<script>
export default {
}
</script>

<style lang="scss" scoped>
.toast{
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    padding: .1rem;
    background: rgba(0,0,0,0.5);
    border-radius: .05rem;
    color: #fff;
}
</style>

👉 Login.vue中引入Toast组件

import Toast from '../../components/Toast'
components: {
    Toast
},
<Toast />

image.png

🔆 可以看到页面上就会直接显示出Toast了。

组件显隐

目前来看Toast组件是一直保持显示状态的,我们需要在点击登录之后才显示出来才行,并且间隔 2 秒之后自动隐藏。

👉 在setup函数中的data里面定义一个showToast,用于判断组件显示隐藏

const data = reactive({
  showToast: false,
})

👉 在Toast组件标签上通过v-if指令来判断showToast,为true的时候就显示,反之隐藏

<Toast v-if="data.showToast" />

👉 需要在handleLogin函数中请求接口失败时,将showToast设为true 👉 将showToast设为true的同时,需要用setTimeout方法将showToast在间隔 2 秒之后设为false

const handleLogin = async () => {
  try {
    const result = await post('/api/user/login', {
      mobile: data.mobile,
      password: data.password
    })
    if (result.data.code === 0) {
      localStorage.isLogin = true
      router.push({ name: 'Home' })
    } else {
      data.showToast = true
      console.log('登录失败')
      setTimeout(() => {
        data.showToast = false
      }, 2000)
    }
  } catch (e) {
    data.showToast = true
    console.log('请求失败')
    setTimeout(() => {
        data.showToast = false
    }, 2000)
  }
}

chrome-capture (1).gif

组件传参

在调用登录接口时,我们判断了登录失败和请求失败的逻辑,但是这些提示不应该直接用comsole.log输出到控制台,应该将提示语动态传递给Toast组件显示出来。

👉 在setup函数中的data里面定义toastMessage

const data = reactive({
    showToast: false,
    toastMessage: ''
})

👉 在Toast组件标签中使用toastMessage

<Toast v-if="data.showToast" :message="data.toastMessage" />

👉 Toast组件中通过props接收传递过来的message

export default {
  props: ['message']
}

👉 将handleLogin方法中的console.log换成toastMessage

data.toastMessage = '请求失败'

chrome-capture (2).gif

拆分组件

按照上面的代码来写的话,基本上是把所有的逻辑全部放在了一个setup函数里面,如果以后还要增加新的组件,那么这一个页面的代码量就又会很难维护了。而且也不符合compositionAPI中将逻辑归纳在一块区域的原则,所以我们需要拆分组件中的逻辑,也就是逻辑必须是跟随组件在一起的。

👉 打开Toast组件,将关于Toast组件逻辑全部放到一个新的方法中,然后通过export导出,可以让外部引入

export const useToastEffect = () => {

  const toastData = reactive({
    showToast: false,
    toastMessage: ''
  })

  const showToast = (message) => {
    toastData.showToast = true
    toastData.toastMessage = message
    setTimeout(() => {
      toastData.showToast = false
      toastData.toastMessage = ''
    }, 2000)
  }
  
  return { toastData, showToast }
}
  • 逻辑部分和setup函数中写法一致,都是需要将绑定的数据定义在reactive中,最后通过return返回给外部。

👉 Login页面中不仅要引入Toast组件,还需要引入Toast的逻辑方法

import Toast, { useToastEffect } from '../../components/Toast'

👉 setup函数中就可以直接使用Toast组件中的逻辑方法useToastEffect

setup () {
    const router = useRouter()
    const data = reactive({ username: '', password: '' })
    const { toastData, showToast } = useToastEffect()

    const handleLogin = async () => {
      try {
        const result = await post('/api/user/login', {
          username: data.username,
          password: data.password
        })
        if (result.data.code === 0) {
          localStorage.isLogin = true
          router.push({ name: 'Home' })
        } else {
          showToast('登陆失败')
        }
      } catch (e) {
        showToast('请求失败')
      }
    }

    const handleRegisterClick = () => {
      router.push({ name: 'Register' })
    }

    return { handleLogin, handleRegisterClick, data, toastData }
  }

总结

本篇文章主要是将Toast组件进行封装,并把setup函数中关于Toast组件的部分全部拆分出来放到组件里面进行统一管理。