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

Запутанный

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

Решение, возможно, уже существует среди тысяч претендентов. Количество языков, фреймворков, библиотек, сред тестирования и инструментов разработки таково, что простой их анализ становится чрезвычайно утомительным. Часто не совсем понятно, как их классифицировать. Все они представлены как спасители; и тем не менее, люди испытывают излишне болезненные сеансы отладки и длительные процессы обучения и повторного обучения. У проектов быстро растет значительная техническая задолженность, и в конечном итоге возникают проблемы с ремонтопригодностью. Мы должны стремиться тратить наши усилия более разумно - сложность не может быть преодолена сложностью.

Нельзя сказать, что хорошие идеи не были представлены в Интернете. Безусловно, некоторые из них заслуживают распространения. Хорошими примерами являются архитектуры однонаправленного потока данных, такие как Flux, Redux или The Elm Architecture. Функциональному программированию еще есть чему нас научить. Замечательно исследовать новые идеи, особенно принципиально инновационные, но мы не должны забывать обо всех опасностях, которые многообещающие, но сложные решения влекли за собой для многих из нас в прошлом.

Из неуверенности

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

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

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

1, 2, 3

Как ни удивительно, но для реализации этой концепции нам нужны только простые функции. Чтобы проиллюстрировать это, давайте поставим перед собой задачу создать счетчик. Прежде чем продолжить, предположим, что у нас есть типобезопасные фабричные функции, доступные для создания всех известных HTML-элементов в пространстве имен «h». Вот схема такого компонента:

import {h} from '@soil/web'
const counter = (input: {value?: number} = {}) => {
    const $counter = h.div()
    const state = {}
    const api = {}
    return $counter
}

Пока мы знаем, что он содержит и возвращает ссылку на элемент HTML (по соглашению с префиксом «$»), и что он будет иметь некоторое внутреннее состояние и некоторый API. При более внимательном рассмотрении мы также видим, что использование этого компонента будет аналогично использованию собственных HTML-элементов. Чтобы лучше понять последнее, вот одна из возможных реализаций с использованием геттеров и сеттеров:

import {h, extend} from '@soil/web'
const counter = (input: {value?: number} = {}) => {
    const $count = h.span({})
    const $counter = h.div({}, [
        h.button({onclick: () => api.value = api.value - 1}, '-'),
        $count,
        h.button({onclick: () => api.value = api.value + 1}, '+')
    ])
    const state = {
        value: input.value || 0
    }
    const api = {
        get value() {
            return state.value
        },
        set value(v: number) {
            state.value = v
            $count.textContent = '' + v
        }
    }
    // Initialization.
    api.value = api.value
    // Similar to Object.assign, considering getters and setters.
    return extend($counter, api)
}

А вот как можно интегрировать компонент:

const $counter = counter({value: 41})
$counter.value++
document.body.appendChild($counter)

Манипуляции с DOM содержатся внутри компонента, как и любое взаимодействие с ним пользователя. Шаблон и его управляющая логика остаются близко друг к другу. Конечный результат можно использовать привычным образом.

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

Помимо квантов

Создание индивидуальных, презентационных компонентов - это только первый шаг. Что дальше, так это организовать их группы масштабируемым образом, сохраняя чистую связь между ними и легко отслеживая поток управления. Еще одна важная тема - установление зависимостей с другими компонентами и внешними службами, сохраняя при этом их тестируемость. Для этого можно прибегнуть к функциям высокого порядка и чистой инъекции зависимостей, и вам помогут параметры JavaScript по умолчанию. На мой взгляд, немного условности, творческое мышление и мотивация всегда продолжать учиться - это варианты, которые следует рассмотреть, прежде чем прыгать в соседний бассейн глубиной около 20 000 мест.