Слоты с областями видимости — это полезная функция Vue.js, которая делает компоненты более универсальными и пригодными для повторного использования. Единственная проблема в том, что их сложно понять! Попытка разобраться в переплетении родительских и дочерних областей — это все равно, что решить сложное математическое уравнение.
Хороший подход, когда вы не можете легко что-то понять, — это попытаться использовать это для решения проблемы. В этой статье я покажу, как я использовал выделенные слоты для создания многоразового компонента списка.
Вы можете увидеть готовый продукт в этом Codepen .
Основной компонент
Компонент, который мы собираемся создать, называется my-list
и отображает списки вещей. Особенностью является то, что вы можете настроить способ отображения элементов списка при каждом использовании компонента.
Давайте сначала разберемся с простейшим сценарием использования, и получим my-list
визуализировать только один список вещей: массив имен геометрических фигур и количество сторон у них.
app.js
JavaScript
1
Vue.component('my-list', {
2
template: '#my-list',
3
data() {
4
return {
5
title: 'Shapes',
6
shapes: [
7
{ name: 'Square', sides: 4 },
8
{ name: 'Hexagon', sides: 6 },
9
{ name: 'Triangle', sides: 3 }
10
]
11
};
12
}
13
});
14
new Vue({
16
el: '#app'
17
});
index.html
HTML
xxxxxxxxxx
1
<div id="app">
2
<my-list></my-list>
3
</div>
4
<script type="text/x-template" id="my-list">
6
<div class="my-list">
7
<div class="title">{{ title }}</div>
8
<div class="list">
9
<div class="list-item" v-for="shape in shapes">
10
<div>{{ shape.name }} <small>({{ shape.sides }} sides)</small></div>
11
</div>
12
</div>
13
</div>
14
</script>
Если добавить немного CSS, это будет выглядеть следующим образом:
Вам также может понравиться:
Создание приложений с помощью API композиции Vue 3 .
обобщающий my-list
Теперь мы хотим сделать my-list
достаточно универсальным, чтобы отобразить любой вид списка. Вторым тестовым примером будет список цветов, включая небольшой образец, чтобы показать, как выглядит цвет.
Для этого нам нужно абстрагировать любые данные, относящиеся к списку фигур. Поскольку элементы в наших списках могут иметь различную структуру, мы дадим my-list
слот, чтобы родитель мог определить, как будет отображаться любой конкретный список.
app.js
JavaScript
xxxxxxxxxx
1
Vue.component('my-list', {
2
template: '#my-list',
3
props: [ 'title' ]
4
});
index.html
HTML
xxxxxxxxxx
1
<script type="text/x-template" id="my-list">
2
<div class="my-list">
3
<div class="title">{{ title }}</div>
4
<div class="list">
5
<slot></slot>
6
</div>
7
</div>
8
</script>
Давайте теперь создадим два экземпляра my-list
компонента в корневом экземпляре для отображения наших двух списков тестовых примеров:
app.js
JavaScript
xxxxxxxxxx
1
new Vue({
2
el: '#app',
3
data: {
4
shapes: [
5
{ name: 'Square', sides: 4 },
6
{ name: 'Hexagon', sides: 6 },
7
{ name: 'Triangle', sides: 3 }
8
],
9
colors: [
10
{ name: 'Yellow', hex: '#F4D03F', },
11
{ name: 'Green', hex: '#229954' },
12
{ name: 'Purple', hex: '#9B59B6' }
13
]
14
}
15
});
16
<div id="app">
17
<my-list :title="Shapes">
18
<div class="list-item" v-for="item in shapes">
19
<div>{{ shape.name }} <small>({{ shape.sides }} sides)</small></div>
20
</div>
21
</my-list>
22
<my-list :title="Colors">
23
<div class="list-item" v-for="color in colors">
24
<div>
25
<div class="swatch" :style="{ background: color.hex }"></div>
26
{{ color.name }}
27
</div>
28
</div>
29
</my-list>
30
</div>
Это будет выглядеть так:
Поверхностные компоненты
То, что мы только что создали, прекрасно работает, но не является хорошим кодом. my-list
по имени является компонентом для отображения списка. Но нам пришлось абстрагировать всю логику рендеринга списка в родительский. Компонент делает немного больше, чем просто оборачивает список некоторой презентационной разметкой.
Учитывая, что в обоих объявлениях компонента (т.е. <div class="list-item" v-for="item in ...">
) все еще повторяется код , было бы здорово, если бы мы могли делегировать это компоненту, чтобы он не был таким поверхностным.
Scoped Слоты
Чтобы позволить нам сделать это, мы будем использовать слот с областью действия вместо обычного слота. Слоты с областью действия позволяют передавать шаблон в слот вместо передачи визуализированного элемента. Он называется «областью видимости», поскольку, хотя шаблон отображается в родительской области, он будет иметь доступ к определенным дочерним данным.
Например, компонент child
с областью видимости может выглядеть следующим образом.
HTML
xxxxxxxxxx
1
<div>
2
<slot my-prop="Hello from child"></slot>
3
</div>
Родитель, который использует этот компонент, объявит template
элемент в слоте. Этот элемент шаблона будет иметь атрибут, scope
который называет объект псевдонима. Любые реквизиты, добавленные в слот (в дочернем шаблоне), доступны как свойства объекта псевдонима.
HTML
xxxxxxxxxx
1
<child>
2
<template scope="props">
3
<span>Hello from parent</span>
4
<span>{{ props.my-prop }}</span>
5
</template>
6
</child>
Представляет как:
HTML
xxxxxxxxxx
1
<div>
2
<span>Hello from parent</span>
3
<span>Hello from child</span>
4
</div>
Использование Soped Slot в my-list
Давайте передадим списки в my-list
качестве реквизита. Затем мы можем заменить слот на область видимости. Этот способ my-list
может быть ответственным за итерацию элементов списка, но родитель все еще может определить, как должен отображаться каждый элемент списка.
index.html
HTML
xxxxxxxxxx
1
<div id="app">
2
<my-list title="Shapes" :items="shapes">
3
<!--template will go here-->
4
</my-list>
5
<my-list title="Colors" :items="colors">
6
<!--template will go here-->
7
</my-list>
8
</div>
Теперь мы my-list
переходим к элементам. Внутри v-for
цикла item
есть псевдоним текущего элемента списка. Мы можем создать слот и привязать этот элемент списка к слоту, используя v-bind="item"
.
app.js
JavaScript
xxxxxxxxxx
1
Vue.component('my-list', {
2
template: '#my-list',
3
props: [ 'title', 'items' ]
4
});
index.html
HTML
xxxxxxxxxx
1
<script type="text/x-template" id="my-list">
2
<div class="my-list">
3
<div class="title">{{ title }}</div>
4
<div class="list">
5
<div v-for="item in items">
6
<slot v-bind="item"></slot>
7
</div>
8
</div>
9
</div>
10
</script>
Примечание: если вы раньше не
v-bind
использовали без аргумента, это свяжет свойства всего объекта с элементом. Это полезно для слотов с ограниченной областью, поскольку часто объекты, которые вы связываете, будут иметь произвольные свойства, которые теперь не нужно указывать по имени.
Теперь мы вернемся к нашему корневому экземпляру и объявим шаблон в слоте my-list
. Посмотрев сначала на список фигур, шаблон должен включать scope
свойство, которому мы назначаем псевдоним shape
. Этот псевдоним позволяет нам получить доступ к реквизиту. Внутри шаблона мы можем использовать точно такую же разметку, которая была раньше, для отображения элементов списка форм.
HTML
xxxxxxxxxx
1
<my-list title="Shapes" :items="shapes">
2
<template scope="shape">
3
<div>{{ shape.name }} <small>({{ shape.sides }} sides)</small></div>
4
</template>
5
</my-list>
Теперь вот полный шаблон:
HTML
xxxxxxxxxx
1
<div id="app">
2
<my-list title="Shapes" :items="shapes">
3
<template scope="shape">
4
<div>{{ shape.name }} <small>({{ shape.sides }} sides)</small></div>
5
</template>
6
</my-list>
7
<my-list title="Colors" :items="colors">
8
<template scope="color">
9
<div>
10
<div class="swatch" :style="{ background: color.hex }"></div>
11
{{ color.name }}
12
</div>
13
</template>
14
</my-list>
15
</div>
Заключение
Хотя этот подход имеет такую же разметку, как и раньше, он делегировал общую функциональность компоненту, что делает его более надежным.