【青训营】- 跟着月影学 JavaScript学习笔记(二) 如何写好一个组件

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

承接上文, 在明确了各司其职这一基本原则后, 我们可以开始设计自己的第一个组件啦

课程中, 月影大大为我们详细阐述了轮播图(Slider)的实现与重构, 跟随着代码与结构一步步的优化升级, 笔者对于组件的认知也随之提升

结合老师所讲的内容, 通过手动完成一个vue3下的轮播图插件, 一起将知识变为应用

阅读本笔记您将收获到:

  1. 组件设计思路
  2. Vue组件相关知识
  3. Vue3中的一个简单轮播组件

3 组件设计

先贴上月影大大的代码链接, 接下来我们会把这个js轮播图修改为vue3的版本

3.1 设计原则

轮播图相信各位耳熟能详, 所以对于它的功能设计不再赘述, 我们主要谈一谈vue3中一个组件的设计思路

首先, 我们应秉承以下原则:

  • 命名原则: 属性/方法的命名应精准无歧义, 不应使用简称/中文拼写/含义不明等内容
  • 方法原则: 组件方法应是原子化的, 每一个方法对应一种操作(极端一些的话读写操作也要分离)

原则确定后, 我们开始编写代码, 我会按如下方式进行设计:

  1. vue2写法
  2. vue3写法
  3. vue3写法升级(面向对象)

3.2 vue2写法

在vue2中, 根据轮播图功能, 我们需要设计组件的name/props/data/methods...等等

先上代码, 我们low一点, 这个组件就叫CarouselOne.vue

我们先来确定nameprops:

import { defineComponent, PropType } from 'vue';

export default defineComponent({
  name: 'CarouselOne',
  props: {
    images: {
      type: Array as PropType<string[]>,
      required: true
    },
    cycle: {
      type: Number,
      default: () => {
        return 3000;
      }
    }
  }
});
复制代码

images为图片链接字符串数组, 我们虽是vue2写法, 但实际是vue3+ts, 所以需要使用Array as PropType<string[]>明确它的类型

cycle代表间隔时间, 这里和月影大大的代码看齐

接下来我们再写data:

interface CarouselData {
  selectedIndex: number;
  timer: number | undefined;
}

export default defineComponent({
  data() {
    return {
      selectedIndex: 0,
      timer: undefined
    } as CarouselData;
  }
})
复制代码

selectedIndex是当前选中的轮播图索引, 默认为0

timer是定时器, 用来实现轮播效果

相信小伙伴们都注意到了interface CarouselData这个东西, 声明它的原因很简单, 为了处理ts对于timer的类型检查问题

定时器一开始不存在时类型为undefined, 创建后类型为number, 在data中没办法进行这种类型的定义, 我们也绝对不写AnyScript, 所以利用as关键字, 来手动声明data中属性的类型即可

下面将要进入最重要的内容, methods

阅读月影大大的代码, 我们将其中每个原子化方法一一实现:

export default defineComponent({
    methods: {
        getSelectedItemIndex(): number {
          return this.selectedIndex;
        },
        getSelectedItem(): string {
          return this.$props.images[this.selectedIndex];
        },
        slideTo(index: number): void {
          this.selectedIndex = index;
        },
        slidePrevious(): void {
          this.slideTo(
            (this.$props.images.length + this.selectedIndex - 1) % this.$props.images.length
          );
        },
        slideNext(): void {
          this.slideTo((this.selectedIndex + 1) % this.$props.images.length);
        },
        stop(): void {
          clearInterval(this.timer);
        },
        start(): void {
          this.stop();
          this.timer = setInterval(() => this.slideNext(), this.$props.cycle);
        }
    }
})
复制代码

ts代码写完了, 接下来转换到页面元素职责, 这是笔者的弱项

据说vue3中template和render方法性能差别不大, 我们再加入html代码:

<template>
  <ul>
    <template v-for="(image, index) in images" :key="index">
      <li v-show="index === selectedIndex">
        <img :src="image" />
      </li>
    </template>
  </ul>
</template>
复制代码

最后, 在组件加载后调用start方法开始轮播就好啦, 最后组件为:

<template>
  <ul>
    <template v-for="(image, index) in images" :key="index">
      <li v-show="index === selectedIndex">
        <img :src="image" />
      </li>
    </template>
  </ul>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';

interface CarouselData {
  selectedIndex: number;
  timer: number | undefined;
}

export default defineComponent({
  name: 'CarouselOne',
  props: {
    images: {
      type: Array as PropType<string[]>,
      required: true
    },
    cycle: {
      type: Number,
      default: () => {
        return 3000;
      }
    }
  },
  data() {
    return {
      selectedIndex: 0,
      timer: undefined
    } as CarouselData;
  },
  methods: {
    getSelectedItemIndex(): number {
      return this.selectedIndex;
    },
    getSelectedItem(): string {
      return this.$props.images[this.selectedIndex];
    },
    slideTo(index: number): void {
      this.selectedIndex = index;
    },
    slidePrevious(): void {
      this.slideTo(
        (this.$props.images.length + this.selectedIndex - 1) % this.$props.images.length
      );
    },
    slideNext(): void {
      this.slideTo((this.selectedIndex + 1) % this.$props.images.length);
    },
    stop(): void {
      clearInterval(this.timer);
    },
    start(): void {
      this.stop();
      this.timer = setInterval(() => this.slideNext(), this.$props.cycle);
    }
  },
  mounted() {
    this.start();
  }
});
</script>
<style scoped></style>
复制代码

下篇文章我们会在vue2写法的基础上进行升级, 利用setup方法让组件结构更加清晰

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享