6 простых принципов написания приложения на Vue, которое легко поддерживать (часть 2)

9078
#Разработка 15 марта 2023
Привет! Я Наталья Калачева, Frontend-разработчик в AGIMA. Эта статья посвящена правилам, которые помогают упростить поддержку и расширение приложений на Vue. Тут я рассказываю, как организовать хранение компонентов, стилей и плагинов, когда использовать стор и полезные функции Vue. Первые 3 принципа я опубликовала вчера. Здесь еще 3.

4. Оборачивать библиотеки при инициализации

Лучше всего создавать отдельный файл для инициализации широко используемых плагинов. Например, вместо того чтобы тянуть Axios в компонентах непосредственно из зависимостей, вы можете создать класс с более общим именем — такой, как HTTP, чьи методы вызывают Axios под капотом. Например:
                    import axios from 'axios';
class $Http {
 constructor(options) {
   this.instance = axios.create({
     baseURL: options?.baseURL ?? '',
     headers: options?.headers ?? {},
     params: options?.params ?? {},
   });
 }
 get = (resource, params) => this.instance.get(resource, { params });
 // другие методы класса, интерцепторы и т.д.
}
export default $Http;
                
На первый взгляд, это бесполезный модуль, ведь он делает то же самое, что делал бы модуль из зависимости. Только вы вместо этого пишете импорт HTTP из «http.js». Однако это облегчает переиспользование кода и расширение общей функциональности. Мы можем расширять наш класс и добавлять новые функции/конфиги глобально.
Например, с Axios это может быть использование интерсепторов. Мы можем всегда предупреждать пользователя, когда запрос ajax терпит неудачу, а не использовать catch везде, где делаем запрос.
Если же мы решим перейти на другой плагин или обновиться до версии с критическими изменениями, не нужно рыскать по всей кодовой базе в поиске всех мест, где это использовалось. Достаточно адаптировать наш класс к изменениям.
Конечно, не все библиотеки стоит оборачивать. Если есть сомнения, можно задать себе ряд вопросов:


  • Вы не уверены в том, что реализация библиотеки на 100% удовлетворяет будущие задачи?
  • Эта зависимость требует настройки и расширения функционала?
  • Будете ли это переиспользовать в разных частях приложения?
  • Вы будете использовать только часть функционала?


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

5. Валидировать пропсы, использовать типы

Во время разработки большого проекта вы можете забыть точный тип и формат данных, которые вы передавали в компонент. Поэтому важно всегда описывать тип данных. Это делает код более читабельным и помогает подсветить ошибки на этапе разработки. Vue предоставляет мощный встроенный механизм для проверки этих данных. Это гарантирует, что компонент будет использоваться по назначению.
                    export default {
 name: 'BlockName',
 props: {
   title: {
     type: String,
     default: ''
   }
 }
}
                
Если вы используете Typescript, то сложные типы можно описывать с помощью интерфейсов:
                    import { PropType, defineComponent } from 'vue'


interface IItem {
 id: string
 title: string
}


export default defineComponent({
 name: 'BaseList',
 props: {
   list: {
     type: Array as PropType<IItem[]>,
     default: () => []
   }
 }
})
                
Лучший способ ограничить пропсы определенным набором значений — использовать параметр валидатора. Это функция проверки, которая принимает значение и возвращает один из двух результатов — true или false.
                    export default {
 name: 'BaseIcon',
 props: {
   position: {
     type: String,
     validator: s => ['top', 'left'].includes(s)
   }
 }
};
                
Всё вышеперечисленное справедливо при использовании Vue 3 либо с options, либо с composition API. Разница только в setup: пропсы должны быть объявлены с помощью функции defineProps(). Например:
                    <script setup lang="ts">
interface IProps {
 title: string
 list: Array<IItem>
}


const { title, list } = defineProps<IProps>()
</script>
                
В сочетании с TypeScript встроенные механизмы валидации и типизирования пропсов могут обеспечить правильное использование компонента, уменьшить количество ошибок и упростить общее понимание кода.

6. Организовать стили

Даже если логика нашего приложения написана идеально, CSS может значительно подпортить внешний вид и создать множество проблем при дебаге и расширении/изменении дизайна. Важно уделять стилям повышенное внимание, чтобы избежать конфликтов и непредсказуемого поведения.
Есть множество методологий написания CSS, например БЭМ, OOCSS, SMACSS, Atomic CSS и т. п. Среди этих подходов нет идеального — у всех свои плюсы и минусы. Поэтому можно выбрать один, а можно совместить несколько. Важно договориться с командой о едином подходе. Тогда масштабирование проекта будет даваться легче.

Нужно прописать глобальные стили, которые распространяются на всё приложение. Есть несколько стилей, которые мы хотим применить ко всем компонентам. Например, normalize.css или reset.css. Или общие стили, такие как шрифты, размеры заголовка, цвета и CSS-переменные. Добавим их в тег стиля App.vue
                    <template>
 <div id="app">
   <main>
     <router-view></router-view>
   </main>
 </div>
</template>


<style>
body {
 margin: 0;
}
</style>
                
Стили компонента всегда изолируются при помощи директивы scoped. Это гарантирует, что любой стиль CSS, определенный в компоненте, будет применяться только к этому шаблону и не создаст конфликта стилей за его пределами.
                    <style lang="scss" scoped>
.Item {
 &__text {
   margin-bottom: 30px;
 }
}
</style>
                
Если стилей одного компонента много или одни и те же стили переиспользуются в нескольких компонентах, в assets я создаю отдельную папку styles/components и добавляю туда стили, которые в дальнейшем импортирую в компонент.
                    <style lang="scss" scoped>
@import "@/assets/styles/components/table.scss";
</style>
                
Однако использование scoped не отменяет использование классов для стилизации элементов. Это наиболее производительный вариант селектора по сравнению с названием тегов. Подробнее об этом в документации.
Как альтернативу можно использовать CSS Module. Они также решают проблему конфликта стилей. Но модули предполагают динамический рендеринг и отображение имен классов (например, можно настроить классы в зависимости от среды разработки или props).
                    <template> 
 <section>
   <div>
     <h1 :class="$style.heading">Заголовок</h1>
   </div>
 </section>
</template>




<style module>
.heading {
 font-size: 50px;
 font-weight: 900;
 text-align: center;
}
</style>
                
Данный модуль генерирует следующий CSS:
                    .ComponentName__heading__2Kxy { 
 font-size: 50px;
 font-weight: 900;
 text-align: center;
}
                

Заключение

Эта статья дает краткое описание хороших практик при разработке современных клиентских приложений на Vue. Тем не менее, нет правильного или неправильного ответа. Ни один подход определенно не лучше других. Это полностью зависит от ситуации и предпочтений разработчика.
Есть много хороших практик, которые не перечислены в этой статье. Пожалуйста, не стесняйтесь оставлять комментарии и дайте мне знать, если знаете что-то интересное. А еще присоединяйтесь к нашему телеграм-каналу.

Контент-хаб

0 / 0
+7 495 981-01-85 + Стать клиентом
Услуги Кейсы Контент-хаб