Cобрать лучшее из двух миров — фреймворков и CMS (часть 3)

в 16:54, , рубрики: cleverstyle, cmf, cms, framework, php, polymer, shadow dom, tinymce, wysiwyg, Веб-разработка

Прошло уже много времени с выхода второй статьи (часть 1, часть 2), а тут как раз есть что рассказать, так как вышел первый релиз третьей версии системы.

Вкратце об изменениях

Третья версия понемногу двигается в направлении микроядерной архитектуры. Это значит что код ядра всё так же достаточно сильно связан (хотя немного меньше чем до этого), некоторые второстепенные фичи были попросту удалены и появилось больше точек соприкосновения, где разработчик может при необходимости вклиниться в работу системы если он того желает.

На стороне сервера был проведен масштабный рефакторинг нацеленный на простоту и качество кода, что за последние пол года вылилось в повышение оценки Scrutinizer с 5.4 или что-то около того до текущих 7.74/10, что уже совсем неплохо.
На стороне клиента произошла революция, Polymer 0.5.x был обновлен до Polymer 1.x и все компоненты были соответственно переписаны, ещё был полностью выпилен UI фреймворк и некоторые другие изменения.

Polymer

Пожалуй, стоит начать с Polymer. На Google I/O 2015 была анонсирована первая стабильная версия Polymer 1.0, которая весьма существенно, хоть и не радикально отличается от версии 0.5, используемой в CleverStyle CMS на тот момент.
К сожалению, первая версия была достаточно кривой с большим количеством неудобств, и даже версия 1.1 не сильно это исправила.

CleverStyle CMS 3 поставляется с Polymer 1.2 и рядом патчей поверх, которые исправляют многие неровности в работе библиотеки, а так же вносят некоторые удобства. Многие из ранних патчей уже приняты в кодовую базу Polymer, но не все.
Так же для более быстрой загрузки сборка Polymer поставляемая с системой являет собой минифиицированную JS версию вместо набора официальных неминифицированных HTML файлов.
Таким образом Polymer в составе системы является гораздо лучшей средой для разработки чем ванильная версия.
Патчи поверх Polymer протестированные и обратно совместимые, так что проблем при использовании собственных или сторонних компонентов не будет (на момент написания статьи я вхожу в топ-10 разработчиков Polymer по вкладу в кодовую базу, так что я знаю о чём говорю).

К примеру:

// Before
Polymer({
    is         : 'my-element',
    properties : {
        username   : {
            type  : String,
            value : 'Guest'
        },
        registered : {
            type  : Boolean,
            value : false
        }
    }
});
// After
Polymer({
    is         : 'my-element',
    properties : {
        username   : 'Guest',
        registered : false
    }
});

То есть имея значение по умолчанию нет никакой проблемы вывести тип свойства, но разработчики никак не примут соответствующий PR, хотя это у них P1.

Или пример касающийся стилей:

// Before
:host ::content div {}
// After
::content div {}

Мелочь, но приятно что этот костыль больше не нужен (это проблема в Polymer, по спецификации :host там не нужен), PR тоже ожидает своего часа.

Последнее что стоит упомянуть — так это то, что Polymer 1.x на момент написания статьи не поддерживает расширение пользовательских элементов, но хак с переопределением элементов всё же был портирован и позволяет разработчику полностью или частично переопределять внешний вид и устройство встроенных системных и любых других пользовательских элементов. Подробнее с примерами в документации.

TinyMCE и Shadow DOM

Cобрать лучшее из двух миров — фреймворков и CMS (часть 3) - 1
Полноценные WYSIWYG редакторы это большие и сложные приложения и ни одно из популярных решений не поддерживает работу внутри Shadow DOM по причине сложности реализации и поддержки (на момент написания статьи, хотя судя по инициативности разработчиков это ещё долгое время будет актуально).

Проведя несколько дней в дебрях мегабайтов JavaScript кода и пошаговом отладчике браузера мне удалось получить обратно совместимую версию TinyMCE, которая полноценно работает внутри теневого дерева.
На сколько мне известно — это первая в мире реализация полнофункционального WYSIWYG редактора с поддержкой Shadow DOM, а CleverStyle CMS первый продукт, где вы можете его использовать что называется из коробки.

Для того чтобы подключить редактор нужно установить плагин TinyMCE и обернуть <textarea> следующим образом:

<cs-editor>
    <textarea></textarea>
</cs-editor>

Под капотом элемент-обертка применит Shadow DOM-совместимую версию TinyMCE к <textarea>, а так же подключит CSS стили к редактору в дополнение к используемым по умолчанию глобальным, чтобы элементы интерфейса корректно отображались даже внутри Shadow DOM.
А ещё элемент-обертка понимает когда он перемещается в DOM, фокусируется и так далее, обновляя соответствующим образом связь с текстовым полем.

