Перейти к содержимому

Input подлагивает когда использую emit во vue

  • автор:

Пользовательские события

Подразумевается, что вы уже изучили и разобрались с разделом Основы компонентов. Если нет — прочитайте его сначала.

Стиль именования событий

В отличие от компонентов и входных параметров, имена событий не предоставляют никакой автоматической трансформации стиля именования события. Вместо этого, имя генерируемого события должно точно соответствовать имени, используемому при прослушивании события. К примеру, если генерируем событие с именем в camelCase:

this.$emit('myEvent')

Прослушивание kebab-cased версии этого имени не будет иметь никакого эффекта:

 
my-component v-on:my-event="doSomething">
my-component>

В отличие от компонентов и входных параметров, имена событий никогда не будут использоваться в качестве имён переменных или имён свойств в JavaScript, поэтому нет причин использовать camelCase или PascalCase. Кроме того, директивы прослушивания событий v-on внутри DOM-шаблонов автоматически преобразуются в нижний регистр (из-за нечувствительности HTML к регистру), поэтому v-on:myEvent станет v-on:myevent — что делает прослушивание события myEvent невозможным.

По этим причинам мы рекомендуем всегда использовать kebab-case для имён событий.

Настройка v-model у компонента

По умолчанию v-model на компоненте использует входной параметр value и событие input . Но некоторые типы полей, такие как чекбоксы или радиокнопки, могут использовать атрибут value для других целей. Использование опции model позволит избежать конфликта в таких случаях:

