这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
承接上文, 在明确了各司其职这一基本原则后, 我们可以开始设计自己的第一个组件啦
课程中, 月影大大为我们详细阐述了轮播图(Slider)的实现与重构, 跟随着代码与结构一步步的优化升级, 笔者对于组件的认知也随之提升
结合老师所讲的内容, 通过手动完成一个vue3下的轮播图插件, 一起将知识变为应用
阅读本笔记您将收获到:
- 组件设计思路
- Vue组件相关知识
- Vue3中的一个简单轮播组件
3 组件设计
先贴上月影大大的代码链接, 接下来我们会把这个js轮播图修改为vue3的版本
3.1 设计原则
轮播图相信各位耳熟能详, 所以对于它的功能设计不再赘述, 我们主要谈一谈vue3中一个组件的设计思路
首先, 我们应秉承以下原则:
- 命名原则: 属性/方法的命名应精准无歧义, 不应使用简称/中文拼写/含义不明等内容
- 方法原则: 组件方法应是原子化的, 每一个方法对应一种操作(极端一些的话读写操作也要分离)
原则确定后, 我们开始编写代码, 我会按如下方式进行设计:
- vue2写法
- vue3写法
- vue3写法升级(面向对象)
3.2 vue2写法
在vue2中, 根据轮播图功能, 我们需要设计组件的name/props/data/methods...
等等
先上代码, 我们low一点, 这个组件就叫CarouselOne.vue
我们先来确定name
和props
:
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方法让组件结构更加清晰