От локальных разработок к лучшим практикам

Одним из преимуществ фреймворков является то, что они говорят нам, как структурировать наш код. Каждый компонент получает свой файл, импортируем нужные нам компоненты и вуаля. Но что, если мы хотим использовать старый добрый простой JavaScript для проекта? В этом посте мы рассмотрим локальную разработку с модулями ES6, импорт/экспорт модулей и использование вспомогательных классов.

1. Локальная разработка с модулями ES6

Есть пара подводных камней, на которые стоит обратить внимание:

<script type="module" src="index.js"></script>

Ошибка «Невозможно использовать оператор импорта вне модуля»

Браузер должен знать, загружает ли он файл с модулями или простой JS-файл. Для этого нам нужно убедиться, что наш тег script в нашем файле index.html получает пару значений атрибута type=”module”.

Ошибка CORS в локальной разработке

После добавления type="module" мы столкнемся с другой проблемой. Если мы откроем наш локальный файл в браузере, мы увидим:

Доступ к скрипту в «file:///…/index.js» из источника «null» заблокирован политикой CORS.

Объяснение, почему так происходит, вы можете найти здесь. Чтобы предотвратить ошибку CORS, нам нужно запустить скрипт с локального сервера. Есть разные способы сделать это. Если у вас установлен php, вы можете открыть окно терминала в текущем каталоге и запустить

php -S localhost:9000

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

В качестве альтернативы вы можете использовать Расширение Visual Studio Code Server.

2. Импорт/экспорт и лучшие практики

Раньше было обычной практикой хранить весь JavaScript в одном файле, возможно, с некоторой инкапсуляцией. Хотя инкапсуляция предотвратила загрязнение глобального пространства имен, все еще оставалась проблема с поддержкой файлов длиной более 200 строк. Начиная с ES6 мы можем легко разделить код на отдельные модули. Это не только обеспечивает инкапсуляцию, но также улучшает ремонтопригодность и возможность повторного использования.

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

Для этого нам понадобятся 4 отдельных файла:

  • customer.js, содержащий класс клиента
  • product.js: содержит класс продукта
  • входной файл для JS: здесь мы создаем экземпляры классов и запускаем поток приложения.

Поэтому нам нужен класс клиента и класс продукта. Клиент получает начальный баланс и нуждается в способе потратить деньги.

export default class Customer {

    constructor(initialBalance){
        this.balance = initialBalance;
    }

    spendMoney(amount){
        this.balance -= amount;
        console.log(`Balance has reduced by ${amount}. The balance is now ${this.balance}.`);
    }
}

Продукту нужны только цена и название для нашего примера.

export default class Product {

    constructor(price, title){
        this.price = price;
        this.title = title;
    }
}

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

import Product from './product.js';
import Customer from './customer.js';

const customer = new Customer(10);
const jacket = new Product(8, "Jacket");

customer.spendMoney(jacket.price);

Конечно, это очень упрощенный пример. Но этого должно быть достаточно, чтобы показать, что, используя модули, мы получаем чистую структуру кода, и каждый файл сохраняет управляемый размер. Обратите внимание, что есть два способа импорта модулей, которые зависят от того, как они экспортируются. Здесь мы использовали экспорт по умолчанию.

Экспорт по умолчанию

Каждый модуль может иметь только один экспорт по умолчанию. Импорту присваивается псевдоним при импорте. Теоретически это может отличаться от имени экспорта по умолчанию. Однако более понятно использовать имя экспортируемого модуля. В идеале это также должно соответствовать имени файла (в нижнем регистре).

export default class Product {}

import Product from './product.js';

//import is given alias on import. This can be confusing
import Test from './product.js';

Именованный экспорт

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

export class Product {}

При импорте вы используете фигурные скобки для именованного импорта.

import { Product } from './product.js';

У вас может быть несколько экспортов именованных классов в одном файле, но было бы чище, если бы для каждого класса был отдельный файл.

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

export function logPurchase {...}
export function closeStore {...}

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

import { logPurchase, closeStore } from './utils.js'

3. Вспомогательный класс со статическими методами

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

export default class Logger {
    static log(message){
        console.log(`${new Date()}: ${message}`);
    }
}

Используйте вспомогательный класс в index.js.

import Product from './product.js';
import Customer from './customer.js';
import Logger from './logger.js';

const customer = new Customer(10);
const jacket = new Product(8, "Jacket");

customer.spendMoney(jacket.price);

Logger.log("Product purchased");

Краткое содержание

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

Я надеюсь, что это дало вам общее представление о том, как использовать модули для разбиения кода JavaScript на поддерживаемые фрагменты и как связать все это вместе.

Если вам понравилось читать, обязательно ознакомьтесь с другими моими статьями и подпишитесь на меня, чтобы не пропустить другие статьи по веб-разработке.

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.