Статьи

Компонентация Сети

Это история о моем проекте. Большой. Смесь между PHP и Node.js. Это одностраничное приложение с одной точки зрения и SEO-оптимизированный сайт с другой. Тонны JavaScript, CSS и HTML были написаны. Одним словом, кошмар спагетти для любого разработчика. Были падения и взлеты. Создание и устранение ошибок. Борьба с новейшими технологиями и в конечном итоге с удивительно простой библиотекой, которая является темой этой статьи.

Как это обычно бывает, проект считался не таким большим. Пришло бриф, мы обсудили, как будет осуществляться разработка, какие технологии будут использоваться и как мы будем их использовать. Мы составили план и приступили к работе. В начале у нас было несколько страниц, которые контролировались CMS. Сначала не было так много кода JavaScript, потому что наша система доставляла большую часть контента.

Вот примерная структура проекта:

Мы помещаем наш клиентский код в разные каталоги. На данный момент серверный код был только PHP, поэтому он вошел в каталог php . Мы завернули все в 30 файлов, и все было в порядке.

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

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

Например, стили CSS для страницы about были в css/about/styles.css . JavaScript в js/about/scripts.js и так далее. Мы использовали PHP-скрипт, который объединяет файлы. Конечно, были части сайта, которые были на нескольких страницах, и мы помещали их в common каталоги. Некоторое время это было хорошо, но долго не получалось, потому что, когда каталоги заполнялись, что-то медленно изменялось. Вам пришлось искать три разных каталога, чтобы найти то, что вам нужно. Сайт по-прежнему в основном был написан на PHP.

В то время мобильные приложения стали популярными. Клиент хотел, чтобы его сайт был доступен для мобильных устройств, и это большой поворотный момент в проекте. Нам пришлось преобразовать сайт в одностраничное приложение. И не только это, он должен был иметь множество функций в реальном времени. Конечно, не весь контент сайта должен был загружаться динамически. SEO был все еще важной частью видения клиента. Мы выбрали стек MEAN для будущих частей. Проблема была со старыми страницами. Их контент должен был обслуживаться PHP, но логика страниц изменилась, и это было полностью сделано с помощью JavaScript. Несколько недель мы чувствовали себя пассажирами Титаника. Мы спешили что-то выпустить, но была дыра за дырой, и очень скоро наш корабль был полон воды (жуков).

Некоторое время мы использовали GruntJS , но перешли на Gulp . Это очень помогло, потому что мы увеличили скорость разработки. Тем не менее, это было слишком раздражающим, чтобы добавлять или редактировать существующие компоненты. Надежная архитектура, которая была у нас в начале, была преобразована в сложную смесь файлов. Да, были строгие соглашения для именования и размещения этих файлов, но это было все еще слишком грязно. Затем мы ударились головой и придумали следующий формат:

Мы разделили сайт на различные компоненты, которые были похожи на черные ящики. Они живут в своей собственной папке. Все, что связано с компонентом, было сохранено в его каталоге. Мы тщательно разработали API-интерфейсы классов. Они были тестируемыми и общительными. Мы обнаружили, что такая структура работает лучше для нас, потому что у нас было множество независимых модулей. Да, мы смешиваем файлы JavaScript со стилями CSS и шаблонами HTML, но было проще работать на единицу, а не копаться в нескольких каталогах.

Те страницы, которые были старыми и которые мы должны были доставлять через PHP, также были полны логики JavaScript. Однако в некоторых случаях Angular работал не очень хорошо. Мы должны были делать небольшие хаки, чтобы все шло гладко. Мы получили смесь между контроллерами Angular и пользовательским кодом. Хорошей новостью было то, что бюджет проекта был расширен, и мы решили использовать нашу собственную структуру. В то время я разрабатывал собственный CSS-препроцессор . Проект идет очень, очень быстро. Очень скоро я перенес свою библиотеку для использования на стороне клиента. Построчно, это было преобразовано в маленькую структуру, которую мы начали интегрировать в проект.

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

Статус нашей кодовой базы был проблемой. Мы были сосредоточены на создании хорошей архитектуры, и все мы согласны с тем, что иногда индивидуальное решение подходит лучше. Использование Angular, Ember, Knockout или Backbone имеет свои преимущества, но правда в том, что универсальной структуры не существует.

Мне нравятся слова Джереми Кейта в его выступлении «Сила простоты» , он сказал, что самое важное при выборе инструмента — это философия человека, который создал инструмент, и соответствует ли эта философия вашей. Если идеи рамок не совпадают с вашими, очень скоро вы пойдете против них. То же самое случилось с нами. Мы пытались использовать Angular, и было слишком много трудностей. Проблемы, которые нам удалось решить, но мы использовали хаки и сложные обходные пути.

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

AbsurdJS изначально был запущен как препроцессор CSS , расширен до препроцессора HTML и был успешно перенесен для использования на стороне клиента. Итак, в начале мы используем его для компиляции JavaScript в HTML или CSS. Да, вы поняли меня правильно; мы начали писать наши стили и разметку на JavaScript (возможно, это звучит странно, но, пожалуйста, продолжайте читать). Я выдвинул библиотеку вперед, и было добавлено дюжина функциональных возможностей.

Когда у вас сложная система с множеством страниц, вы действительно не хотите решать большие проблемы. Гораздо лучше разбить все на более мелкие задачи и решать их одну за другой. Мы сделали то же самое. Мы решили, что наше приложение будет построено из более мелких компонентов, например:

1
2
3
4
5
6
7
var absurd = Absurd();
var MyComp = absurd.component(‘MyComp’, {
    constructor: function() {
        // …
    }
});
var instance = MyComp();

absurd.component определяет класс. Вызов MyComp() создает новый экземпляр.

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

01
02
03
04
05
06
07
08
09
10
var MyComp = absurd.component(‘MyComp’, {
    doSomething: function() {
        this.dispatch(‘something-happen’);
    }
});
var instance = MyComp();
instance.on(‘something-happen’, function() {
    console.log(‘Hello!’);
});
instance.doSomething();

Мы также можем передавать данные вместе с сообщением. Определение компонентов и их природы «прослушивания-отправки» довольно тривиально. Я принял эту концепцию из других популярных фреймворков, потому что она выглядит естественно. Моим коллегам также было намного легче начать использовать AbsurdJS.

Наряду с разметкой, обслуживаемой PHP, мы динамически создавали элементы DOM. Это означает, что нам был необходим доступ к существующим элементам DOM или новым элементам, которые позже будут добавлены на страницу. Например, предположим, что у нас есть следующий HTML:

1
2
3
4
<div class=»content»>
    <h1>Page title</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>

Вот компонент, который получает заголовок:

1
2
3
4
5
6
7
absurd.component(‘MyComp’, {
    html: ‘.content h1’,
    constructor: function() {
        this.populate();
        console.log(this.el.innerHTML);
    }
})();

Метод populate — единственный магический метод во всей библиотеке. Он делает несколько вещей, таких как компиляция CSS или HTML, он связывает события и тому подобное. В приведенном выше примере он видит свойство html и инициализирует переменную el которая указывает на элемент DOM. Это работает очень хорошо для нас, потому что, как только мы получили эту ссылку, мы смогли работать с элементами и их дочерними элементами. Для тех компонентов, которым необходимы динамически созданные элементы, свойство html принимает объект.

01
02
03
04
05
06
07
08
09
10
11
12
absurd.component(‘MyComp’, {
    html: {
        ‘div.content’: {
            h1: ‘Page title’,
            p: ‘Lorem ipsum dolor sit amet, consectetur adipiscing elit.’
        }
    },
    constructor: function() {
        this.populate();
        document.querySelector(‘body’).appendChild(this.el);
    }
})();

JSON выше переведен на ту же HTML-разметку. Я выбрал JSON, потому что с точки зрения JavaScript он гораздо более гибкий. Мы можем объединять объекты, заменять или удалять только их части. В большинстве популярных фреймворков шаблоны представляют собой простой текст, который затрудняет манипулирование ими. AbsurdJS также имеет собственный шаблонизатор .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
absurd.component(‘MyComp’, {
    html: {
        ‘div.content’: {
            h1: ‘<% this.title %>’,
            ul: [
                ‘<% for(var i=0; i’,
                { li: ‘<% this.availableFor[i] %>’ },
                ‘<% } %>’
            ]
        }
    },
    title: ‘That\’s awesome’,
    availableFor: [‘all browsers’, ‘Node.js’],
    constructor: function() {
        this.populate();
        document.querySelector(‘body’).appendChild(this.el);
    }
})();