Есть так же вариация с inline режимом, применяемым для редактируемого <div>:

<cs-editor-inline>
    <div></div>
</cs-editor-inline>

Интересно, что при отсутствии плагина TinyMCE элемент-обертка просто не будет обновлен до веб-компонента и пользователь увидит обычное текстовое поле, такое вот прогрессивное улучшение:)

Так что теперь впервые в истории человечества можно использовать WYSIWYG редактор в любом месте документа на любом уровне вложенности теневого дерева.

Подробнее в документации.

UI фреймворк

Исторически система поставлялась с каким-то UI фреймворком. Давным давно это был jQuery UI, который впоследствии был заменен на UIkit.
Последний служил весьма неплохо до того момента как в системе начали использоваться веб-компоненты, после чего несколько критических недостатков UIkit послужили причиной его полного выпиливания.
Во-первых та же поддержка Shadow DOM. UIkit понятия не имеет (так же как и Bootstrap, Foundation и другие приятели) что такое теневое дерево. И ладно если бы это касалось только стилей — были проблемы с обработкой событий и прочими нюансами.
В течении многих месяцев система поставлялась с растущим количеством патчей поверх ванильной версии для обеспечения поддержки Shadow DOM, но разработчики не спешили принимать патчи, к сожалению.
Вторая существенная проблема — это избыточная верстка и отсутствие поддержки data-binding из Polymer. В результате подключение компонентов UIkit превращалось в месиво как на стороне разметки, так и в JavaScript, причиняя боль и страдания при каждой попытке использования.

Решение было нелегким. На поиски альтернативы ушло дня три, ещё пару дней на обдумывание реализации и в результате появился пока ещё подпроект CleverStyle Widgets (есть планы выделить в отдельный самостоятельный проект).

CleverStyle Widgets

Cобрать лучшее из двух миров — фреймворков и CMS (часть 3) - 2
Как следует из названия, это не фреймворк, хотя он и заменил на 99% UIkit.
Виджеты — это набор веб-компонентов.
Все веб-компоненты отлично работают с Shadow DOM по определению, так как используют Polymer и поддерживают его data-binding систему (в отличии от UIkit, Bootstrap, Foundation и других приятелей).
Они имеют радикально простой API для пользователя, чаще всего это один тэг, который делает всё что нужно — никаких пятиэтажных вложенностей безликих <div>.
Но что ещё более важно — элементы сами по себе не имеют визуального стиля — он определяется извне с помощью CSS mixin-ов (в отличии от фиксированного внешнего вида Paper Elements с Material Design и Strand, который вообще идет в едином визуальном исполнении). Таким образом вы можете сделать внешний вид таким, каким он нужен именно вам.

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

Несколько примеров использования:

<button is="cs-button" type="button" icon="home" primary>Primary button with home icon</button>
<label is="cs-label-switcher">
    <input checked type="radio" value="0">
    Zero
</label>
<label is="cs-label-switcher">
    <input checked type="radio" value="1">
    One
</label>
<nav is="cs-nav-pagination" page="3" pages="10"></nav>
<cs-notify success left>Hello</cs-notify>
<button is="cs-button" type="button">Button will open modal</button>
<section is="cs-section-modal">One Two Three</section>

Упор сделан на максимальное сходство с нативными элементами, максимальное использование встроенного функционала браузера.

Больше примеров и описание свойств каждого элемента в документации.

Фронтенд без стилей по умолчанию

Используя UI фреймворк вы получаете огромную кучу стилей примененных ко всему документу, что периодически вынуждает бороться с этими стилями, порой даже прибегая к использованию !important.
Что хорошего в CleverStyle CMS 3 так это то, что НИКАКИЕ СТИЛИ НЕ ПРИМЕНЕНЫ К ДОКУМЕНТУ ПО УМОЛЧАНИЮ!
На практике это значит что даже normalize.css по умолчанию не используется. Разработчик имеет полную свободу по настройке внешнего вида и при желании может воспользоваться встроенными shared styles. Это так же значит что вы можете без конфликтов портировать любой готовый HTML шаблон в качестве темы оформления, можете при желании воспользоваться тем же Bootstrap, UIkit или чем-то ещё без проблем с наложением стилей.

Ещё больше фронтенда

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

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

Качество кода

Cобрать лучшее из двух миров — фреймворков и CMS (часть 3) - 3
Рефакторинг на сервер не только упростил понимание кода, но и в целом повысил его качество по ряду объективных метрик. При этом Scrutinizer имеет доступ к ещё большему количеству кода, так как API системы использует маршрутизацию основанную на контроллере, а Scrutinizer с ООП дружит лучше.

Прочие интересные изменения

На фронтенде был переработан объект cs.Event, теперь события основываются на Promise.

