Во многих случаях атрибуты, передаваемые компоненту Vue, следует добавлять не к корневому элементу, а к подэлементу. Например, в этом компоненте пользовательского интерфейса необходимо использовать невероятное количество свойств, чтобы гарантировать добавление атрибутов к элементу input
вместо оболочки div
.
Кроме того, часто желательно предоставить родительскому элементу все прослушиватели событий в элементе формы, что также требует большого количества шаблонов в настоящее время, если элемент не является корневым (в этом случае модификатор .native
может решить проблему. ).
РЕДАКТИРОВАТЬ: начните здесь, чтобы продолжить обсуждение.
В настоящее время по умолчанию «открытый» элемент (тот, к которому могут быть добавлены произвольные атрибуты) всегда является корневым элементом. Новую директиву можно использовать для определения другого открытого элемента. Некоторые идеи для названия директивы:
v-expose
(наверное, мой личный фаворит)v-expose-attrs
(возможно, яснее, но длиннее)v-main
v-primary
Если к элементу добавляется v-expose
, он примет атрибуты, переданные его компоненту, и эти атрибуты __дольше__ не будут передаваться корневому элементу.
Другие функции, которые могут быть приятными:
v-expose
может принимать строку или массив строк (например, v-expose="class"
или v-expose="['class', 'type', 'placeholder']"
). В этом случае эти атрибуты будут добавлены к элементу (опять же, а не к корневому элементу), но все остальные атрибуты будут добавлены к корневому элементу или к элементу (элементам) с бесполезным значением v-expose
.Хм, я не знаю об этом, но для таких компонентов, я думаю, вы могли бы использовать JSX или createElement
для распространения свойств.
https://github.com/doximity/vue-genome
Для нас это было бы здорово. Мы оборачиваем все входные данные в метку для стилизации и пользовательского интерфейса. Я согласен с тем, что вместо этого мы могли бы перейти к jsx, но всем гораздо проще следовать шаблонам.
@Austio , к сожалению, это spread
props в шаблоны vue?
Мне лично нравится эта функция. Но, похоже, это нарушает согласованность поведения v-bind, например, иногда мне все еще нужно привязать свойство class
для корневого элемента.
Итак, как насчет использования пары директив в качестве получателя и установщика, например:
Внутри компонента определите привязку v-expose
:
<input v-expose="foo" />
При его использовании:
<the-component v-define:foo="{propA: '', propB: ''}"></the-component>
<!-- or maybe use v-bind for it directly -->
<the-component :foo="{propA: '', propB: ''}"></the-component>
@jkzing , это выглядит потрясающе, но опять же, это похоже на базовый спред с потенциальными проблемами, например, как бы вы определили @keyup.enter.prevent="myAction"
?
вы не можете просто <the-component :foo="{'@keyup.enter.prevent': myAction}"></the-component>
, это означает, что вам придется сохранить все модификаторы, такие как enter
и prevent
во время выполнения (который является частью vue-template-compiler атм)
@nickmessing
это похоже на базовый спред
Речь идет о том, чтобы предоставить пользователям шаблонов что-то вроде распространения.
<the-component :foo="{'@keyup.enter.prevent': myAction}"></the-component>
@
- шортленд v-on, не означает prop
(v-bind).
@jkzing , в ссылке из описания тоже много v-on
привязок
@nickmessing Гм ... Что касается привязок v-on
, это другая тема, IMO, например, всплытие событий. 🤔
@jkzing , это была вся концепция v-expose
afaik, чтобы все свойства «переходили» к определенному элементу в компоненте.
@nickmessing , не могу быть уверен в исходном предложении, но я не думаю, что слушатель событий следует рассматривать как attribute
.
@jkzing , вероятно, нет, но, учитывая общий пример <my-awesome-text-input />
где у вас может быть> 9000 различных реквизитов, вы просто хотите, чтобы они все добрались до вашего <input />
внутри вашего пользовательского компонента без тонны кода.
Я лично использую v-bind="$props"
или вы можете отфильтровать их, чтобы исключить реквизиты, которые вы не хотите применять. Таким образом, вы можете применить к входу несколько свойств одновременно. Действительно, v-expose может быть полезен, потому что для компонентов оболочки, таких как входы, вы должны указать все эти html-реквизиты
Так что это
https://github.com/almino/semantic-ui-vue2/blob/master/src/elements/Input.vue#L9
может быть уменьшено до v-bind="$props"
или v-bind="filteredProps"
где filterProps может быть некоторым вычисляемым свойством
@cristijora Мы тоже используем v-bind="someProps"
. Проблема с этим решением заключается в том, что избыточные свойства будут добавлены как атрибуты HTML. Было бы здорово, если бы v-bind=
мог отфильтровать все свойства, которые не принимаются компонентом. С динамическим <component>
мы не знаем, какие свойства фильтровать в вычисляемом свойстве. Хотя можно извлечь options.props
и использовать lodash._pick
.
Это реально возможно с помощью директивы?
@posva , я не думаю, что это будет работать как директива как таковая, но это может быть частью механизма шаблонов vue, который делает что-то вроде внутреннего распространения + некоторое распространение событий
@posva Я не думаю, что это директива,
@chrisvfritz есть ли у вас какие-либо мысли по
Я мог видеть, что это похоже на использование концепции предоставления / внедрения.
@Austio Возможно, я не понимаю вопроса, но я
Привет, Крис, имел в виду дополнительные мысли об использовании подобного для предоставления инъекции, где вы объявляете, что должно быть отображено в родительском элементе, а затем используете это в дочернем элементе.
Ах я вижу. Я не уверен, что в этом есть необходимость. Информация уже может быть передана через реквизиты и слоты - и даже частные свойства родительского объекта могут быть доступны с помощью this.$parent
, хотя я думаю, что лучше избегать этого шаблона.
@Austio Есть ли конкретный вариант использования, о котором вы думаете?
@chrisvfritz, как это будет работать в функциях рендеринга?
Думаю, может быть, лучше:
$attributes
, например (имя tbd)$props
:v-bind="$attributes"
Это будет иметь дополнительное преимущество - работать практически идентично в функциях JSX / рендеринга.
@LinusBorg Мне нравится, как ты думаешь. 😄 Ваш путь намного более интуитивен.
В качестве примечания, я думаю, что с этим API следующая основная версия Vue может даже полностью удалить автонаследование атрибутов, так что межкомпонентная связь может оставаться явной с обеих сторон.
Можно было бы обесценить или удалить это поведение, да.
Если это стоит, необходимо решить, возможно, необходимые изменения во многих компонентах в библиотеках и т. Д., И их следует обсудить с сообществом, особенно с авторами коллекций пользовательского интерфейса.
А о предполагаемой функции: эта информация уже доступна в функциональных компонентах через context.data.attributes
, поэтому эта функция предоставит в основном идентичную функциональность компонентам экземпляра.
Да, точно. Основная цель, которую я имею в виду, - упростить работу авторов компонентов пользовательского интерфейса (как сторонних, так и внутренних). Сейчас очень много случаев, когда необходимо что-то вроде этого:
<input
v-bind:id="id"
v-bind:accept="accept"
v-bind:alt="alt"
v-bind:autocomplete="autocomplete"
v-bind:autofocus="autofocus"
v-bind:checked="checked"
v-bind:dirname="dirname"
v-bind:disabled="disabled"
v-bind:form="form"
v-bind:formaction="formaction"
v-bind:formenctype="formenctype"
v-bind:formmethod="formmethod"
v-bind:formnovalidate="formnovalidate"
v-bind:formtarget="formtarget"
v-bind:list="list"
v-bind:max="max"
v-bind:maxlength="maxlength"
v-bind:min="min"
v-bind:multiple="multiple"
v-bind:name="name"
v-bind:pattern="pattern"
v-bind:placeholder="placeholder"
v-bind:readonly="readonly"
v-bind:required="required"
v-bind:src="src"
v-bind:step="step"
v-bind:type="type"
v-bind:value="value"
v-on:keydown="emitKeyDown"
v-on:keypress="emitKeyPress"
v-on:keyup="emitKeyUp"
v-on:mouseenter="emitMouseEnter"
v-on:mouseover="emitMouseOver"
v-on:mousemove="emitMouseMove"
v-on:mousedown="emitMouseDown"
v-on:mouseup="emitMouseUp"
v-on:click="emitClick"
v-on:dblclick="emitDoubleClick"
v-on:wheel="emitWheel"
v-on:mouseleave="emitMouseLeave"
v-on:mouseout="emitMouseOut"
v-on:pointerlockchange="emitPointerLockChange"
v-on:pointerlockerror="emitPointerLockError"
v-on:blur="emitBlur"
v-on:change="emitChange($event.target.value)"
v-on:contextmenu="emitContextMenu"
v-on:focus="emitFocus"
v-on:input="emitInput($event.target.value)"
v-on:invalid="emitInvalid"
v-on:reset="emitReset"
v-on:search="emitSearch"
v-on:select="emitSelect"
v-on:submit="emitSubmit"
>
Новое свойство $attributes
могло бы сократить его до следующего:
<input
v-bind="$attributes"
v-on:keydown="emitKeyDown"
v-on:keypress="emitKeyPress"
v-on:keyup="emitKeyUp"
v-on:mouseenter="emitMouseEnter"
v-on:mouseover="emitMouseOver"
v-on:mousemove="emitMouseMove"
v-on:mousedown="emitMouseDown"
v-on:mouseup="emitMouseUp"
v-on:click="emitClick"
v-on:dblclick="emitDoubleClick"
v-on:wheel="emitWheel"
v-on:mouseleave="emitMouseLeave"
v-on:mouseout="emitMouseOut"
v-on:pointerlockchange="emitPointerLockChange"
v-on:pointerlockerror="emitPointerLockError"
v-on:blur="emitBlur"
v-on:change="emitChange($event.target.value)"
v-on:contextmenu="emitContextMenu"
v-on:focus="emitFocus"
v-on:input="emitInput($event.target.value)"
v-on:invalid="emitInvalid"
v-on:reset="emitReset"
v-on:search="emitSearch"
v-on:select="emitSelect"
v-on:submit="emitSubmit"
>
Хотя тогда, я полагаю, было бы неплохо иметь способ также разоблачать события. Может быть, пустая директива v-on
может перенаправить всех слушателей событий родительского элемента на этот элемент?
<input
v-bind="$attributes"
v-on
>
Или, если в конечном итоге возникнет несколько проблем, которые мы хотим объединить, мы можем вернуться к чему-то вроде v-expose
:
<input v-expose>
Это превратилось в более широкое обсуждение того, как упростить создание компонентов пользовательского интерфейса, а не в запрос конкретной функции, поэтому я переименую эту проблему. 🙂
Я опаздываю в эту тему, но у меня тоже есть кое-какие мысли.
v-bind
Текущее решение и недостаткиВо-первых, я уже использую и мне нравится функция v-bind="propObject"
(такая мощная). Например, bootstrap-vue имеет компонент внутренней ссылки, который используется везде (кнопки, навигация, раскрывающиеся списки и т. Д.). Компонент поворачивается, превращаясь в собственный якорь по сравнению с ссылкой маршрутизатора на основе href
сравнению с to
и наличием vm.$router
, поэтому существует довольно много свойств, которые можно условно передать каждому этих компонентов.
Наше решение заключалось в том, чтобы поместить эти реквизиты в миксин и использовать v-bind="linkProps"
с вычисляемым объектом. Это прекрасно работает, но по-прежнему требует много накладных расходов, добавляя этот миксин _все другие компоненты, использующие компонент ссылки_.
v-expose
Возможности использования v-bind
Мне лично нравится концепция v-expose
, и, возможно, она могла бы работать как слот по умолчанию + именованные слоты, а затем использовать модификаторы для доступа к именованным слотам атрибутов.
Атрибут по умолчанию _ "slot" _ всегда будет передавать атрибуты самому компоненту (без изменений), в то время как другие именованные цели могут быть указаны компонентом. Возможно что-то вроде этого:
<template>
<my-component
<!-- Nothing new here -->
v-bind="rootProps"
<!-- This binds the `linkProps` object to the named attribute slot `link` -->
v-bind.link="linkProps"
/>
</template>
Внутри MyComponent.vue
:
<template>
<div>
<router-link v-expose="link" />
</div>
</template>
Мне нечего добавить сюда, за исключением того, что .native
- потрясающе мощный модификатор. Решил для меня много проблем. Тем не менее, это кажется в значительной степени неизвестным разработчикам Vue (я вижу большое количество проблем с библиотекой пользовательского интерфейса, которые можно решить, открыв разработчикам эту функцию). Я разместил PR на веб-сайте, чтобы добавить дополнительную документацию и поддержку поиска на сайте, который потенциально оптимизирован для поиска Google.
Исходя из аргумента о поверхности API в другом выпуске, я должен повторить, что я не поклонник идеи v-expose
. он вводит другой "способ работы" и не работает для JSX без реализации чего-то особенного и т. д.
Одна вещь, которую я уважаю в ребятах из React, - это их приверженность тонкому API и максимальное использование возможностей языка. В этом духе повторное использование уже имеющегося у нас шаблона для свойств для атрибутов кажется намного лучше, чем введение другой абстракции.
<my-input
type="file"
mode="dropdown"
>
<template>
<div>
<input v-bind="$attributes">
<dropdown v-bind="{ ...$props, $attributes.type }"/>
</div>
</template
Ах, теперь я понимаю, о чем вы говорите. И мне нравится это! Это в настоящее время доступно? vm.$attributes
вместо этого будет добавлением?
Перечитываю ваши комментарии. Я сейчас отслеживаю 👍
Да, $attributes
будет добавлением.
Кроме того, нам понадобится опция, позволяющая отключить текущее поведение по умолчанию при применении атрибутов к корневому элементу, например:
`` html
Затем это может стать настройкой по умолчанию в Vue 3.0, если мы затем решим это сделать, что приведет к критическому изменению.
@LinusBorg Что вы думаете по поводу событийной стороны вещей? Чтобы следовать той же стратегии, я предположил, что мы могли бы также добавить свойство $listeners
, которое могло бы выглядеть так:
{
input: function () { /* ... */ },
focus: function () { /* ... */ },
// ...
}
Тогда, возможно, v-on
сможет принять объект, похожий на v-bind
. Итак, у нас было бы:
<input v-bind="$attributes" v-on="$listeners">
Одна проблема, которую я предвижу, связана с событиями input
/ change
, поскольку v-model
работает для компонентов несколько иначе, чем для элементов. Я также не знаю, нужны ли нам и $listeners
и $nativeListeners
. Я полагаю, если бы $listeners
были доступны, то модификатор .native
мог бы быть устаревшим.
Кроме того, что касается опции applyComponentAttrsToRoot
, возможно, exposeRootEl
будет хорошим именем, которое при установке на false
может отключить как автоматически применяемые атрибуты, так и событие .native
пересылка?
Также было бы неплохо иметь возможность отключить это для всего приложения с помощью Vue.config
, а также для одного компонента.
Недавно у меня возникла аналогичная идея о $listeners
- она также доступна для функциональных компонентов через
context.data.listeners
Таким образом, мы получили бы $props
, $attributes
, $listeners
что мне подходит.
Там также # 5578 запрашивает синтаксис объекта v-on="{...}"
как я использовал для $attributes
, он подойдет.
Но насчет модификатора .native я не уверен. Чтобы заставить эту работу работать как с компонентными событиями, так и с собственными прослушивателями, API будет намного сложнее, а использование вызывает сомнения, поскольку собственный прослушиватель событий, примененный к корневому элементу, все равно будет улавливать всплытие желаемого события, поэтому он может не необходимо назначить его конкретному элементу в шаблоне.
В общем, я бы сказал, что для низкоуровневых библиотек компонентов предпочтительнее использовать функции рендеринга, когда с шаблонами становится неудобно работать. Но я согласен с тем, что было бы полезно следующее:
Отключение стандартного поведения «автоматического применения привязок, которые не встречаются в реквизитах в качестве атрибутов корневого элемента» (связанная проблема: должно ли это повлиять style
привязки class
и style
?)
предоставление более простого способа «наследования» внешних привязок компонента к внутреннему элементу, который не обязательно является корневым. В идеале с согласованностью между шаблонами и функциями рендеринга.
ia kie, как vue, простые инструменты
Сразу хочу сказать, что PR в версии 2.4 отличный! 👍
Из примечания к выпускам
Их объединение позволяет упростить такой компонент до следующего:
<div>
<input v-bind="$attrs" v-on="$listeners">
</div>
Выглядит неплохо, но это не совсем так, поскольку такие компоненты предназначены для работы с v-моделью, и, насколько я знаю, v-модель для компонента упаковки не работает. Есть ли какой-либо пример того, как, например, перенаправить v-модель из компонента упаковки на вход?
Самый простой способ нашел:
https://jsfiddle.net/60xdxh0h/2/
Возможно, с функциональным компонентом, работающим по шаблону, было бы проще
такого рода компоненты предназначены для работы с v-моделью, и, насколько я знаю, v-model на компоненте упаковки не работает в стандартном режиме.
Почему ты так думаешь? v-model - это просто синтаксический сахар для свойства и прослушивателя событий, оба будут в $ attr / $ props и, таким образом, могут быть легко переданы.
Я полагаю, единственное, что потребует знания дочерних параметров, - это если бы ребенок изменил значения по умолчанию model
, это правда.
Но их можно было бы прочитать, в зависимости от обстоятельств.
Конечно, это синтаксический сахар, но я имею в виду, что читать
Их объединение позволяет упростить такой компонент до следующего:
когда на самом деле на основе примера https://github.com/almino/semantic-ui-vue2/blob/master/src/elements/Input.vue , вы не можете просто передавать слушателей напрямую для достижения того же контроля. (например: вы должны использовать v-on: input = "emitInput ($ event.target.value)")
В любом случае, это ценный пиар, хорошая работа!
@AlexandreBonaventure Это потому, что v-model
работает с компонентами несколько иначе, чем с элементами. События DOM предоставляют объект события для обратных вызовов, тогда как события компонента предоставляют значение напрямую. В результате v-model
_does_ работает, но связанным значением является объект события DOM. 😕
Я думаю, вы правы, что здесь было бы желательно, чтобы v-model
работал, но я не уверен, где лучше всего решить эту проблему. Некоторые идеи:
Возможно, к $listeners
можно добавить неперечислимое свойство (например, __$listeners__: true
, чтобы помочь v-on
обнаруживать использование v-on="$listeners"
. Затем в тех случаях, когда $listeners
объект передается в v-on
, каждый слушатель может быть обернут:
function (event) {
listener(event.target.value)
}
Один недостаток - теперь мы выбрасываем данные. Например, если кто-то хочет получить доступ к keyCode
, ему не повезло. Однако, если бы модификаторы поддерживались для синтаксиса объекта v-on
, мы могли бы исправить это, отключив функцию автоматического переноса в .native
.
@ yyx990803 @LinusBorg Что вы думаете о возможности? Какие-нибудь крайние случаи, которые мне не хватает?
О, понятно, вы имеете в виду v-model на Rral. Элементы формы, я думал об этом на компонентах.
Вы не можете / не должны использовать это на реквизите в любом случае, с этим PR или без него. А в продвинутых приложениях его использовать довольно редко (хотя и достижимо).
@LinusBorg Просто хочу убедиться, что мы на одной странице. Учитывая компонент CustomInput
с этим шаблоном:
<div>
<input v-bind="$attrs" v-on="$listeners">
<div>
Вы не ожидали, что приведенный ниже код будет работать?
<CustomInput v-model="myValue" />
Я ожидал, что это сработает - но, как я понял Александра, он имел в виду v-модель элемента, а не компонента, которая в конечном итоге работает только с изменением локального состояния.
Я пытался сказать то, что @chrisvfritz объяснил в своем последнем посте. (Английский не мой родной язык, извините :))
@LinusBorg проблема с выполнением этого в последней версии заключается в том, что это все еще считается анти-шаблоном и вызывает предупреждение об изменении состояния.
Чрезвычайно полезно, чтобы вышеуказанное работало, когда свойство value не является строкой. Возьмем, например, комбо-компонент, в котором я пытаюсь использовать перечисления, импортированные из моей собственной библиотеки, в качестве значений для параметров выбора:
<template>
<select class="combo" v-model="value" v-on="$listeners">
<option v-for="(item, key) in items" :value="item">{{key}}</option>
</select>
</template>
<script>
export default {
props: {
items: {
type: Object,
required: true
},
value: {}
}
}
</script>
Это пример одного из списков, которые я использую для элементов в родительском элементе:
execList: {
"None": ACT_EXEC_TYPES.NONE,
"Function": ACT_EXEC_TYPES.FUNCTION,
"Code": ACT_EXEC_TYPES.CODE
}
И как я использую комбо-компонент:
<combo :items="execList" v-model="selectedAction.execType"/>
Я пытался сделать эту работу уже 2 дня и все еще очень расстраиваюсь. Проблема в том, что $ event.target.value всегда является строкой и не оценивается так, как должно быть в :value
.
@LinusBorg @AlexandreBonaventure @RobertBColton Я только что открыл проблему, в которой мы можем сосредоточить дальнейшее обсуждение этой проблемы.
Самый полезный комментарий
@chrisvfritz, как это будет работать в функциях рендеринга?
Думаю, может быть, лучше:
$attributes
, например (имя tbd)$props
:Это будет иметь дополнительное преимущество - работать практически идентично в функциях JSX / рендеринга.