Результат:

1
2
3
4
5
6
7
<div class=»content»>
    <h1>That’s awesome</h1>
    <ul>
        <li>all browsers</li>
        <li>Node.js</li>
    </ul>
</div>

Ключевое слово this в вышеприведенных выражениях указывает на сам компонент. Код между <% и %> является допустимым JavaScript. Таким образом, такие функции, как вычисляемые свойства, могут быть легко разработаны непосредственно в определении шаблона. Конечно, мы можем использовать тот же шаблонизатор с уже существующей разметкой. Например:

1
2
3
4
5
6
7
8
<div class=»content»>
    <h1><% this.title %></h1>
    <ul>
        <% for(var i=0; i&amp;lt;this.availableFor.length; i++) { %>
        <li><% this.availableFor[i] %></li>
        <% } %>
    </ul>
</div>

… можно контролировать с помощью следующего компонента (результат тот же):

1
2
3
4
5
6
7
8
absurd.component(‘MyComp’, {
    html: ‘.content’,
    title: ‘That\’s awesome’,
    availableFor: [‘all browsers’, ‘Node.js’],
    constructor: function() {
        this.populate();
    }
})();

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

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
absurd.component(‘MyComp’, {
    css: {
        ‘.content’: {
            h1: {
                color: ‘#99FF00’,
                padding: 0,
                margin: 0
            },
            p: {
                fontSize: ’20px’
            }
        }
    },
    html: ‘.content’,
    constructor: function() {
        this.populate();
    }
})();

Вот тег style который производится:

01
02
03
04
05
06
07
08
09
10
<style id=»MyComp-css» type=»text/css»>
    .content h1 {
      color: #99FF00;
      padding: 0;
      margin: 0;
    }
    .content p {
      font-size: 20px;
    }
</style>

И день за днем ​​мы переносили стили CSS из файлов SASS (потому что в какой-то момент мы выбрали SASS в качестве препроцессора CSS) в компоненты AbsurdJS. Честно говоря, это было довольно легко, потому что все миксины и переменные, которые у нас есть, были определены как функции и переменные JavaScript. Совместное использование стилей было еще проще, потому что все было JavasSript.

… когда все работает отлично, но вы чувствуете, что что-то не так

Мы смотрели на код. Это сработало. AbsurdJS водил даже старые запчасти. Новый материал использует ту же библиотеку. HTML и CSS были красиво разделены и помещены непосредственно в определение компонентов. Однако я почувствовал, что что-то не так. Я остановился на некоторое время и спросил себя: «Из чего сделана сеть?».

И то, что мы сделали, немного по-другому. Это больше похоже на картинку ниже.

Я создавал веб-сайты более десяти лет, и я помню времена, когда мы все боролись за разделение этих трех строительных материалов. И то, что я сделал в этом проекте, с точностью до наоборот. Не было CSS и HTML файлов (почти) вообще. Все было JavaScript.

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

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

Так что в итоге бизнес клиента прошел хорошо. Это было так хорошо, что он решил запустить новый сервис. И угадайте, что. Он хотел, чтобы некоторые части существующего приложения были перенесены в новый проект. Я не могу сказать вам, как мы были рады переместить компоненты из одного места в другое. Нам не нужно было что-то настраивать, копировать HTML-разметку или CSS-файлы. Мы просто получили файл JavaScript компонента, поместили его куда-то и создали его экземпляр. Это просто работало, потому что не было никаких зависимостей. Я не удивлюсь, если некоторые из этих компонентов будут выставлены на продажу в ближайшее время. Они довольно легкие и обеспечивают приятную функциональность, связанную с продуктом клиента.

Да, мы нарушили некоторые правила. Правила, с которыми я лично согласен. Правила, которым я следовал в течение многих лет. Однако реальность такова, что мы все хотим качества, а иногда это качество достигается путем нарушения правил. Мы хотим создать хороший, хорошо структурированный код, который легко поддерживать, гибок и расширяем. Мы не хотим оглядываться назад и говорить: «О, черт возьми … это было написано мной !?». Когда я оглядываюсь назад сейчас, я знаю, почему код выглядит так, как он. Похоже, потому что он был написан специально для этого проекта.

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