Vue: Улучшенный API для компонентов пользовательского интерфейса, сокращение шаблонов для пересылки атрибутов и событий.

Созданный на 27 июн. 2017  ·  46Комментарии  ·  Источник: vuejs/vue

Какую проблему решает эта функция?

Во многих случаях атрибуты, передаваемые компоненту Vue, следует добавлять не к корневому элементу, а к подэлементу. Например, в этом компоненте пользовательского интерфейса необходимо использовать невероятное количество свойств, чтобы гарантировать добавление атрибутов к элементу input вместо оболочки div .

Кроме того, часто желательно предоставить родительскому элементу все прослушиватели событий в элементе формы, что также требует большого количества шаблонов в настоящее время, если элемент не является корневым (в этом случае модификатор .native может решить проблему. ).

Как выглядит предлагаемый API?

РЕДАКТИРОВАТЬ: начните здесь, чтобы продолжить обсуждение.

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

  • v-expose (наверное, мой личный фаворит)
  • v-expose-attrs (возможно, яснее, но длиннее)
  • v-main
  • v-primary

Если к элементу добавляется v-expose , он примет атрибуты, переданные его компоненту, и эти атрибуты __дольше__ не будут передаваться корневому элементу.

Другие функции, которые могут быть приятными:

  • Если директива определена для нескольких элементов, атрибуты будут дублироваться для каждого из них.
  • В случае, когда элемент должен принимать подмножество атрибутов, v-expose может принимать строку или массив строк (например, v-expose="class" или v-expose="['class', 'type', 'placeholder']" ). В этом случае эти атрибуты будут добавлены к элементу (опять же, а не к корневому элементу), но все остальные атрибуты будут добавлены к корневому элементу или к элементу (элементам) с бесполезным значением v-expose .
discussion feature request

Самый полезный комментарий

@chrisvfritz, как это будет работать в функциях рендеринга?

Думаю, может быть, лучше:

  • предоставить возможность отключить автоматическое наследование атрибутов для корневого узла
  • выставьте эти атрибуты как $attributes , например (имя tbd)
  • используйте v-bind, чтобы добавить их куда угодно, как мы уже показали, как это сделать с $props :
v-bind="$attributes"

Это будет иметь дополнительное преимущество - работать практически идентично в функциях JSX / рендеринга.

Все 46 Комментарий

Хм, я не знаю об этом, но для таких компонентов, я думаю, вы могли бы использовать 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)
  • используйте v-bind, чтобы добавить их куда угодно, как мы уже показали, как это сделать с $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 будет намного сложнее, а использование вызывает сомнения, поскольку собственный прослушиватель событий, примененный к корневому элементу, все равно будет улавливать всплытие желаемого события, поэтому он может не необходимо назначить его конкретному элементу в шаблоне.

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

  1. Отключение стандартного поведения «автоматического применения привязок, которые не встречаются в реквизитах в качестве атрибутов корневого элемента» (связанная проблема: должно ли это повлиять style привязки class и style ?)

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

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 Я только что открыл проблему, в которой мы можем сосредоточить дальнейшее обсуждение этой проблемы.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги