vue-mods-names
Version:
Simple adding mods to all components class names in your vue app
364 lines (278 loc) • 19.4 kB
Markdown
## Введение
### Коротко о методологиях CSS
При разработке проектов нам часто приходится создавать компоненты, которые похожи друг на друга, но при использовании в разных местах имеют разный внешний вид или небольшие модфикации. Для этого разработчики часто используют распространённые методологии вёрстки, например БЭМ, согласно которой каждый блок (в нашем случае компонент) имеет свои модификации, которые добавляются новым классом с таким же именем и добавочным названием модификатора. Более наглядно это продемонстрировано на примере ниже.
<vuep template="#simpledemo"></vuep>
<script v-pre type="text/x-template" id="simpledemo">
<template>
<div class="good">
<header class="good__header">Header</header>
<div class="good__strip"></div>
<div class="good__body">Хороший компонент</div>
<div class="good__button">+</div>
</div>
</template>
<style>
.good {
position: relative;
width: 100%;
min-height: 200px;
background-color: #e0e0e0;
border-radius: 15px;
}
.good__header {
width: 100%;
height: 40px;
background-color: #bf360c;
border-radius: 15px 15px 0 0;
color: #fff;
font-weight: bold;
text-align: center;
line-height: 40px;
}
.good__strip {
width: 100%;
height: 5px;
background-color: #870000;
}
.good__body {
padding: 30px;
font-weight: bold;
font-size: 30px;
text-align: center;
}
.good__button {
position: absolute;
bottom: -25px;
left: calc(50% - 25px);
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #795548;
color: #fff;
font-weight: bold;
font-size: 40px;
text-align: center;
line-height: 50px;
}
.good__button:hover {
cursor: pointer;
background-color: #4b2c20;
}
</style>
</script>
Нам нужно использовать этот компонент в другом месте в другой цветовой схеме и без кнопки с плюсиком. Согласно методологии нам нужно добавить модификатор к этому классу главного блока, чтобы не сломать оформление самого блока, а менять только стили его модификации. На практике это выглядит так:
> Добавляем класс с модфикатором cyan - `good_cyan`. Часто вы можете встретить вариант, когда используют `-` вместо `_`, а для обозначения элемента блока `--`, а не `__`. Но это уже нюансы. `vue-mods-names` позволяет использовать любые символы в качестве префикса. Принимает их как опции плагина при его установке. Также прошу обратить ваше внимание, что хорошей практикой является наименование модификаторов в соответствии с их принадлежностью к определенной странице на которой он используется или функционалу, но не цветовому оформлению. В данном случае использовался модификатор `cyan` для большей наглядности.
<vuep template="#simpledemo2"></vuep>
<script v-pre type="text/x-template" id="simpledemo2">
<template>
<div class="good good_cyan">
<header class="good__header">Header</header>
<div class="good__strip"></div>
<div class="good__body">Хороший компонент</div>
<div class="good__button">+</div>
</div>
</template>
<style>
.good {
position: relative;
width: 100%;
min-height: 200px;
background-color: #e0e0e0;
border-radius: 15px;
}
.good_cyan .good__header {
background-color: #00bcd4;
color: #000;
}
.good_cyan .good__strip {
background-color: #008ba3;
color: #000;
}
.good_cyan .good__button {
display: none;
}
.good__header {
width: 100%;
height: 40px;
background-color: #bf360c;
border-radius: 15px 15px 0 0;
color: #fff;
font-weight: bold;
text-align: center;
line-height: 40px;
}
.good__strip {
width: 100%;
height: 5px;
background-color: #870000;
}
.good__body {
padding: 30px;
font-weight: bold;
font-size: 30px;
text-align: center;
}
.good__button {
position: absolute;
bottom: -25px;
left: calc(50% - 25px);
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #795548;
color: #fff;
font-weight: bold;
font-size: 40px;
text-align: center;
line-height: 50px;
}
.good__button:hover {
cursor: pointer;
background-color: #4b2c20;
}
</style>
</script>
Мы использовали тот же компонент, но указали один класс с модификатором и в стилях компонента просто добавили стили для данной модификации этого блока:
``` css
.good_cyan .good__header {
background-color: #00bcd4;
color: #000;
}
.good_cyan .good__strip {
background-color: #008ba3;
color: #000;
}
.good_cyan .good__button {
display: none;
}
```
При использовании препроцессоров это будет выглядить еще красивее и компактнее:
``` css
.good_cyan .good__header,
.good_cyan .good__strip
background-color: #00bcd4
color: #000
.good_cyan .good__button
display: none
```
Теперь представьте как хорошо контролировать оформление определённого блока с помощью таких модификаторов. Вы просто описываете нужные вам стили в другом модификаторе и применяете их где нужно, можно применять разное количество модификаторов. Например, в один и тот же блок добавить `.good_with-border` и `.good_red`. Так же хочу ещё раз напомнить, что в основном блоки называют не в соответствии с их цветовым оформлением, а в соответствии с их принадлежностью (контекстом использования), например, `.good_main-page` и `.good_article-page`. И из названия сразу становится понятно, что этот блок с такими стилями используется на главной странице, но такой же блок с модифицированными стилями используется на странице статей. Большинство разработчиков используют такой подход и не жалеют об этом. Одно из важных приемуществ при данном подходе - это возможность оградить себя от ненужных изменений в основном блоке, менять только его модификации.
### Компоненты во Vue
При компонентном подходе такая методика разработки оказывается очень кстати и более того становится очень лёгкой для использования. Чтобы использовать компонент с другими модификаторами, вы конечно должны позволить компоненту принимать эти модификаторы через входные свойства (props). В нашем примере, это будет выглядить примерно так:
`MyComponent.vue <script>`
```js
export default {
props: {
type: String
}
}
```
`MyComponent.vue <template>`
```html
<template>
<div class="good" :class="mods && 'good_' + mods">
<header class="good__header">Header</header>
<div class="good__strip"></div>
<div class="good__body">Good component</div>
<div class="good__button">+</div>
</div>
</template>
```
Теперь компонент может принимать строковый модификатор `mods`, который он будет складывать с основным классом компонента и вставлять его в аттрибут class. И теперь вы можете спокойно использовать ваш компонент в разных местах, прокидывая ему разные `mods` свойства. А стили для них описываются уже в самом компоненте. Для примера:
```js
// Используем компонент с голубой цветовой схемой
<MyComponent mods="cyan" />
// Используем компонент с цветовой схемой, которая предназначена для страницы "контакты"
<MyComponent mods="contacts" />
```
В самих стилях компонента мы просто добавим оформление для блока с модификатором, то есть `.my-component-name_contacts`. Это легко и **ЕСЛИ** вы не используете в своей вёрстке каскадов, то ваши модификаторы будут работать как ожидается, не поломают другие элементы и основной блок.
> Под каскадами автор понимает такой кейс: `div > .my-component-name > div:first-child` (это просто для примера, особенно часто можно встретить когда такая конструкция находится даже не в стилях самого блока). Всё будет работать не как вы ожидаете, потому что в данном случае затрагивается вопрос приоритета применения стилей и каскад в этом случае выигрывает. Так же самый большой приоритет имеет `id` и `!important`. И если вы в каких то местах указали это, то не удивляйтесь. При вашем подходе, вас уже ничего не спасёт. В утопичном мире где всё хорошо, подразумевается что вы всегда следуете методологии. (Хочу заметить, что это не сказка и многие лиюди действительно делают всё хорошо и потом очень приятно поддерживать проект, который полностью написан в соответствие с таким подходом).
Но наш компонент написанный на данный момент не совсем гибкий. Чтобы дать ему возможность принимать несколько модификаторов т.е. в качестве параметра `mods` массив модификаторов, а не просто строку, то нам уже нужно будет вытаскивать строку добавления модификаторов в компоненте в `computed properties` и обрабатывать там случай если это массив и если это строка. На самом деле, это делается очень легко:
`Псевдокод`
```js
// Если это массив
if (mods is Array) {
return mods.map(item => 'good' + '_' + item)
} else {
return `good_${mods}`
}
```
Однако нам же потребуется еще проверка на существование базового класса и возможность передавать разный префикс (многие используют для обозначения модификаторов `-`, а не `_`). К тому же самое главное, что если весь ваш проект использует такой подход, то было бы не плохо чтобы вы не думали больше об этом и чтобы у вас всегда была возможность передавать модфикаторы любым блокам компонентам в вашем проекте. Как раз для этого вам отлично зайдёт плагин `vue-mods-names`, который очень упрощает ваш код и выносит весь этот функционал добавления модификаторов от ваших глаз.
> Пользуясь случаем, хочу сказать, что плагин действительно невероятно легкий и простой, ничего сложного в коде нету. Немного больше, чем в примере выше и всё. Поэтому (`Contributions welcome`) участие в проекте приветствуется. Мы вынесли его в открытый доступ не потому что мы сделали что-то гениальное, а потому что нам в "РГ - Российская Газета" он действительно помогает в повседневной работе и надеемся зайдет вам. Опять же, если вы разрабатываете согласно методологиям.
### Чем полезен vue-mods-names
**Преимущества**
1. Используя плагин, каждый компонент вашего приложения получает возможность принимать модификаторы через props: mods. (Вам больше не нужно добавлять это поведение в отдельные компоненты)
2. Ваш код становится меньше. Вся работа с модификаторами скрыта в плагине.
3. В любой компонент можно передавать один модификатор строкой или любое количество модификаторов в массиве.
4. Настройка использования базового класса (смотрите раздел `"продвинутое использование"`)
## Установка
Всё как обычно
```js
npm i vue-mods-names -D
```
## Использование
```js
import VueModsNames from 'vue-mods-names'
Vue.use(VueModsNames)
```
Если вы хотите поменять префикс (по умолчанию: `_`), то просто передайте его в опции плагина:
```js
import VueModsNames from 'vue-mods-names'
Vue.use(VueModsNames, { prefix: '-' })
```
После этого любой компонент в вашем приложении готов использовать модификаторы, если вы укажете имя базового класса в качестве аргумента директивы. Примеры ниже.
Декларируем поведение добавляя специальную директиву `v-mods-names` в шаблоне. `good` - это название базового класса, к которому будут присоединяться модификаторы.
`Good.vue`
```html
<!-- Шаблон компонента 'Good', который будет подключаться в разных местах: -->
<div v-mods-names:good>Круто</div>
```
Теперь мы можем использовать компонент с разными модификаторами:
`Главная страница`
```html
<Good mods="main" />
```
В итоге вы получите на главной странице компонент, у которого на выходе будут такие классы:
```html
<div class="good good_main">Круто</div>
```
> Все модификации компонента указываются в стилях самого компонента. Например:
```css
.good {}
.good_main {}
.good_big {}
```
Вы можете передавать массив модификаторов:
```html
<Good :mods="['main', 'big']" />
```
На выходе получим это:
```html
<div class="good good_main good_big">Круто</div>
```
## Продвинутое использование
### no-origin
Если вам вдруг понадобилось использовать только названия классов с модификаторами, без базового класса, то вам на помощь придёт модификатор для директивы `v-mods-names`, который добавляется после названия вашего базового класса:
`Awesome.vue`
```html
<!-- Шаблон компонента Awesome -->
<div v-mods-names:awesome.no-origin>Шикарно</div>
```
Теперь при использовании вашего компонента таким образом:
```html
<Awesome :mods="['main', 'big']" />
```
Вы получите это:
```html
<div class="awesome_main awesome_big"></div>
```
> Работает одинаково как для одного модификатора в виде строки, так и для массива модификаторов
Никто не мешает вам использовать это таким способом:
```html
<Awesome class="awesome" mods="big" />
```
На выходе вы также получите ваш базовый класс указаный собственноручно в шаблоне компонента и сгенерированный класс с модификатором
```html
<div class="awesome awesome_big"></div>
```
> Однако в этом мало смысла и гораздо проще написать в шаблоне директиву с базовым классом как в примере выше: `v-mods-names:awesome`
[KamilOcean](https://twitter.com/kamil_ocean)