[Fenom] Проблема с кэшированием
Многие разработчики сталкивались с проблемой кэширования при использовании шаблонизатора Fenom. И я в том числе. Эта проблема не раз поднималась в сообществе — раз, два, три и ещё много комментариев в других постах. Об этом писал даже Василий (автор pdoTools). Вот анализ проблемы Николая Ланца. Но решение так и не было найдено, хотя попытки были. Многие просто отключали кэш ресурсов или отказывались от кэширования сниппетов. Кто-то правил исходники проблемных пакетов. А так как эта хрень с кэшем меня достала (постоянно отваливается редактор markItUp для комментариев), то я решил таки потратить пару часов и вникнуть, почему это происходит и как это решить.
Налил себе большой бокал чая и начал погружаться. В итоге ситуация оказалась ровно такой, как и написал Николай. Но и он не стал до конца вникать в проблему (оно и понятно, он использует Smarty). И поэтому решение, которое он предложил, не совсем корректное. Он оптимизировал задачу, которую описал во втором абзаце своего комментария. Я полностью согласен с его выводом. Но проблему с кэшированием, ссылки на которую я давал в самом начале, это не решает. Сразу скажу, что эта проблема напрямую никак не связана с Fenom. Просто вот эти строчки в парсере pdoParser нарушают логику кэширования MODX.
Лично у меня два раза выводился скрипт с настройками TicketConfig. И получалось, что второй скрипт перебивал первый, заново переопределял переменную TicketConfig и затирал настройки редактора markItUp.
Опущу рассказ о заваривании второй чашки и дальнейшем погружении в логику кэширования и перейду сразу к её описанию. В MODX контент ресурса кэшируется отдельно от скриптов и стилей. Т.е. кэшируется не готовая страница, а массив параметров, в котором отдельно указан контент, стили, скрипты, кэшируемые элементы и ещё куча других параметров. Так вот, логика следующая — если на странице вызывается кэшированный сниппет, в котором подключается скрипт или стили, то эти файлы сохраняются в массиве в отдельных ключиках — _sjscripts
, _jscripts
и _loadedjscripts
. Т.е. в этом случае эти файлы сохраняются в ресурсе:
// Класс modResponse (строка 63) $this->modx->resource->_jscripts= $this->modx->jscripts; $this->modx->resource->_sjscripts= $this->modx->sjscripts; $this->modx->resource->_loadedjscripts= $this->modx->loadedjscripts;
А если сниппет некэшированный, то в ресурс ничего сохраняться не будет. Это логично. Сниппет выполнится и сам подключит файлы. А вот если он вызывается без знака !, то выполнится он только первый раз, и парсер заменит его плейсхолдер на результат его работы, который попадёт в кэш ресурса. Но так как этот сниппет подключал файлы, то MODX их указывает в соответствующих ключах массива закэшированного ресурса. При обращении к ресурсу, MODX будет искать файл кэша, получит контент и динамически подключит указанные файлы. Наверно, было бы проще кэшировать страницу уже с подключёнными скриптами и стилями. Но разработчики MODX таким образом борются с дублями, чтобы один и тот же скрипт не подключался 2 раза. Им видней, но мне кажется эту задачу нужно переложить на разработчиков.
Для информации!
Свойство класса modX sjscripts
используется для хранения скриптов, стилей и HTML кода, которые должны подключаться в секции head
HTML страницы. Скрипты и стили, указанные в свойстве jscripts
, вставляются перед закрывающим тегом body
. А в loadedjscripts
указываются все скрипты, стили и HTML блоки, подключенные на странице. Последнее свойство используется только для логирования уже подключённых скриптов — перед подключением нового скрипта MODX проверяет, нет ли такого в списке.
Таким образом, если коротко, то процесс подготовки контента выглядит так:
// Обработка запроса (класс modRequest) 1. Определяется запрашиваемый ресурс. 2. Если он загружается из кэша, то MODX запоминает сохранённые скрипты. // Подготовка ответа (modResponse::outputContent()) 1. Парсятся кэшируемые элементы, если ресурс не из кэша. 2. Сохраняются в ресурс все стили и скрипты, вызываемые кэшируемыми сниппетами и сохраненные в MODX для последующего сохранения в кэш ресурса (см. предыдущий блок кода). 3. Парсятся некэшируемые элементы. Нераспарсенные не удаляются. Скрипты в ресурс не пишутся. 4. Парсятся некэшируемые элементы. Нераспарсенные удаляются. Скрипты в ресурс не пишутся. 5. Выводится готовый контент. 6. Сохраняется кэш ресурса.
Как видите, в кэш ресурса сохраняются файлы только для кэшированных сниппетов. А если мы посмотрим в pdoParser, то увидим, что подключенные скрипты и стили сохраняются в кэше ресурса и для кэшированных и для некэшированных сниппетов и на 3-ем шаге и на 4-ом, когда, согласно логике MODX, их сохранять уже нельзя. Николай предлагает сохранить в кэш все скрипты/стили (и из кэша ресурса и подключённые) только один раз на событие OnBeforeSaveWebPageCache на 6-ом шаге. Но проблема остаётся — в кэш попадают скрипты, которые туда попадать не должны.
Вообще, Василий об этой проблеме писал в статье про pdoTools 2.7.0.
Например, вывод в консоли случайного числа:{var $rand = rand()} {$_modx->regClientScript(' <script type="text/javascript"> console.log(' ~ $rand ~ '); </script>', true)}При первом вызове, когда кэша у документа еще нет, этот вызов попадёт к нему в свойство _jscripts, и будет вставлен на страницу при следующей загрузке.Но так как вызов скрипта из кэша не совпадает с тем, что будет в шаблоне во второй вызов (число-то случайное!), то при последующих загрузках мы получим 2 вызова этого скрипта с 2мя разными числами. Первое будет получаться из кэша, а второе — запускаться динамически из шаблона.Можно представить массу ситуаций, когда такой кэш будет мешать разрабатывать динамические сайты.Поэтому я добавил сохранение скриптов и стилей, подключаемых через Fenom, в отдельные временные массивы и удаление их из кэша ресурса. Таким образом всё, что подключается через {$_modx->regClientScript()} и другие подобные функции, в кэш больше не сохраняется.
Но это решение только для регистрации скриптов через феном и для обычной регистрации оно не работает. Как видите, задача нетривиальная и ещё есть над чем подумать.
Update 06.10.2017. Свой вариант решения этой проблемы отправил Василию (автору pdoTools), так как заинтересован, чтобы всё работало из коробки.
Update 22.10.2017. Мой PR принят. Теперь о проблеме с кэшированием сниппетов можно забыть.
Вы должны авторизоваться, чтобы оставлять комментарии.
Комментарии ()