10 порад для створення масштабних проєктів Vue.js

10 хв. читання
19 листопада 2019

З'ясуємо, як найкраще працювати з масштабними проєктами на Vue.js.

1. Використовуйте слоти, аби ваші компоненти були зрозумілішими й потужнішими

Історія з життя. Якось розробнику треба було зробити звичайне спливне вікно. Нічого складного: просто заголовок, описання та деякі кнопки. Рішенням було передати все як props. На той момент було всього три властивості, які можна було використовувати для кастомізації компонентів, та обробник події кліку на кнопку. Звучить просто, чи не так?

Але зі зростанням проєкту знадобились нові фічі: поля форми, різні види кнопок (залежно від сторінки), картки, футер, і цей список далі поповнювався. Підхід з props швидко зробив компонент занадто складним для розуміння, адже додавались безліч дочірніх компонентів, передавалось багато обробників тощо. Замість того, щоб зробити підтримуваний компонент, розробник отримав справжнього монстра, який ламався від мінімальних змін.

Однак справи йшли б набагато краще, якби розробник з самого початку використовував слоти. Після рефакторингу компонент зменшився в рази. Стало набагато легше його підтримувати, розуміти та розширяти функціонал:

<template>
  <div class="c-base-popup">
    <div v-if="$slot.header" class="c-base-popup__header">
      <slot name="header">
    </div>
    <div v-if="$slot.subheader" class="c-base-popup__subheader">
      <slot name="subheader">
    </div>
    <div class="c-base-popup__body">
      <h1>{{ title }}</h1>
      <p v-if="description">{{ description }}</p>
    </div>
    <div v-if="$slot.actions" class="c-base-popup__actions">
      <slot name="actions">
    </div>
    <div v-if="$slot.footer" class="c-base-popup__footer">
      <slot name="footer">
    </div>
  </div>
</template>

<script>
export default {
  props: {
    description: {
      type: String,
      default: null
    },
    title: {
      type: String,
      required: true
    }
  }
}
</script>

Значно легше підтримувати проєкти, де слоти використовуються з розумом. Як бачимо, тут набагато менше обробників, код простіший і більш гнучкий, адже ви можете рендерити що завгодно всередині компонента.

⚠️ Чудовим індикатором для використання слотів є повторення props дочірніх компонентів у батьківських компонентах.

2. Організовуйте ваш Vuex Store правильно

Зазвичай розробники переходять до вивчення Vuex, коли виникають такі проблеми:

  • Потрібен доступ до даних певного компонента з іншого компонента, розташованого далеко за ієрархією;
  • Потрібно зберегти дані після знищення компонента.

Тоді розробники створюють свій перший Vuex store, дізнаються про модулі та починають організовувати їх у своєму застосунку.

Насправді не існує єдиного підходу до організації модулів. Однак варто заздалегідь подумати про їхні структури. Досить часто застосовується підхід, коли модуль створюється для певної фічі. Наприклад:

  • Авторизація;
  • Блог;
  • Пошта;
  • Налаштування.

Легше організовувати модулі відповідно до моделей даних, які отримуються з API. Наприклад:

  • Користувачі;
  • Команди;
  • Повідомлення;
  • Віджети;
  • Статті.

Вибирайте зручний для вас підхід, однак пам'ятайте, що добре організований Vuex store додасть продуктивності проєкту надалі. До того ж новим розробникам на проєкті буде значно простіше зрозуміти кодову базу.

3. Використовуйте Actions для викликів API та комітів даних

Чому цей підхід вдалий? Просто більшість Actions отримують дані, які треба закомітити у сховище. До того ж це додатковий рівень інкапсуляції, котрий допомагає використовувати компонент повторно.

Ось кілька причин використовувати цей підхід:

  • Потрібно отримати першу сторінку статей у двох різних місцях (наприклад, блог та домашня сторінка), він просто викликає dispatcher з потрібними параметрами. Дані будуть отримані, закомічені та повернуті без зайвого повторюваного коду;
  • Треба створити певну логіку, щоб не отримувати першу сторінку, якщо вона вже була завантажена;
  • Обробники Mixpanel в Actions роблять кодову базу більш підтримуваною.

4. Спрощення кодової бази з mapState, mapGetters, mapMutations та mapActions

Зазвичай не потрібно створювати декілька обчислюваних властивостей чи методів, коли просто треба отримати доступ до стану/гетерів або викликати actions/mutations всередині компонентів. З mapState, mapGetters, mapMutations та mapActions ви можете суттєво скоротити ваш код. До того ж він стане набагато зрозумілішим, коли ви згрупуєте все, що приходить з модулів сховища, в одному місці:

// NPM
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";

export default {
  computed: {
    // Доступ до кореневих властивостей
    ...mapState("my_module", ["property"]),
    // Доступ до гетерів
    ...mapGetters("my_module", ["property"]),
    // Доступ до некореневих властивостей
    ...mapState("my_module", {
      property: state => state.object.nested.property
    })
  },

  methods: {
    // Доступ до actions
    ...mapActions("my_module", ["myAction"]),
    // Доступ до mutations
    ...mapMutations("my_module", ["myMutation"])
  }
};

Більш детальну інформацію про згадані зручні хелпери можна знайти в офіційній документації Vuex.

5. Використання API Фабрик

Створимо хелпер this.$api, який можна використовувати будь-де, аби отримати дані з API. Припустимо, що в корені проєкту є тека api, яка вміщує всі класи:

api
├── auth.js
├── notifications.js
└── teams.js