Это последняя стабильная ветка с поддержкой IE10 (и, возможно, IE11), поэтому полифилы для IE/Edge теперь вынесены в отдельную папку и подключаются для этих отставших браузеров, остальные браузеры не тратят на это трафик.
Cобрать лучшее из двух миров — фреймворков и CMS (часть 3) - 4
К минификации CSS и объединению CSS/JS/HTML добавилась минификация JS. Реализация весьма простая, как и минификация CSS, но очень быстраяя и весьма эффективная.
А CSS минификатор перестал встраивать картинки и прочие ресурсы размером больше 4 КиБ, вместо этого он просто корректирует относительные пути, на практике это означает гораздо более начальную быструю загрузку страницы.

Plupload модуль объявлен устаревшим, вместо него теперь появился модуль Uploader, который имеет независимую реализацию всего нужного для загрузки файлов (в том числе Drag'n'Drop, множественная загрузка файла), но при этом в неминифицированном виде имеет в 15 раз меньший размер JavaScript кода чем минифицированный Plupload.

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

Небольшой тест производительности

Имея достаточно много функциональности из коробки в ядре, CleverStyle CMS остается чрезвычайно быстрой системой.

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

Забавы ради была взята последняя git версия CleverStyle CMS на момент написания статьи (3.146.4+build-1793) и абсолютно пустая Symfony Standard Edition 3.0.1.

Нужно заметить, что CleverStyle CMS не работает без БД, поскольку она заводит сессию на каждого посетителя в БД (в данном случае без поддержки cookie это значит что сессия создавалась при каждом запросе) + использовался файловый движок кэша. Symfony Standard Edition устанавливалась через Composer и к БД при запросах не подключалась.

Состояние обоих систем «из коробки», в CleverStyle CMS загружалась пустая главная страница, в Symfony загружалась стандартная страница с надписью «Welcome to Symfony 3.0.1».

Прочее ПО: Apache 2.4.17, PHP 7.0.2, MariaDB 10.1, Ubuntu 16.04 x64
Запускается на ноутбуке: Core i7 4900MQ, SSD

Результаты:

CleverStyle CMS

nazar-pc@nazar-pc ~> ab -c256 -n10000 cscms.loc
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, www.zeustech.net
Licensed to The Apache Software Foundation, www.apache.org

Benchmarking cscms.loc (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests

Server Software: Apache/2.4.17
Server Hostname: cscms.loc
Server Port: 80

Document Path: /
Document Length: 2132 bytes

Concurrency Level: 256
Time taken for tests: 7.813 seconds
Complete requests: 10000
Failed requests: 8468
(Connect: 0, Receive: 0, Length: 8468, Exceptions: 0)
Total transferred: 25217819 bytes
HTML transferred: 21327819 bytes
Requests per second: 1279.86 [#/sec] (mean)
Time per request: 200.022 [ms] (mean)
Time per request: 0.781 [ms] (mean, across all concurrent requests)
Transfer rate: 3151.89 [Kbytes/sec] received

Connection Times (ms)
min mean[±sd] median max
Connect: 0 4 58.3 0 1003
Processing: 5 171 670.6 96 6888
Waiting: 4 171 670.7 96 6888
Total: 9 175 673.2 96 6892

Percentage of the requests served within a certain time (ms)
50% 96
66% 103
75% 108
80% 112
90% 126
95% 148
98% 221
99% 3507
100% 6892 (longest request)

Symfony Standard Edition

nazar-pc@nazar-pc ~> ab -c256 -n10000 symfony.loc/web
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, www.zeustech.net
Licensed to The Apache Software Foundation, www.apache.org

Benchmarking symfony.loc (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests

Server Software: Apache/2.4.17
Server Hostname: symfony.loc
Server Port: 80

Document Path: /web/
Document Length: 4580 bytes

Concurrency Level: 256
Time taken for tests: 8.290 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 48440000 bytes
HTML transferred: 45800000 bytes
Requests per second: 1206.22 [#/sec] (mean)
Time per request: 212.233 [ms] (mean)
Time per request: 0.829 [ms] (mean, across all concurrent requests)
Transfer rate: 5706.01 [Kbytes/sec] received

Connection Times (ms)
min mean[±sd] median max
Connect: 0 14 119.0 0 3003
Processing: 6 197 43.7 194 594
Waiting: 6 194 40.3 192 594
Total: 15 211 127.4 194 3226

Percentage of the requests served within a certain time (ms)
50% 194
66% 203
75% 210
80% 216
90% 242
95% 283
98% 387
99% 1182
100% 3226 (longest request)

Выводы каждый сделает для себя сам.

На этом всё, жду комментариев.
Желающие пообщаться чатике могут сделать это в Gitter.
Код на GitHub, документация там же в разделе wiki.
Запустить пощупать можно одной строчкой с помощью Docker:

docker run --rm -p 8888:8888 nazarpc/cleverstyle-cms

Автор: nazarpc

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js