Vue3 新手面试题 - 轮播图

88 阅读4分钟

photo-1607705703571-c5a8695f18f6.avif

前言

这是一篇新手面试级别的题目解析,欢迎讨论

昨天在群里看到有人发了一道轮播图的面试题,用来面试新人,感觉还挺不错的,非常适合考察新手对 Vue3 和 JavaScript 的理解程度,于是我便自己写了一份 demo 分享出来。

题目

题目如下所示: image.png

代码

初始化

首先我们可以直接用vite构建项目,并且为了方便我还安装了tailwindcss作为辅助:

$ npm create vite@latest my-vue-app -- --template vue
$ npm install -D tailwindcss postcss autoprefixernpx && tailwindcss init -p

然后更新tailwind.config.js文件,注意 content 部分,将你需要用到 tailwind 的地方包含进来:

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{vue,js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

然后,记得更新style.css内容:

@tailwind base; 
@tailwind components; 
@tailwind utilities;

并且注意在main.js处引入style.css

Template 模版

模版代码如下:

<template>
  <div @mouseenter="onMouseEnter" @mouseleave="() => autoPlay()" class="container group select-none" :style="{
    '--left': left + 'px'
  }">
    <div class="wrap">
      <img v-for="url in imgList" :src="url" :key="url" alt="" />
    </div>

    <ul
      class="absolute z-10 inset-x-0 -bottom-12  group-hover:bottom-0 transition-all flex items-center justify-center gap-4 p-4">
      <li :class="['w-3 h-3 rounded-full cursor-pointer', left === index * -800 ? 'bg-blue-500' : 'bg-sky-50/40']"
        v-for="(_, index) in 3" :key="index" @click="handleClick(index)">
      </li>
    </ul>
    <div
      class="absolute px-2 -left-12 inset-y-0 flex items-center transition-all delay-100 ease-in-out group-hover:left-0 bg-transparent group-hover:bg-gray-400/10"
      :style="{
        opacity: left === 0 ? 0.2 : 1
      }">
      <span class="cursor-pointer bg-blue-50 rounded-full p-1" @click="() => {
        if (left === 0) return
        left += 800
      }">
        < </span>
    </div>
    <div
      class="absolute px-2 -right-12 inset-y-0 flex items-center transition-all delay-100 ease-in-out group-hover:right-0 bg-transparent group-hover:bg-gray-400/10"
      :style="{
        opacity: left === -(imgList.length - 1) * 800 ? 0.2 : 1
      }">
      <span class="cursor-pointer bg-blue-50 rounded-full p-1" @click="() => {
        if (left === -(imgList.length - 1) * 800) return
        left -= 800
      }">
        >
      </span>
    </div>
  </div>
</template>

组件 setup

组件 setup 部分代码如下:

<script setup>
import { onMounted, onUnmounted, ref } from 'vue';

const left = ref(0)
const imgList = [
  'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?q=80&w=2673&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
  'https://images.unsplash.com/photo-1439405326854-014607f694d7?q=80&w=2670&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
  'https://images.unsplash.com/photo-1471922694854-ff1b63b20054?q=80&w=2672&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D'
]

// 处理点击事件
const handleClick = (index) => {
  // 计算起始位置
  left.value = -800 * index
}
// 暂停
const onMouseEnter = () => {
  clearInterval(timer)
}

// 定时器
let timer = null
// 根据 flag 判断向前还是向后播放
let positive = true
const autoPlay = () => {
  timer = setInterval(() => {
    if (left.value === -(imgList.length - 1) * 800) {
      positive = false
    } else if (left.value === 0) {
      positive = true
    }
    if (positive) {
      left.value -= 800
    } else {
      left.value += 800
    }
  }, 2000)
}

// 初始化
onMounted(autoPlay)

// 销毁
onUnmounted(() => {
  clearInterval(timer)
})

</script>

样式

<style scoped>
/* 容器大小 */
.container {
  height: 300px;
  width: 800px;
  overflow: hidden;
  position: relative;
  margin: 100px auto;
}

/* .wrap */
.wrap {
  position: relative;
  left: var(--left);
  width: 2400px;
  transition: left 0.6s ease-in-out;
}

/* 图片大小 */
.wrap img {
  width: 800px;
  float: left;
  height: 300px;
  display: block;
}
</style>

解析

轮播图我们可以有很多种实现方式,本文介绍的思路和代码段,某种意义上是为了引出一些技术点,如果你不知道这些知识是什么,然后自己去查询学习,那么这将是一件好事。

首先,我们定义了三张图片地址构成的imgList数组和left值,我们给轮播图添加了类名为container的容器,三张图放在.wrap下,通过核心的overflow属性去控制相对位置,来让主要显示区域显示不同的图片,达到轮播的效果。

left值用来创建一个--left变量(css 变量),用于相对位置设定。 你可以在.wrap样式下看到这个用法,并且我们通过transition来添加动画效果,指定属性和时间、动画渐入渐出的效果。

这时候,我们首先要实现自动轮播的效果,那么我们只要设置一个函数autoPlay,定时地修改left相对位置即可。

这个函数的核心在于:

  • 设置定时器,用于在需要的时候控制清除定时器
  • 判断当前是向前还是向后轮播(左右皆可),当播放到最后一张图的时候我们可以向前,效果看起来好一些
  • 调整相对位置的像素值,这里仅需要注意图片个数和宽度即可

我们利用Vue的生命周期函数,在mounted之后执行autoPlay,在unMounted之后清除定时器。与此同时,利用mouseentermouseleave事件去实现清理定时器和重新运行轮播即可。

更多建议

对于这个Vue项目来说,以下几点是笔者扩展方向的建议:

  • 使用TypeScript进行开发
  • 使用UnoCss配合Icons preset来写样式和添加 svg 图标
  • 将轮播图封装成组件,通过一个基础组件内部添加插槽和传给基础组件初始化配置,开启和关闭不同的功能
  • 通过 providerinject即可在父子组件之间传递数据,通过pinia管理全局共用数据和持久化
  • 通过ky封装请求,通过TanStack Query去管理API数据和缓存

如果你对这些内容不太了解,那么非常推荐你进一步去接触这些东西。

好了,下次见。

其他

ps: 欢迎讨论