Оглавление

    Архитектура

    Структурирование компонентов

    Линейная структура компонентов самая понятная и расширяемая. Каждую компоненту необходимо сделать независимой от компонентов находящихся на одном уровне.

    Важно не забывать о том, что компоненты находящиеся на одном уровне должны быть независимы друг от друга.

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

    # 😊 Хорошо: у компонентов линейная структура
    
    page/
      --page-top/
      ----page-top-buttons/
      ------components/
      --------page-top-buttons-accept/
      --------page-top-buttons-decline/
      --page-aside/
      ----page-aside-menu/
      ------page-aside-menu-item/
      --page-footer/
      ----page-footer-links/
      
    # 💩 Плохо: все компоненты в одном месте
    
    page/
      --page-top-buttons
      --page-top-buttons-decline
      --page-top-buttons-accept
      --page-aside-menu
      --page-aside-menu-item
      --page-footer-links
    

    Глупые компоненты

    Общие компоненты, используемые по всему проекту, не имеющие в себе бизнес логику, работы со стором или привязку к какой-то странице должны лежать в папке components в корне проекта.

    Такие компоненты следует именовать через префикс base-

    # 😊 Хорошо: глупые компоненты вынесены в одно место
    
    components/
      --base-link
      --base-button
      --base-slider
      
      
    # 💩 Плохо: общие компоненты сделаны внутри страницы и разбросаны по всему проекту
    
    page/
      --base-button
    header/
      --base-slider 
    footer/
      --base-link   
    

    Умные компоненты

    Составные компоненты с логикой, взаимодействием со стором следует хранить в папке views в корне проекта. Базовую структуру для такого компонента можно создать с помощью команды npm run create.

    # 😊 Хорошо: умные компоненты вынесены в одно место
    
    views/
      --tapbar
      --header
      --footer
      
      
    # 💩 Плохо: умные компоненты лежат вместе с глупыми компонентами
    
    components/
      --base-link
      --base-slider
      --tabbar
      --header
      --footer
    

    Страницы

    Страницы хранятся в папке pages в корне проекта. Базовую структуру для страницы можно создать с помощью команды npm run create, которая вызовет пакет @maxus/cli.

    Структура у страницы может быть следующая:

    • routes - папка со всеми страница
    • index - индексная страница, /page
    • single - страница записи, /page/12342
    • slug - уникальная страница, /pages/custom-slug
    • router.js - весь роутинг для страницы, который экспортится и подключается в основном роутере (в корне проекта)

    Правила нейминга внутренних компонентов смотреть выше, при нейминге используем названия рутовых папок (index, single, slug).

    # 😊 Хорошо: страница собрана правильно
    
    page/
    --routes/
    ----index/
    ------page-index-header/
    ----single/
    ------page-single-header/
    ----slug/
    ------page-slug-header/
    --router.js
    

    Роутер

    Роутинг в nuxt.js делается как в обычных vue-приложениях. В папке router лежит файл, в котором подключаются все роуты из папки pages.

    import Cart from '../pages/cart/router'
    
    export default function (router) {
      router.push(...[
        Cart,
      ])
    }
    

    Сервисы

    Все сервисы хранятся в пакете @maxus/services. Название папки сервиса должно совпадать с разрабатываемым компонентом или страницей. В индексном файле должны быть только методы для вызова. Все параметры, экстенды, хелперы уносятся в params.js.

    /**
     * Делаем страницу акций – special-offers
     * --services/
     * ----special-offers/
     * ------index.js
     * ------params.js
     */
      export const actionsParams = ({ page = 1, mechanicId, brandId, channelId }) => {
          return {
            page,
            per_page: 12,
            mechanic_ids: mechanicId,
            brand_ids: brandId,
            channel_ids: channelId,
            expand: [
              'type',
              'slug',
              'listing_image',
              'views_count',
              'preview_text',
              'active_from',
              'active_to',
              'channels',
              'preview_image',
            ].join(','),
          }
        }
      
      export const actionParams = () => {
        return {
          expand: [
            'type',
            'slug',
            'detail_image',
            'detail_text',
            'product_tag',
            'active_from',
            'active_to',
            'channels',
            'preview_image',
            'how_to_use',
            'usage_condition',
          ].join(','),
        }
      }
    
      import Base from '@maxus/api'
      import { actionsParams, actionParams } from './params'
      
      export default class Articles extends Base {
        static service () {
          return '/api/articles/'
        }
      
        static getActions (params) {
          return this.get(this.service() + 'actions', actionsParams(params), { withHeader: true })
        }
      
        static getAction (id) {
          return this.get(`${this.service()}${id}`, actionParams())
        }
      
        static getMechanics () {
          return this.get(`${this.service() + 'actions/mechanics'}`)
        }
      
        static getBrands () {
          return this.get(`${this.service() + 'actions/brands'}`)
        }
      
        static getChannels () {
          return this.get(`${this.service() + 'actions/channels'}`)
        }
      }
    

    Стор

    Стор делится на две папки: views и pages.

    • views – хранится стор для конкретного умного компонента
    • pages – хранится стор для конкретной страницы

    Также в корне доступны экшены nuxtServerInit и nuxtClientInit.

    views/
    --header/
    ----actions.js
    ----getters.js
    ----mutations.js
    ----state.js
    
    pages/
    --main/
    ----actions.js
    ----getters.js
    ----mutations.js
    ----state.js
    

    Компонентная разработка

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

    Компонент по определению это небольшая ограниченная часть приложения. "Строительный блок", самодостаточный функционально. Организация Vue позволяет создавать подобные модули ориентируясь на визуальные компоненты.

    Компоненты небольших размеров легче взять для использования — понять что они делают, дорабатывать или переиспользовать. И вам и всей вашей команде.

    Старайтесь чтобы каждый Vue компонент соответствовал принципам FIRST:

    • решающий одну задачу,
    • независимый,
    • переиспользуемый,
    • небольшой,
    • простой в тестировании.
    <!-- 😊 Хорошо: компонент простой. Выполняет только одну функцию.  -->
    <template>
      <button
        v-bind="$attrs"
        v-on="$listeners"
        class="base-button"
      >
        <slot></slot>
      </button>
    </template>
    
    <script>
    export default {
      name: 'BaseButton'
    }
    </script>
    
    <!-- 💩 Плохо: компонент сложный. Зависит от множества сущностей. Трудно переиспользовать.  -->
    <template>
      <button
        v-if="$breakpoint.isMobile"
        v-bind="$attrs"
        v-on="$listeners"
        :class="classes"
      >
        <slot></slot>
      </button>
    </template>
    
    <script>
    import { mapState } from 'vuex';
    
    export default {
      name: 'BaseButton',
      
      inject: [
        '$breakpoint'  
      ],
    
      computed: {
        ...mapState('route', [
          'page'  
        ]),
        
        classes () {
          return [
            'base-button',
            {
              'base-button--old': this.page === '/'
            }
          ]
        }
      }
    }
    </script>
    

    Наименование компонентов

    Имя каждого компонента должно соответствовать следующим критериям:

    • Понятное: в меру детальным, в меру абстрактным
    • Короткое: не более 2-3 слов
    • Произносимое: чтобы его можно было упомянуть в обсуждении

    Имя компонента используется людьми и должно облегчать коммуникацию

    <!-- 😊 Хорошо: компоненты состоят из двух слов  -->
    <app-header></app-header>
    <user-list></user-list>
    <range-slider></range-slider>
    
    <!-- 💩 Плохо: короткое, но произносить - язык сломаешь -->
    <btn-group></btn-group>
    
    <!-- 💩 Плохо: все компоненты - так или иначе UI элементы, приставка не нужна -->
    <ui-slider></ui-slider>
    
    <!-- 💩 Плохо: не соответствует спецификации HTML5 -->
    <slider></slider>
    <header></header>
    <footer></footer>
    

    scoped стили

    Для избежания конфликтов в CSS следует использовать scoped стили для всех компонентов. Исключение составляют сторонние компоненты, например, дизайн-система @maxus/svztify.

    Иногда бывает так, что scoped стили не применяются. Не стоит отказываться от них и выносить стили в глобальную область видимости. Воспользуйтесь ::v-deep у селектора.

    <style scoped>
      .button {
        color: #d9d9d9;
        
        &__text::v-deep {
          .base-text {
            font-size: 14px;
          }
        }
      }  
    </style>