Кожен з них групує ендпоінти за категоріями. Тепер ми можемо ініціалізувати плагін в Nuxt-застосунку з таким підходом (це досить поширений процес для стандартних Vue-застосунків):

// Проєкт: API
import Auth from "@/api/auth";
import Teams from "@/api/teams";
import Notifications from "@/api/notifications";

export default (context, inject) => {
  if (process.client) {
    const token = localStorage.getItem("token");
    // Встановлюємо токен, якщо він визначений
    if (token) {
      context.$axios.setToken(token, "Bearer");
    }
  }
  // Ініціалізуємо API репозиторіїв
  const repositories = {
    auth: Auth(context.$axios),
    teams: Teams(context.$axios),
    notifications: Notifications(context.$axios)
  };
  inject("api", repositories);
};
export default $axios => ({
  forgotPassword(email) {
    return $axios.$post("/auth/password/forgot", { email });
  },

  login(email, password) {
    return $axios.$post("/auth/login", { email, password });
  },

  logout() {
    return $axios.$get("/auth/logout");
  },

  register(payload) {
    return $axios.$post("/auth/register", payload);
  }
});

Тепер ми можемо просто викликати методи в компонентах або Vuex actions:

export default {
  methods: {
    onSubmit() {
      try {
        this.$api.auth.login(this.email, this.password);
      } catch (error) {
        console.error(error);
      }
    }
  }
};

6. Використовуйте \\$config для доступу до змінних середовища (особливо корисно в шаблонах)

Імовірно, що у вашому проєкті є файли зі змінними конфігурації середовища:

config
├── development.json
└── production.json

Ми можемо отримати до них доступ за допомогою хелпера this.$config, особливо всередині шаблона. Як правило, розширити об'єкт Vue досить легко:

// NPM
import Vue from "vue";

// Проєкт: спільні файли
import development from "@/config/development.json";
import production from "@/config/production.json";

if (process.env.NODE_ENV === "production") {
  Vue.prototype.$config = Object.freeze(production);
} else {
  Vue.prototype.$config = Object.freeze(development);
}

7. Використовуйте єдиний стиль для іменування комітів

Зі зростанням проєкту регулярно потрібно знаходити щось в історії компонентів. Якщо ваша команда не дотримується єдиного стилю іменування комітів, розуміти один одного стане значно складніше.

Краще використовувати керівництво з іменування комітів від Angular. З ним знайти потрібні коміти стане в рази легше, незважаючи на велику історію. Якщо коротко, то працює це так:

git commit -am "<type>(<scope>): <subject>"

# Деякі приклади
git commit -am "docs(changelog): update changelog to beta.5"
git commit -am "fix(release): need to depend on latest rxjs and zone.js"

8. Завжди заморожуйте версії пакетів, якщо ваш проєкт в продакшені

Звичайно, ви скажете, що всі пакети повинні відповідати правилам семантичного версіонування. Але зазвичай все не зовсім так 😅.

Аби не прокидатися посеред ночі, тому що якась з ваших залежностей зламала весь проєкт, вкажіть точні версії проєктів. Тобто слідкуйте, щоб ваші залежності не мали версій з префіксом ^:

{
  "name": "my project",

  "version": "1.0.0",

  "private": true,

  "dependencies": {
    "axios": "0.19.0",
    "imagemin-mozjpeg": "8.0.0",
    "imagemin-pngquant": "8.0.0",
    "imagemin-svgo": "7.0.0",
    "nuxt": "2.8.1",
  },

  "devDependencies": {
    "autoprefixer": "9.6.1",
    "babel-eslint": "10.0.2",
    "eslint": "6.1.0",
    "eslint-friendly-formatter": "4.0.1",
    "eslint-loader": "2.2.1",
    "eslint-plugin-vue": "5.2.3"
  }
}

9. Використовуйте Vue Virtual Scroller, якщо рендерите великий обсяг даних

Якщо вам доводилось відображати багато рядків на заданій сторінці або циклічно обходити великий набір даних, ви могли помітити, як сторінка стає повільною. Щоб це виправити, використовуйте vue-virtual-scoller.

npm install vue-virtual-scroller

Він рендерить лише видимі елементи в списку та повторно використовує компоненти та елементи DOM для максимального збільшення ефективності та продуктивності.

<template>
  <RecycleScroller
    class="scroller"
    :items="list"
    :item-size="32"
    key-field="id"
    v-slot="{ item }"
  >
    <div class="user">
      {{ item.name }}
    </div>
  </RecycleScroller>
</template>

10. Контролюйте розмір ваших сторонніх пакетів

Коли над проєктом працює велика команда, кількість встановлених сторонніх пакетів невпинно зростає. Щоб не було проблем з продуктивністю застосунку (особливо при повільному мобільному інтернеті), використовуйте import cost package у Visual Studio Code. Так ви побачите розмір імпортованої бібліотеки одразу в редакторі і з'ясуєте, чому вона займає стільки місця.

Наприклад, в одному з проєктів, повна імпортована бібліотека lodash займала приблизно 24kB. У чому ж проблема? А в тому, що у проєкті використовувався лише метод cloneDeep, тому є сенс імпортувати тільки його:

npm remove lodash
npm install lodash.clonedeep

Тепер можемо імпортувати функцію лише там, де вона потрібна:

import cloneDeep from "lodash.clonedeep";

⚠️ Аби оптимізувати імпортовані пакети ще більше, можна використати [пакет Webpack Bundle Analyzer] (https://www.npmjs.com/package/webpack-bundle-analyzer) для візуалізації розміру вихідних файлів webpack на інтерактивній збільшуваній карті.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 6.2K
Приєднався: 7 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація