Написание повторно используемых компонентов является очень распространенным требованием в реальной разработке.В соответствии с различными требованиями будет много разных компонентов, включая простые всплывающие компоненты, такие как оповещения, сложные компоненты календаря и т. д.
Сегодня давайте вместе напишем компоненты (компоненты Vue) и посмотрим, сможем ли мы освоить какие-либо знания из этого! ! !
Написать компонент карусели
Компонент карусели должен быть одним из наиболее часто используемых компонентов, особенно на каждой домашней странице.
Итак, давайте прекратим сплетни и реализуем компонент карусели, подходящий для мобильных терминалов.
Сначала сделаем фотографии
Сначала нарисуйте ключевой момент: чтобы реализовать разработку компонентов, сначала кратко рассмотрите следующие три логики.
основная логика
- Инициализировать карусель
- Автовоспроизведение
- Переключите изображение карусели влево и вправо
Если вы хотите сделать хорошую работу, вы должны сначала заточить свои инструменты.Прежде всего, несмотря ни на что, давайте сначала познакомим с компонентами.
Представляем компоненты
Использование компонентов всегда вводилось первым, без исключения, см. следующие ссылки на компоненты
// App.vue文件 -> js部分
<script>
// 引入Swiper和SwiperItem组件
import Swiper from './components/Swiper/Swiper';
import SwiperItem from './components/Swiper/SwiperItem';
export default {
// 组件内注册组件
components: {
Swiper,
SwiperItem
}
}
</script>
Часть js уже зарегистрировала представленные компоненты в текущем компоненте приложения, давайте начнем использовать их в шаблоне.
// App.vue文件 -> html部分
<template>
<div id="app">
<Swiper v-model="currentId">
<SwiperItem :id="currentId">
<div>第一张图</div>
</SwiperItem>
<SwiperItem :id="currentId">
<div>第二张图</div>
</SwiperItem>
<SwiperItem :id="currentId">
<div>第三张图</div>
</SwiperItem>
</Swiper>
</div>
</template>
Дети, у вас много вопросительных знаков? Не волнуйся, просто слушай ветер
В компоненте SwiperItem есть 3 компонента SwiperItem, и содержимое, реализованное в компоненте SwiperItem, точно такое же.
Поэтому нет необходимости писать 3 раза, просто используйте массив для передачиv-forРендеринг в порядке
данные
Здесь я просто использую узел, чтобы написать интерфейс для возврата данных карты карусели.Если вы не хотите писать интерфейс, я опубликую данные напрямую, чтобы каждый мог их увидеть интуитивно.
фиктивные данные
// 轮播图数据
const albums = [
{ "id": 1, "title": "叶惠美", "public": 2003, "song": '晴天', "img": "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=281827478,1045705647&fm=26&gp=0.jpg"},
{ "id": 2, "title": "七里香", "public": 2004, "song": '七里香', "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873891083&di=7892d2142e6aba7e203d270e20599235&imgtype=0&src=http%3A%2F%2Fpic.rmb.bdstatic.com%2Fc1505303db7c257f248adc87b6e22fd5.jpeg"},
{ "id": 3, "title": "十一月的萧邦", "public": 2005, "song": '夜曲', "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873911041&di=51fc38d5805edc63fdd7301dbcef316f&imgtype=0&src=http%3A%2F%2Fzximg.ingping.com%2Fueditor%2Fjsp%2Fupload%2F201705%2F201705031153520358724.jpg"},
{ "id": 4, "title": "依然范特西", "public": 2006, "song": '听妈妈的话', "img": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584873937432&di=ebf0092e78d5499f54728eeb43449414&imgtype=0&src=http%3A%2F%2Fimg1.dongqiudi.com%2Ffastdfs2%2FM00%2F66%2FBA%2FChOqM1rO_0mADp5rAACEhAjn7aY043.jpg"}
];
Интерфейс, который я написал,/getalbums, возврат в формате json,{code: 0, data: albumes}
Ниже я все еще работаю по обычному запросу, в предыдущей структуре каталогов вы должны увидетьapi/shop.jsфайл, вот код, который я инкапсулировал для запроса данных карусели
// api/shop.js文件
import axios from 'axios';
// 拦截返回的响应数据
axios.interceptors.response.use(res => res.data);
export default {
async getAlbums() {
let { data } = await axios.get('/api/getalbums');
return data;
}
}
запросить данные
Теперь вернемся к App.vue и начнем делать запросы, а затемv-forПройдите массив и визуализируйте компонент SwiperItem
// App.vue文件 -> js部分
<script>
import Swiper from './components/Swiper/Swiper';
import SwiperItem from './components/Swiper/SwiperItem';
// 引入封装好的请求方法
// @是webpack配的alias别名,指代src目录
import shop from '@/api/shop';
export default {
data() {
// 轮播图数据
sliders: [],
// 当前id,用来显示对应的图片
currentId: 1
},
async mounted() {
// 请求数据并更新slider数组
this.sliders = await shop.getAlbums();
},
components: {
Swiper,
SwiperItem
}
}
</script>
визуализировать данные
Часть данных рендеринга будет передана нашему шаблону для обработки
// App.vue文件 -> html部分
<template>
<div id="app">
<Swiper v-model="currentId" v-if="sliders.length">
<template v-for="item in sliders">
<SwiperItem :key="item.id" :id="item.id">
<img :src="item.img" />
</SwiperItem>
</template>
</Swiper>
</div>
</template>
Почему приведенный выше шаблон написан именно так?
-
v-modelВы также можете привязывать компоненты- на компоненте
v-modelПо умолчанию будет использоватьсяvalueопора и имяinputмероприятие
- на компоненте
-
v-ifКомпонент управления визуализируется после получения данных - на SwiperItem
idДинамические атрибуты используются для определения фокуса изображения, которое должно отображаться в данный момент.
Ну и пишите сюда, основной эффект отображения сделан,imgШирина и высота элемента могут быть определены в разделе CSS App.vue следующим образом:#app img { width: 100%; height: 220px; }, представляется более целесообразным ограничить ширину и высоту
Теперь начните развиваться, как вам угодно
Компонент Swiper
Swiper написал несколько внутренних компонентов SwiperItem, поэтому один из них должен бытьslotслот для распространения контента и полученияv-modelПройдено после привязки данныхvalue
Теперь давайте посмотрим на реализованную логику.
// Swiper.vue文件
<template>
<div class="swiper">
<div class="view">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
props: {
// v-model默认传递过来的value属性
value: {
type: Number,
default: 1
}
}
}
</script>
<style scoped>
.swiper {
position: relative;
width: 100%;
height: 220px;
overflow: hidden;
}
</style>
Сначала напишем базовую логику, остальную логику напишу позже, реализуем компонент SwiperItem
Компонент SwiperItem
Изображение карусели очень похоже на вкладку, в данный момент отображается только одно изображение, а остальные скрыты, для этого используется компонент SwiperItem.
// SwiperItem.vue文件
<template>
<transition name="items">
<div class="swiper-item" v-if="isShow">
<slot></slot>
</div>
</transition>
</template>
<script>
export default {
props: {
id: {
type: Number,
// 必填属性
required: true
}
},
data() {
return {
selectedId: 1
}
},
computed: {
isShow() {
// 根据props传过来的值判断v-if显示
return this.id === this.selectedId;
}
}
}
</script>
<style scoped>
.items-enter-active,
.items-leave-active {
transition: .5s linear;
}
.items-leave-to {
transform: translateX(-100%);
}
.items-enter {
transform: translateX(100%);
}
.items-enter-active {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
}
</style>
Инициализировать карусель
Вернувшись в компонент Swiper, нам нужно написать метод для отображения карусели, поэтому давайте просто назовем его методом show.
Что он делает: траверс$childrenПодсборкаПример, измените значение selectedId в дочернем компоненте, а затем отобразите начальную карусель
// Swiper.vue文件
<script>
export default {
...省略,
data() {
return {
index: 1
}
},
methods: {
show() {
// 给index赋值为传递过来的value
// 如果没有传递value值,就默认取第一个子组件里的id值
this.index = this.value || this.$children[0].id;
// 遍历子组件,并修改实例上对应的selectedId值
this.$children.forEach(vm => {
vm.selectedId = this.index;
});
}
},
mounted() {
// 初始化
this.show();
}
}
</script>
Реализовать карусельные точки
Под изображением карусели часто есть соответствующие точки, что позволяет пользователям интуитивно видеть, сколько всего изображений карусели На самом деле это очень легко реализовать, потому что, пока мы знаем, сколько фрагментов данных существует, мы можем их использовать.v-forВышел соответствующий цикл
// Swiper.vue文件
<template>
<div class="swiper">
<div class="view">
<slot></slot>
</div>
<div class="dots">
<span class="dot"
v-for="dot in len"
:key="dot"
:class="{active: index === dot}">
</span>
</div>
</div>
</template>
<script>
export default {
...省略,
data() {
return {
index: 1,
// 记录轮播图的图片数量
// 定义在data是为了在模板使用
len: 0
}
},
methods: {
...省略
},
mounted() {
// 初始化
this.show();
// 更新len的长度,其实就是有几个子组件而已
this.len = this.$children.length;
}
}
</script>
<style scoped>
.swiper {
position: relative;
width: 100%;
height: 220px;
overflow: hidden;
}
/* 轮播图小点样式 */
.swiper .dots {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
}
.swiper .dots .dot {
width: 10px;
height: 4px;
border-radius: 6px;
background-color: rgba(255, 255, 255, 0.7);
display: inline-block;
margin: 0 2px;
}
.swiper .dots .active {
width: 14px;
background-color: #fff;
}
</style>
После некоторого подбрасывания выше, это должно быть немного похоже на то, что должно быть, давайте посмотрим на рендеры.
Автовоспроизведение
Многие эффекты карусели воспроизводятся автоматически, поэтому мы не будем игнорировать их в настоящее время. Передаем его компоненту Swiperautoplayреквизит для установки
// App.vue文件 -> html部分
<template>
<div id="app">
<Swiper v-model="currentId" v-if="sliders.length" :autoplay="true">
...省略
</Swiper>
</div>
</template>
Приложение родительского компонента переданоautoplay, затем продолжайте возвращаться к нашему компоненту Swiper
Давайте реализуем другойplayметод, в основном, чтобы написать таймер, а затем передатьchangeметод измененияvalueзначение, сделать автовоспроизведение
// Swiper.vue文件 -> js部分
<script>
export default {
props: {
...省略,
autoplay: {
type: Boolean,
default: false
}
},
data() {
return {
index: 1,
len: 0
}
},
methods: {
show() {...省略},
// 定时器播放
play() {
if (this.autoplay) {
this.timer = setInterval(() => {
this.change(this.index + 1);
}, 3000);
}
},
// 切换轮播图
change(index) {
// 处理右边界如果超过了len
if (index === this.len + 1) {
// 将index改回到1
index = 1;
} else if (index === 0) {
index = this.len;
}
// 通过$emit,触发input事件,修改value为index值
this.$emit('input', index);
},
// 清除定时器
stop() {
clearInterval(this.timer);
this.timer = null;
}
},
watch: {
// 这里是关键
// 监听value值的变化,value赋给了index,随着每次的index变化
// 就重新调用一下show方法,刷新视图
value() {
this.show();
}
},
mounted() {
this.show();
this.len = this.$children.length;
// 调用自动播放
this.play();
}
}
</script>
Давайте посмотрим на эффект вместе
переключаться влево и вправо
При автовоспроизведении по умолчанию картинка перемещается в позицию -100% влево. Если предполагается переключение пользователей, необходимоmountedзаписать предыдущее значение индексаprevIndex
предыдущее значение индексаprevIndexи текущее значение индексаindexсравнивать, если он больше текущегоindex, то это значит, что это обратный поворот, и картинку нужно сдвинуть вправо
Вернемся к компоненту SwiperItem и добавим к нему динамический класс для отображения эффекта перехода в прямом и обратном направлениях.
переход в разные стороны
// SwiperItem.vue文件
<template>
<transition name="items">
<div class="swiper-item" v-if="isShow" :class="{direction}">
<slot></slot>
</div>
</transition>
</template>
<script>
export default {
...省略
data() {
return {
index: 1,
// 判断方向
direction: false
}
}
}
</script>
<style scoped>
...省略
/* 反向过渡 */
.items-leave-to.direction {
transform: translateX(100%);
}
.items-enter.direction {
transform: translateX(-100%);
}
</style>
Судя по направлению
Приведенный выше код модифицирует для нас эффекты перехода в разные стороны, больше никакой чепухи, просто посмотрите на код
// Swiper.vue文件 -> js部分
<script>
export default {
...省略,
methods: {
show() {
this.index = this.value || this.$children[0].id;
this.$children.forEach(vm => {
this.$nextTick(() => {
vm.selectedId = this.index;
});
// 如果prevIndex大于了index就是反向轮播
vm.direction = this.prevIndex > this.index;
// 处理自动播放时的边界情况
if (this.timer) {
if (this.prevIndex === 1 && this.index === this.len) {
// 处理从第1张反向跳到最后一张的条件
vm.direction = true;
} else if (this.prevIndex === this.len && this.index === 1) {
// 处理最后一张正向跳到第一张的条件
vm.direction = false;
}
}
});
},
change(index) {
this.prevIndex = this.index;
...省略
}
},
mounted() {
this.show();
this.len = this.$children.length;
this.play();
// 先记录当前的值
this.prevIndex = this.index;
}
}
</script>
простое расчесывание:
- Измените метод показа
-
prevIndexИзначально он не определен, так как вmountedпозвоните по порядкуshowметод,prevIndexне был назначенindex - эквивалентно
prevIndex > indexВ первом сравнении сравнивается undefined и 1, а потом каждый раз при переключении картинки вызыватьchangeметод и датьprevIndexнаделенныйindexзначение -
$nextTickдляvm.directionДанные были изменены, подождите, пока DOM обновится, прежде чем изменять соответствующийselectedIdПоказать соответствующие фотографии соответственно
- Изменить метод изменения
- Существует очень мало мест, где можно изменить метод изменения,
this.prevIndex = this.indexЭто ставить последний каждый раз при переключенииindexназначенныйprevIndex - Например: в начале
prevIndexне определено,indexравно 1; когдаprevIndexКогда это 1,index2 и так далее, чтобы всем было понятно
На данный момент мы завершили логику кода эффекта перехода для левого и правого переключения, и нам все еще не хватает последнего шага, давайте добавим в карусельtouchСобытия, позволяющие пользователям переключать изображения влево и вправо
добавить сенсорное событие
Логика для оценки направления была завершена выше.Это кусок пирога для пользователя, чтобы переключиться влево и вправо.Сначала добавьте событие касания к элементу.
// Swiper.vue文件
<template>
<div class="swiper">
<div class="view" @touchstart="touchstart" @touchend="touchend">
<slot></slot>
</div>
...省略
</div>
</template>
<script>
export default {
...省略,
methods: {
touchstart(e) {
// 刚触摸屏幕时记录一个x坐标
this.startX = e.touches[0].pageX;
// 并且停止自动播放
this.stop();
},
touchend(e) {
// 计算开始点和手指抬起时离开点的坐标差值
let disX = e.changedTouches[0].pageX - this.startX;
// 如果小于0就表示是正向切换(向左滑动)
// 反之,就是反向切换(向右滑动)
if (disX < 0) {
this.change(this.index + 1);
} else {
this.change(this.index - 1);
}
// 切换完毕后,继续进行自动播放
this.play();
},
...省略
}
}
</script>
Даже если здесь написано, давайте еще раз взглянем на эффект.
Поскольку компонент используетсятаймер, тогда не забудьbeforeDestroyпри звонкеstopКак очистить таймер
Обучение на протяжении всей жизни
Изначально я хотел написать еще два повторно используемых компонента, но побоялся, что слов будет слишком много, и всем надоест его читать, поэтому просто нажмите на него, и я тоже поставлю соответствующийадресОтправьте его для справки
Впитывая человеческую мудрость, учиться можно бесконечно, спасибо за просмотр