Еще один пример абстрактного компонента Vue.js — используем render

Еще один пример абстрактного компонента Vue.js - используем render

От автора: всем привет! Я front-end разработчик и надеюсь, что вы тоже программист. Как часто вам бывает необходимо внедрять абстракции в код JavaScript? Никогда? О, давайте изменим это! Нам поможет функция Vue.js render.

Знаете ли вы что-нибудь об Intersection Observers API? Если да, отлично! Вы можете забить в Google «Написание абстрактных компонентов Vue.js» и получить множество статей с примерами написания отложенных компонентов для достижения эффективности рендеринга страницы в DOM. Я не буду обсуждать это здесь, давайте рассмотрим мой собственный пример.

Я и моя команда слишком часто внедряем новые функции, некоторые из них поступают в производство через 2-3 дня. Чтобы не пугать пользователей непонятными функциями и новыми ошибками, мы скрываем это за правами доступа и показываем только для администраторов. Когда администратор считает, что сейчас самое время показать это пользователям, он меняет конфигурацию в панели доступов и открывает остальные функции.

Хорошо, давайте напишем код. Позвольте представить вам простую функцию:

const EDITABLE = 2;
const VISIBLE = 1;
const HIDDEN = 0; function userHasPermission('permissionName', EDITABLE) { … }

Как мы можем использовать эту функцию в качестве вычисляемого метода в шаблонах Vue?

<template> <div> <my-text-input v-if="userHasPermission('team.internalNotes', VISIBLE)" label="Team Internal Notes" :disabled="!userHasPermission('team.internalNotes', EDITABLE)" /> <my-text-input v-if="userHasPermission('team.budget', VISIBLE)" label="Total Team Budget" :disabled="!userHasPermission('team.budget', EDITABLE)" /> <my-time-input v-if="userHasPermission('team.trainingTime', VISIBLE)" label="Next Training Time" :disabled="!userHasPermission('team.trainingTime', EDITABLE)" /> </div>
</template>

Основная идея такова: если у пользователя есть доступ только для чтения, он может видеть поле, но оно будет отключено. Если у пользователя нет на это прав, он ничего не увидит. Какие проблемы в этой части шаблона?

Во-первых, это слишком сложно для чтения. Мы вызываем функцию userHasPermission два раза для каждого поля. Второе: представьте ситуацию, когда вам нужно добавить дополнительные правила для отключения поля. Например, если у команды нет больше тренингов, поле «Next Training Time» необходимо отключить, не запрашивая никакого разрешения. Без решения, которое я предлагаю вам в этой статье, возможно, это выглядит так:

<template> <div> <my-time-input v-if="userHasPermission('team.trainingTime', VISIBLE)" label="Next Training Time" :disabled="isNextTimeDisabled" /> </div>
</template> <script> export default { name: 'football-team-form', computed: { isNextTraningTimeDisabled() { return !hasNextTraining || !userHasPermission('team.traningTime', EDITABLE); }, }, }
</script>

В этом коде есть путаница с ответственностями! И как решить нашу проблему? Хотя мы связываем одно конкретное разрешение с одним или несколькими полями, нам нужно иметь возможность связывать разрешение и поле в одной строке кода. Представьте, что у нас есть абстрактный компонент (например, transition или keep-alive) с именем permission-control, которое может отвечать за поведение только с разрешениями. Нужно взять разрешение в свойстве и манипулировать с его производными. Посмотрите на пример использования:

<template> <div> <permission-control permission-name="team.internalNotes"> <my-text-input label="Team Internal Notes" /> </permission-control> <permission-control permission-name="team.budget"> <my-text-input label="Total Team Budget" /> </permission-control> <permission-control permission-name="team.trainingTime"> <my-time-input label="Next Training Time" /> </permission-control> </div>
</template>

И переработанный пример с дополнительными правилами для последнего поля:

<template> <div> <permission-control permission-name="team.trainingTime"> <my-time-input label="Next Training Time" :disabled="!hasNextTraining" /> </permission-control> </div>
</template> <script> export default { name: 'football-team-form', }
</script>

Оба примера выглядят очень чисто. Новый член команды нашего проекта может легко понять бизнес-логику этой формы. Какова будет реализация этого полезного (надеюсь) permission-control? Идея основана на возможностях функции render в фреймворке Vue.js и функционале слотов. Посмотрите на прототип:

<script> export default { name: 'permission-control', props: ['permission-name'], render() { // Случай EDITABLE. Не изменяйте ничего, а просто отобразите элемент управления, как есть. if (userHasPermission(this.permissionName, EDITABLE)) { return this.$slots.default[0]; } // Если пользователь не имеет прав EDITABLE, но имеет 'VISIBLE', // давайте изменим значение свойства "disabled" на true if (userHasPermission(this.permissionName, VISIBLE)) { this.$slots.default[0].componentOptions.propsData.disabled = true; return this.$slots.default[0]; } // Пользователь не имеет никаких прав! Ничего ему не выводим! return null; } } </script>

Есть ли ограничения у этого кода? Вам нужно обернуть только один элемент управления в этот компонент, все элементы управления должны поддерживать имя свойства disabled и элемент управления должен существовать. Если у вас есть дополнительные правила, чтобы скрыть управление атрибутом v-if, вы получите ошибку Can not read property 0 of undefined. В любом случае вы можете избежать этой ошибки, если поместите проверки дочерних элементов в верхнюю часть функции render:

if (!this.$slots.default) { return null;
}

Абстракция … Абстрактный компонент Vue… Я помню, что еще, связанное с этим, для intersection observer API…

Да вы правы! Vue имеет недокументированное ключевое слово abstract: true для абстрактных компонентов. Но я действительно не знаю, как оно меняет поведение. Я не вижу дополнительных комментариев в других статьях. Если я не знаю причину использования этого слова, я не могу просить вас использовать его. Это хороший повод исследовать данную тему в отдельной статье. Спасибо за внимание, друзья!

Автор: Michael Lelyakin

Источник: https://medium.com/

Редакция: Команда webformyself.