Vue.component('base-checkbox', { 
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: `

type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
})

Теперь, когда используем v-model на этом компоненте:

base-checkbox v-model="lovingVue"> base-checkbox> 

значение lovingVue будет передано во входном параметре checked . А обновляться свойство lovingVue будет когда сгенерирует событие change с новым значением.

Обратите внимание, что вам всё равно нужно объявлять входной параметр checked в опции props компонента.

Подписка на нативные события в компонентах

Иногда нужно подписаться на нативные события браузера на корневом элементе компонента. В таких случаях можно применять модификатор .native для v-on :

base-input v-on:focus.native="onFocus"> base-input> 

Иногда это может быть полезно, но это не очень хорошая идея, когда вы пытаетесь прослушивать очень специфичный элемент, к примеру . Например, компонент можно переделать так, чтобы корневой элемент был фактически элементом :

label> 
{{ label }}
input
v-bind="$attrs"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
label>

В этом случае слушатель .native в родителе просто тихо перестанет работать. Ошибок не будет, но обработчик onFocus не будет вызываться, когда мы этого ожидаем.

Для решения этой проблемы Vue предоставляет свойство $listeners , содержащее объект всех слушателей, которые используются на компоненте. Например:

{ 
focus: function (event) { /* . */ }
input: function (value) { /* . */ },
}

Используя свойство $listeners , вы можете передать все слушатели событий на компоненте на определённый дочерний элемент с помощью v-on=»$listeners» . Для таких элементов как , вы также можете захотеть работоспособности с v-model , для чего бывает полезно создать новое вычисляемое свойство для слушателей, например inputListeners указанный ниже:

Vue.component('base-input', { 
inheritAttrs: false,
props: ['label', 'value'],
computed: {
inputListeners: function ( ) {
var vm = this
// `Object.assign` объединяет объекты вместе, чтобы получить новый объект
return Object.assign({},
// Мы добавляем все слушатели из родителя
this.$listeners,
// Затем мы можем добавить собственные слушатели или
// перезаписать поведение некоторых существующих.
{
// Это обеспечит, что будет работать v-model на компоненте
input: function (event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `

{{ label }}

v-bind="$attrs"
v-bind:value="value"
v-on="inputListeners"
>
`
})

Теперь компонент стал полностью прозрачной обёрткой, что означает, что его можно использовать точно также, как и обычный элемент : все те же атрибуты и слушатели будут работать без указания модификатора .native .

Модификатор .sync

В некоторых случаях нам может понадобиться «двусторонняя привязка» для входных параметров. К сожалению, настоящая двусторонняя привязка может создавать проблемы с поддержкой, поскольку дочерние компоненты смогут мутировать состояние родителя без источника, который является очевидным как у родителя, так и у потомков.

Поэтому вместо этого, мы рекомендуем генерировать события с определённым шаблоном имени update:myPropName . Например, в гипотетическом компоненте с входным параметром title , мы можем сообщить о намерении присвоить новое значение:

this.$emit('update:title', newTitle)

Затем, родитель может прослушать это событие и обновить локальное свойство, если захочет. Например:

text-document
v-bind:title="doc.title"
v-on:update:title="doc.title = $event"
> text-document>

Для удобства мы предлагаем краткую запись для этого шаблона с помощью модификатора .sync :

text-document v-bind:title.sync="doc.title"> text-document> 

Обратите внимание, что v-bind с модификатором .sync не работает с выражениями (например, v-bind:title.sync=”doc.title + ‘!’” не будет работать). Вместо этого нужно указывать только имя свойства, которое хотите привязать, аналогично v-model .

Модификатор .sync также может использоваться вместе с v-bind при использовании объектной записи, чтобы сразу устанавливать значения нескольких входных параметров:

text-document v-bind.sync="doc"> text-document> 

Это передаёт каждое свойство в объекте doc (например, title ) в качестве индивидуальных входных параметров, а затем добавляет слушатели событий v-on для каждого из них.

Использование v-bind.sync с литеральным объектом, например таким как v-bind.sync=”{ title: doc.title }” , не будет работать, потому что необходимо будет учитывать слишком много различных пограничных случаев при анализе подобного сложного выражения.

Обнаружили ошибку или хотите добавить что-то своё в документацию? Измените эту страницу на GitHub! Опубликовано на Netlify .

Зачем использовать $emit во Vue если можно просто передать метод в пропсах?

Зачем мне все усложнять, если я могу сделать все проще и получить тот же результат? Может я чего то не понял?

Отслеживать
задан 3 июн 2022 в 9:57
Антон Гуменов Антон Гуменов
21 2 2 бронзовых знака

Потому что у Вас событие может быть вызвано в зависимости от состояния. Не только по стандартным событиям типа клика или ховера. Кнопка на изменение фаворита если внутри, то Вы и не вызовете никак Ваш метод на изменение. Если снаружи — эмиты вам и не нужны.

7 июн 2022 в 7:27

Всё сильно зависит от изолированности компонентов. Если Вы будете несколько раз эти фавориты использовать — ну и будете везде логику однотипную плодить. Не по DRY это как-то.

# Пользовательские события

Подразумевается, что уже изучили и разобрались с разделом Основы компонентов. Если нет — прочитайте его сначала.

# Стиль именования событий

Также как для компонентов и входных параметров, для имён событий выполняется автоматическое преобразование регистра. Если событие генерируется в дочернем компоненте в camelCase, то в родительском потребуется прослушивать в kebab-case:

this.$emit('myEvent') 
my-component @my-event="doSomething">my-component> 

Как и в случае с регистром входных параметров, рекомендуется использовать kebab-case при использовании DOM-шаблонов. Для строковых шаблонов этого ограничения не будет.

# Определение пользовательских событий

События генерируемые компонентом можно объявить с помощью опции emits .

app.component('custom-form',  emits: ['inFocus', 'submit'] >) 

Если в опции emits объявить нативное событие (например, click ), то будет использоваться событие компонента вместо отслеживания нативного события.

Рекомендуется объявлять все генерируемые события, чтобы лучше задокументировать как должен работать компонент.

# Валидация сгенерированных событий

Аналогично валидации входных параметров, также можно валидировать и генерируемые события, если они объявлены с помощью объектного синтаксиса или синтаксиса массива.

Для добавления валидации события нужно указать функцию, которая получит те аргументы, с которыми был вызван $emit , и возвращает булево, чтобы сообщить является ли событие корректным или нет.

app.component('custom-form',  emits:  // Без валидации click: null, // Валидация события submit submit: ( email, password >) =>  if (email && password)  return true > else  console.warn('Некорректные данные для генерации события submit!') return false > > >, methods:  submitForm(email, password)  this.$emit('submit',  email, password >) > > >) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Аргументы v-model

При использовании v-model на компоненте по умолчанию используется входной параметр modelValue и событие update:modelValue . Их можно изменить через аргумент v-model :

my-component v-model:title="bookTitle">my-component> 

Для такой записи дочерний компонент будет ожидать входной параметр title и должен будет генерировать событие update:title для синхронизации значения:

app.component('my-component',  props:  title: String >, emits: ['update:title'], template: ` input type="text" :value="title" @input="$emit('update:title', $event.target.value)"> ` >) 

1
2
3
4
5
6
7
8
9
10
11
12

# Использование нескольких v-model

Развивая потенциал возможности настройки используемого входного параметра и события, как изучили выше с помощью аргумента v-model , теперь стало возможным одновременно использовать несколько привязок v-model на одном экземпляре компонента.

Каждая v-model будет синхронизироваться со своим входным параметром, не требуя дополнительных настроек в компоненте:

user-name v-model:first-name="firstName" v-model:last-name="lastName" >user-name> 
app.component('user-name',  props:  firstName: String, lastName: String >, emits: ['update:firstName', 'update:lastName'], template: ` input type="text" :value="firstName" @input="$emit('update:firstName', $event.target.value)"> input type="text" :value="lastName" @input="$emit('update:lastName', $event.target.value)"> ` >) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Обработка модификаторов v-model

Изучая работу с формами, можно заметить, что у v-model есть встроенные модификаторы — .trim , .number и .lazy . Но в некоторых случаях может потребоваться добавить пользовательские модификаторы.

Создадим для примера пользовательский модификатор capitalize , который станет делать заглавной первую букву строки, привязанной с помощью v-model .

Модификаторы, которые будут использоваться в v-model компонента нужно указывать через входной параметр modelModifiers . В примере ниже, у компонента есть входной параметр modelModifiers , который по умолчанию будет пустым объектом.

Обратите внимание, в хуке жизненного цикла created входной параметр modelModifiers будет содержать capitalize со значением true — потому что он используется на привязке v-model компонента v-model.capitalize=»myText» .

my-component v-model.capitalize="myText">my-component> 
app.component('my-component',  props:  modelValue: String, modelModifiers:  default: () => (>) > >, emits: ['update:modelValue'], template: ` input type="text" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)"> `, created()  console.log(this.modelModifiers) // > >) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

После настройки входного параметра теперь можно проверять ключи modelModifiers и реализовать обработчик для модификации значения. В примере ниже обработчик будет запускаться каждый раз, когда элемент генерирует событие input .

div id="app"> my-component v-model.capitalize="myText">my-component> > div> 
const app = Vue.createApp( data()  return  myText: '' > > >) app.component('my-component',  props:  modelValue: String, modelModifiers:  default: () => (>) > >, emits: ['update:modelValue'], methods:  emitValue(e)  let value = e.target.value if (this.modelModifiers.capitalize)  value = value.charAt(0).toUpperCase() + value.slice(1) > this.$emit('update:modelValue', value) > >, template: `input type="text" :value="modelValue" @input="emitValue">` >) app.mount('#app') 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

Если используется привязка v-model с аргументом, то имя входного параметра будет генерироваться таким образом arg + «модификатор» :

my-component v-model:description.capitalize="myText">my-component> 
app.component('my-component',  props: ['description', 'descriptionModifiers'], emits: ['update:description'], template: ` input type="text" :value="description" @input="$emit('update:description', $event.target.value)"> `, created()  console.log(this.descriptionModifiers) // > >) 

1
2
3
4
5
6
7
8
9
10
11
12
13

(opens new window)
Последнее обновление страницы: почти 2 года назад

Saved searches

Use saved searches to filter your results more quickly

Cancel Create saved search

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

vuejs / vue Public

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to emit event from component inside slot #4332

purepear opened this issue Nov 28, 2016 · 32 comments

Is it possible to emit event from component inside slot #4332

purepear opened this issue Nov 28, 2016 · 32 comments

Comments

purepear commented Nov 28, 2016 •

Vue.js version

Reproduction Link

Events emitted from component inside slot are not received.
$emit(‘testevent’) is not working but $parent.test() works.

script src pl-s">https://unpkg.com/vue@2.1.3/dist/vue.js">script> div id pl-s">app" > parent> child>child> parent> div>
Vue.component('parent',  template: ` `, methods:  test ()  alert('Test ok') > > >) Vue.component('child',  template: `` //template: `` >) new Vue( el: '#app', >)

The text was updated successfully, but these errors were encountered:

yyx990803 closed this as completed Nov 29, 2016
BartCorremans mentioned this issue Jan 24, 2017
guykatz commented Jul 31, 2017 •

not trying to wake this up from the dead but.
image I want a wrapper custom component that shows a loading spinner and has a slot (slot container). the spinner should be removed when an event is emitted from a component that occupies the slot (slot child).
the parent’s template looks something like:

the child’s code looks something like:

 mounted: function () < this.$emit('loadStatusChange'); >>, template:'some template txt' 

the usage is something like this:

in order for the slot child to fulfill the parent’s interface, they should emit an event to turn the spinner to inactive. One can argue that using $parent.emit with $on (per suggestion @yyx990803 above) complicates the issue as you need to take into consideration the life cycle of components into this. if, for example:
IT the slot child emits the event on mount than the slot container should register for it in earlier phases (not mount) because the child is mounted before the parent container and so in such a case, the event will be fired but missed by the parent.
Now, if you passed the hurdle above, you find yourself in an odd situation because you want to remove the spinner on the slot container usually after it has been mounted (thats the whole point). so now you need some code trickery on the parent in order to see if the ‘clear spinner’ event was received AND the the parent component is mounted.

long story short, in order to get the full slot power I do believe some sort of a mechanism should be introduced.
I am no JS expect by any means, this is just from the short experience I have with vue.
your thoughts are welcomed

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *