Наверх

Оптимизация сайта на MODX Revolution — уменьшение времени загрузки страницы

Советы по оптимизации сайта штатными средствами MODX

Всегда вызывайте сниппеты кешируемыми


Многие разработчики привыкли писать так:
[[!getResource?
  &parents=`5`
  &tpl=`tpl.news`
]]

Это в корне неверно. Убирайте восклицательные знаки везде. Исключение составляют те сниппеты, которые обрабатывают какую-то информацию, которая поступает от пользователя, т. е.:
  • FormIt (пользователь заполняет форму и сниппет должен данные обработать);
  • Login, Register (точно так же — обработка пользовательских данных, например, логин и пароль);
  • getPages (ссылки на разные страницы формируются с GET-параметрами, типа /?page=3, соответственно сниппет должен обработать этот GET-параметр);
  • Ну и другие сниппеты, например, на моем сайте — это сниппеты Loginza для авторизации и TicketsComments, с помощью которого можно оставлять комментарии к статье.
Остальные сниппеты (такие как Wayfinder, getResources, Breadcrumb и пр.) должны вызываться без восклицательного знака.

Отключите плагин phpThumbofCacheManager


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

Этот плагин удаляет все сгенерированные ранее картинки во время очистки кеша (а это происходит постоянно — при сохранении любых документов, например). Если он включен, то ваш аккаунт хостинга могут заблокировать за высокую нагрузку, помимо того, что страницы будут открываться очень долго — ведь картинки будут постоянно генерироваться заново.

Следите за временем генерации страницы


Добывьте в футер такой плейсхолдер: [^t^] — он будет показывать вам общее время генерации страницы. Если страница генерируется больше 1 секунды (или если обновить страницу, а время генерации больше 0,5 секунды), это плохо. Если советы выше не помогли, то читаем советы ниже про кеширование.

Совет. Вы можете заключить этот плейсхолдер в комментарий так:
<!-- Render time: [^t^] -->
И тогда на страницах сайта этой служебной информации не будет, но вы всегда сможете посмотреть ее в исходном коде страницы.

Продвинутая оптимизация. Управляем кешированием целых блоков.


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

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

Для этого создадим новый сниппет chunk:
$cache_key = "chunk_".$name;

$output = $modx->cacheManager->get($cache_key);

if (empty($output)) {
  $output = $modx->getChunk($name, $scriptProperties);
  $modx->cacheManager->set($cache_key,$output);
}

return $output;
После этого возьмем наш шаблон и заменим в нем вызовы чанков нашим сниппетом:
<!DOCTYPE html>                  <!DOCTYPE html>
<html>                           <html>
<head>                           <head>
[[$head]]                        [[$head]]
</head>                          </head>
<body>                           <body>
<header class="page">            <header class="page">
[[$header]]        <!--  →  -->  [[chunk? &name=`header`]]
</header>                         </header>
<section class="page">            <section class="page">
[[$asideRight]]    <!--  →  -->  [[chunk? &name=`asideRight`]]
[[$asideLeft]]     <!--  →  -->  [[chunk? &name=`asideLeft`]]
  <article>                        <article>
   <h1>[[*longtitle]]</h1>           <h1>[[*longtitle]]</h1>
   <div id="content">                <div id="content">
[[*content]]                         [[*content]]
    </div>                           </div>
   <div class="clear"></div>        <div class="clear"></div>
  </article>                       </article>
</section>                       </section>
<footer class="page">            <footer class="page">
[[$footer]]        <!--  →  -->  [[chunk? &name=`footer`]]
</footer>                        </footer>
</body>                          </body>
</html>                          </html>

Чанк [[$head]] я не стал заменять, потому что у меня там жестко прописаны пути и устанавливаются TITLE, KEYWORDS, DESCRIPTION, а они на всех страницах разные.

Совет. В вызове Wayfinder добавьте &hereClass=``:
[[Wayfinder? &startId=`0` &hereClass=``]]
так как если первой будет открыта внутренняя страница, она получит класс active и будет подсвечиваться на всех остальных страницах.

Когда мы это сделали, можно открыть сайт и, тем самым, сгенерировать наш первый пользовательский кеш. Чтобы посмотреть, что из себя представляет кеш, загляните в папку /core/cache/default/ — там вы найдете по одному файлу для каждого чанка. Откройте эти файлы и посмотрите, что записалось в кеш )))

Исправляем меню


После наших действий сломалось меню на сайте — раньше на каждой странице в меню проставлялся класс «active» у тех пунктов, которые в данный момент активны. Сейчас же меню для всех страниц одинаково и класс «active» не проставляется нигде.

Исправим это с помощью JavaScript. Находим, где у нас вызывается Wayfinder и после него добавляем код, проставляющий нужные классы у текущих пунктов меню. Например, так:
<nav>
[[Wayfinder? &startId=`0` &hereClass=``]]
</nav>
<script type="text/javascript">
var tagLimit = 'nav';     // нужен, чтобы ограничить подъём по дереву DOM
var className = 'active'; // какой клас будем проставлять
var setActiveMenuItem = function ($li) {
    var $liParent = $li.addClass(className).parent('ul').closest('li');
    if ($li.closest(tagLimit).length && $li.length) {
        setActiveMenuItem($liParent);
    }
}
setActiveMenuItem($(tagLimit +' [href="[[~[[*id]]]]"]').closest('li'));
</script>
Этот скрипт пройдет до ближайшего nav вверх и проставит всем нужным элементам списка класс «active» (то есть подходит и для многоуровневых меню).

Итог


Применяя эти советы вы с легкостью будете делать сайты, которые не будут создавать нагрузку на сервер и будут открываться очень быстро. Например, сайт balirehab.ru/ открывается в среднем за 0.05 — 0.15 секунды. И это нормально для MODX))) G+


65 комментариев

  1. Denis Malafey 09 мая 2013, 13:48(Комментарий был изменён) # 0
    Здравствуйте, Илья.
    Спасибо большое за статью!

    Идея со сниппетом «chunk» очень интересная.
    А можно провернуть такое со сниппетом, не с чанком?

    Я вообще не использую чанки, у меня 3 статичных меню: верхнее, нижнее и боковое.
    Это самописные сниппеты, вызываются [[Top_menu]], вот так. Меню мультиязычное.
    То есть, можно ли как-то сказать MODX, чтоб он один раз их загрузил и чтоб потом лишние файлы не инклудились?
    Менюхи ведь статичные, только вот мультиязычность сделана с помощью Babel.

    Спасибо.
    С праздником!
    1. Илья Уткин 09 мая 2013, 15:58(Комментарий был изменён) # 0
      Конечно, можно. Babel, насколько мне известно, просто использует разные контексты для разных языков? Тогда вы можете сделать кеширование прямо в своем сниппете. В начале сниппета мы ищем кеш, если находим, просто выводим его. Если нет кеша, то сниппет отрабатывает дальше и создает меню. Потом это меню закидываем в кеш и выводим результат на страницу. Примерно так:
      $lang = $modx->context->get('key');
      $cache_key = "Top_menu_".$lang;
      
      $output = $modx->cacheManager->get($cache_key);
      
      if (empty($output)) {
        // Здесь код вашего сниппета. Само меню помещаем в переменную $output
        // Например, $output = $modx->runSnippet('Wayfinder', array('startId' => 0));
        $modx->cacheManager->set($cache_key,$output);
      }
      
      return $output;
      И вас, кстати, с праздником)
      1. Denis Malafey 09 мая 2013, 17:58(Комментарий был изменён) # 0
        Спасибо большое!
        Попробую — отпишусь.
        1. Denis Malafey 09 мая 2013, 18:06(Комментарий был изменён) # 0
          То есть, мне нужно модифицировать свои сниппеты?
          Дописав в них код, который вы дали?
          Или вызывать как-то по-особому, вроде этого [[chunk? &name=`footer`]]
          Распишите, пожалуйста, поподробнее и попроще, если несложно.

          Спасибо.
          1. Илья Уткин 09 мая 2013, 18:37(Комментарий был изменён) # 0
            Да, нужно добавить этот код в свои сниппеты. Изменять вызов сниппетов не надо
            1. Denis Malafey 09 мая 2013, 18:41(Комментарий был изменён) # 0
              Ок, спасибо.
      2. Denis Malafey 13 мая 2013, 01:09(Комментарий был изменён) # 0
        Спасибо, сделал.
        Не могу понять, лучше стало, или нет.
        Время загрузки не изменилось.
        1. Алексндр Наумов 23 мая 2013, 22:04(Комментарий был изменён) # 0
          Илья, скажите, пожалуйста, а как вы выводите в подвале сайта количество используемой памяти?
          Извиняюсь, что не по теме.
          1. Илья Уткин 24 мая 2013, 10:13(Комментарий был изменён) # 0
            Сниппет [[!Mem]] (не уверен, что он показывает точно, но хоть какой-то показатель):
            <?php
            return round(memory_get_usage()/1024/1024, 4).' Mb';
            1. Алексндр Наумов 24 мая 2013, 13:56(Комментарий был изменён) # 0
              Спасибо!
          2. Evgeny Epifanov 23 сентября 2013, 14:26(Комментарий был изменён) # 0
            Странные дела. Прирост вроде как чувствуется, но только при посещении конкретной страницы. Как только переходишь на другую снова грустно, а если ее обновить, то прирост скорости ощутим.
            Еще не вижу кэша чанков в /core/cache/default/ — просто нет такой папки, создал вручную, но тоже ничего в ней не появилось.
            Куда копать?
            1. Илья Уткин 23 сентября 2013, 14:28(Комментарий был изменён) # 0
              А что сделали? Сниппет chunk создали? В шаблоне заменили вызов чанков на этот сниппет? Если в шаблоне прописан вызов других сниппетов (getResources, Wayfinder), выносите их в чанки, а чанки вызывайте через сниппет chunk.
            2. Evgeny Epifanov 23 сентября 2013, 16:05(Комментарий был изменён) # 0
              Да, Илья, все делал по инструкции.
              На сайте есть еще вызов Wayfinder, в связи с этим вопрос: нормально ли, что после каждого вызова Wayfinder'a будет прописан скрипт? Просто они (меню) в разных местах или можно в скрипте контейнеры через запятую прописать?
              Есть также и вызов getProducts, если его закешировать, не получится так, что при открытии разных категорий посетитель увидит один и тот же контент? Насколько я понял сниппет именно для статичных блоков, или я не прав.
              Сразу скажу в программировании я не силен, поэтому приношу извинения за возможно глупые вопросы.
              Ну вот, еще и иерархию нарушил…
              1. Илья Уткин 23 сентября 2013, 16:57(Комментарий был изменён) # 0
                Да, этот сниппет только для контента, который на всех страницах одинаковый. Насчет меню — необязательно скрипт ставить после каждой менюшки. Можно перечислить через запятую и вставить в футер. Скиньте код шаблона, посмотрим, что может тормозить. (разместите код здесь, а в комментарии оставьте ссылку).

                Плагин phpThumbOfCacheManager отключили?
                1. Evgeny Epifanov 23 сентября 2013, 17:11(Комментарий был изменён) # 0
                  Еще странность.
                  Классы в Wayfinder'e обновляются только после очистки кэша. WF вызывается на странице 5 раз (меню состоит из 5 кусков). А если зайти в карточку товара, то класс вообще не проставляется. Что-то я косячу…
                  1. Илья Уткин 23 сентября 2013, 17:13(Комментарий был изменён) # 0
                    Так и должно быть — меню кешируется, а класс должен проставлять JS. Поэтому в вызове Wayfinder нужно прописывать параметр &hereClass=``
                    1. Evgeny Epifanov 23 сентября 2013, 17:16(Комментарий был изменён) # 0
                      phpThumbOfCacheManager — отключен.
                      &hereClass=`` — прописан
                      Первичная загрузка страницы ~ 5сек.
                2. Evgeny Epifanov 23 сентября 2013, 17:14(Комментарий был изменён) # 0
                  Илья, может я доступы лучше скину? Если не сложно, посмотришь?
                  1. Илья Уткин 23 сентября 2013, 17:23(Комментарий был изменён) # 0
                    Давай, на почту ilyautkin@mail.ru
                    1. Evgeny Epifanov 23 сентября 2013, 17:26(Комментарий был изменён) # 0
                      ОК
                      1. Илья Уткин 23 сентября 2013, 17:51(Комментарий был изменён) # 0
                        Посмотрел… Сильнее всего рендер страницы тормозит сниппет мини-корзины — его удаляешь и при переходе по страницам заметно шустрее сайт работает. Может, есть у шопкипера какой-то отдельный сниппет для миникорзины, чтобы пошустрее был? Тут я не смогу подсказать, так как с шопкипером не работал.
                        1. Илья Уткин 23 сентября 2013, 17:53(Комментарий был изменён) # 0
                          Кстати, у вас стоит APC-кеш, соответственно у вас и не будет в папке /core/cache/default/ файлов, так как эта папка родного кеша MODX. А APC, насколько я знаю, сохраняет кеш в оперативке. Но кеширование блоков работает нормально, так что надо что-то делать с корзиной.
                          1. Evgeny Epifanov 23 сентября 2013, 17:55(Комментарий был изменён) # 0
                            Так это и есть миникорзина ))
                            1. Evgeny Epifanov 23 сентября 2013, 17:59(Комментарий был изменён) # 0
                              Кстати, а как Вы смотрите что тормозит страницу?
                              1. Илья Уткин 23 сентября 2013, 18:05(Комментарий был изменён) # 0
                                По очереди блоки прям из шаблона удалял, потом локализовал, в каком чанке проблема, в этом чанке так же — удалил Wayfinder, потом шопкипер и понял.
                                1. Evgeny Epifanov 23 сентября 2013, 18:19(Комментарий был изменён) # 0
                                  Спасибо.
                      2. Михаил Лавронов 19 октября 2013, 16:14(Комментарий был изменён) # 0
                        Если вы используете сниппет phpThumbOf для уменьшения и обрезки изображений, отключите плагин phpThumbofCacheManager (он идет в комлекте пакета phpThumbOf).
                        Иван, привет! А не подскажешь как и где его отключить? А то у меня такая бадья.
                        1. Илья Уткин 20 октября 2013, 17:13(Комментарий был изменён) # 0
                          Нажимаешь правой кнопкой на плагин и выбираешь «Деактивировать»
                          1. Михаил Лавронов 20 октября 2013, 18:13(Комментарий был изменён) # 0
                            Спасибо. До этого вообще не знал)
                        2. Саша Друмс 25 октября 2013, 09:42(Комментарий был изменён) # 0
                          Есть несколько вопросов по оптимизации:
                          1. Можно-ли как-нибудь закешировать вызов getPage который вызывает getResources?
                          2. Как можно оптимизировать время генерации страницы, если у меня портал, с большим количеством выводов сниппетов: getResources (6 кешируемых вызовов) + вывод блога с помощью getPage + Мультиязычность + Опрос + simpleSearch + likeDislike + modxTalks и всё это на одной странице. В данный момент из базы загрузка около 3 секунд, а из кеща около 1 секунды.
                          3. Можно-ли кешировать Опросы, Поиск, Комментарии, Babel?
                          Спасибо.
                          1. Илья Уткин 25 октября 2013, 09:51(Комментарий был изменён) # 0
                            getPage оборачивайте в сниппет getCache, Babel тоже, думаю. Остальное кешировать не получится… Пробуйте с помощью pdoTools получать все данные за один-два запроса, скорость должна повыситься.
                            1. Саша Друмс 25 октября 2013, 09:53(Комментарий был изменён) # 0
                              Спасибо за ответы :-)
                          2. Дмитрий 21 мая 2014, 16:40 # +1
                            Спасибо друг! хорошая статья — очень помог
                            1. Марина 12 июля 2014, 20:24(Комментарий был изменён) # 0
                              Спасибо большое! Очень пригодилось. На самой «тяжелой» странице (там, где витрина со всеми товарами +меню тоже отображает все товары) до оптимизации — около 1000 запросов к базе, 5 секунд время загрузки. После кеширования всех блоков, кроме контента и head — 22 запроса к базе и 0.0265 секунд — загрузка страницы. Разительные изменения!!!
                              Одно НО! — На JavaScript не работает!!! Точнее, работает только первую загрузку, после очистки кеша. Потом подсвечивается все время одна страница. &hereClass=`` есть. Первый раз классы выставляет JavaScript. Почему потом он не вносит изменения?
                              1. Илья Уткин 13 июля 2014, 10:17 # 0
                                Покажи ссылку — скорее всего ошибка в Javascript'е
                                1. Марина 13 июля 2014, 18:22 # 0
                                  Спасибо! Вот ссылка
                                  1. Илья Уткин 14 июля 2014, 10:01 # 0
                                    Этот код нужно вставить в самый низ шаблона, чтобы он не кешировался. Его надо вынести из всех чанков прям в сам шаблон.
                                    <script type="text/javascript">
                                    var tagLimit = 'nav';     // нужен, чтобы ограничить подъём по дереву DOM
                                    var className = 'active'; // какой клас будем проставлять
                                    var setActiveMenuItem = function ($li) {
                                        var $liParent = $li.addClass(className).parent('ul').closest('li');
                                        if ($li.closest(tagLimit).length && $li.length) {
                                            setActiveMenuItem($liParent);
                                        }
                                    }
                                    setActiveMenuItem($(tagLimit +' [href="[[~[[*id]]]]"]').closest('li'));
                                    </script>
                                    1. Марина 14 июля 2014, 12:01 # 0
                                      Заработало! Спасибо!
                              2. Марина 12 июля 2014, 21:56 # 0
                                И еще вопрос: можно ли таким образом кешировать счетчики Яндекс и Гугл. По идее — да, но я сомневаюсь. Спасибо!
                                1. Илья Уткин 13 июля 2014, 10:16 # 0
                                  Счетчики можно кешировать — они на всех страницах одинаковые.
                                2. Rahim 06 апреля 2015, 21:35 # 0
                                  Приветь
                                  А где надо отключит phpThumbOfCacheManager в Modx 2.3.3
                                  1. Илья Уткин 07 апреля 2015, 07:23 # 0
                                    phpThumbOfCacheManager — это плагин. Он входит в состав компонента phpThumbOf и очищает кеш картинок при любой очистке кеша в админке. Если компонент phpThumbOf не установлен, то и отключать ничего не нужно.
                                  2. Alex Lotz 03 августа 2016, 19:41(Комментарий был изменён) # 0
                                    var tagLimit = 'nav';
                                    это тег из bootstrap?

                                    а как сделать если вот так:
                                    <div class="col-xs-12 col-sm-8 col-md-9 column">
                                    	[[pdoCrumbs?
                                    	    &exclude=`2,113`
                                                &showAtHome=`0`
                                                &showHome=`1`
                                                &outputSeparator=``
                                                &tpl=`@INLINE <li><b><a href="[[+link]]">[[+menutitle]]</a></b></li>`
                                                &tplCurrent=`@INLINE <li class="active">[[+menutitle]]</li>`
                                                &tplWrapper=`@INLINE <ul class="breadcrumb">[[+output]]</ul>`
                                              ]]
                                    </div>
                                    1. Илья Уткин 03 августа 2016, 19:44 # 0
                                      Ну, сделайте так, и всё будет ок.
                                      <nav class="col-xs-12 col-sm-8 col-md-9 column">
                                      	[[pdoCrumbs?
                                      	    &exclude=`2,113`
                                                  &showAtHome=`0`
                                                  &showHome=`1`
                                                  &outputSeparator=``
                                                  &tpl=`@INLINE <li><b><a href="[[+link]]">[[+menutitle]]</a></b></li>`
                                                  &tplCurrent=`@INLINE <li class="active">[[+menutitle]]</li>`
                                                  &tplWrapper=`@INLINE <ul class="breadcrumb">[[+output]]</ul>`
                                                ]]
                                      </nav>
                                      1. Alex Lotz 03 августа 2016, 19:47 # 0
                                        супер спс
                                        1. Alex Lotz 03 августа 2016, 21:40 # 0
                                          почему-то
                                          &showAtHome=`0`
                                          не работает
                                      2. Андрей 13 февраля 2017, 09:34 # 0
                                        Сниппет никак не хочет выводить чанк на фронтенд… MODX самый новый. Что-то поменялось в cachemanager в новом 2.5 MODX?

                                        Причем в cache чанк он складывает, но не отображает его.
                                        1. Владимир 11 марта 2017, 14:07(Комментарий был изменён) # 0
                                          Добрый день! Спасибо за идею с кэшированием блоков. Сайт стал работать заметно быстрее. Подскажите пожалуйста как исправить меню, если оно реализовано с помощью pdoMenu. После правки шаблона активной стала только кнопка главной страницы. Куда в этом случае вставить скрипт.
                                          Помогите, с системой MODX пока только знакомлюсь.
                                          Спасибо.
                                          Ниже приведен код сниппета:
                                          <?php
                                          /** @var array $scriptProperties */
                                          
                                          // Convert parameters from Wayfinder if exists
                                          if (isset($startId)) {
                                              $scriptProperties['parents'] = $startId;
                                          }
                                          if (!empty($includeDocs)) {
                                              $tmp = array_map('trim', explode(',', $includeDocs));
                                              foreach ($tmp as $v) {
                                                  if (!empty($scriptProperties['resources'])) {
                                                      $scriptProperties['resources'] .= ',' . $v;
                                                  } else {
                                                      $scriptProperties['resources'] = $v;
                                                  }
                                              }
                                          }
                                          if (!empty($excludeDocs)) {
                                              $tmp = array_map('trim', explode(',', $excludeDocs));
                                              foreach ($tmp as $v) {
                                                  if (!empty($scriptProperties['resources'])) {
                                                      $scriptProperties['resources'] .= ',-' . $v;
                                                  } else {
                                                      $scriptProperties['resources'] = '-' . $v;
                                                  }
                                              }
                                          }
                                          
                                          if (!empty($previewUnpublished) && $modx->hasPermission('view_unpublished')) {
                                              $scriptProperties['showUnpublished'] = 1;
                                          }
                                          
                                          $scriptProperties['depth'] = empty($level) ? 100 : abs($level) - 1;
                                          if (!empty($contexts)) {
                                              $scriptProperties['context'] = $contexts;
                                          }
                                          if (empty($scriptProperties['context'])) {
                                              $scriptProperties['context'] = $modx->resource->context_key;
                                          }
                                          
                                          // Save original parents specified by user
                                          $specified_parents = array_map('trim', explode(',', $scriptProperties['parents']));
                                          
                                          if ($scriptProperties['parents'] === '') {
                                              $scriptProperties['parents'] = $modx->resource->id;
                                          } elseif ($scriptProperties['parents'] === 0 || $scriptProperties['parents'] === '0') {
                                              if ($scriptProperties['depth'] !== '' && $scriptProperties['depth'] !== 100) {
                                                  $contexts = array_map('trim', explode(',', $scriptProperties['context']));
                                                  $parents = array();
                                                  if (!empty($scriptProperties['showDeleted'])) {
                                                      $pdoFetch = $modx->getService('pdoFetch');
                                                      foreach ($contexts as $ctx) {
                                                          $parents = array_merge($parents,
                                                              $pdoFetch->getChildIds('modResource', 0, $scriptProperties['depth'], array('context' => $ctx)));
                                                      }
                                                  } else {
                                                      foreach ($contexts as $ctx) {
                                                          $parents = array_merge($parents,
                                                              $modx->getChildIds(0, $scriptProperties['depth'], array('context' => $ctx)));
                                                      }
                                                  }
                                                  $scriptProperties['parents'] = !empty($parents) ? implode(',', $parents) : '+0';
                                                  $scriptProperties['depth'] = 0;
                                              }
                                              $scriptProperties['includeParents'] = 1;
                                              $scriptProperties['displayStart'] = 0;
                                          } else {
                                              $parents = array_map('trim', explode(',', $scriptProperties['parents']));
                                              $parents_in = $parents_out = array();
                                              foreach ($parents as $v) {
                                                  if (!is_numeric($v)) {
                                                      continue;
                                                  }
                                                  if ($v[0] == '-') {
                                                      $parents_out[] = abs($v);
                                                  } else {
                                                      $parents_in[] = abs($v);
                                                  }
                                              }
                                          
                                              if (empty($parents_in)) {
                                                  $scriptProperties['includeParents'] = 1;
                                                  $scriptProperties['displayStart'] = 0;
                                              }
                                          }
                                          
                                          if (!empty($displayStart)) {
                                              $scriptProperties['includeParents'] = 1;
                                          }
                                          if (!empty($ph)) {
                                              $toPlaceholder = $ph;
                                          }
                                          if (!empty($sortOrder)) {
                                              $scriptProperties['sortdir'] = $sortOrder;
                                          }
                                          if (!empty($sortBy)) {
                                              $scriptProperties['sortby'] = $sortBy;
                                          }
                                          if (!empty($permissions)) {
                                              $scriptProperties['checkPermissions'] = $permissions;
                                          }
                                          if (!empty($cacheResults)) {
                                              $scriptProperties['cache'] = $cacheResults;
                                          }
                                          if (!empty($ignoreHidden)) {
                                              $scriptProperties['showHidden'] = $ignoreHidden;
                                          }
                                          
                                          $wfTemplates = array(
                                              'outerTpl' => 'tplOuter',
                                              'rowTpl' => 'tpl',
                                              'parentRowTpl' => 'tplParentRow',
                                              'parentRowHereTpl' => 'tplParentRowHere',
                                              'hereTpl' => 'tplHere',
                                              'innerTpl' => 'tplInner',
                                              'innerRowTpl' => 'tplInnerRow',
                                              'innerHereTpl' => 'tplInnerHere',
                                              'activeParentRowTpl' => 'tplParentRowActive',
                                              'categoryFoldersTpl' => 'tplCategoryFolder',
                                              'startItemTpl' => 'tplStart',
                                          );
                                          foreach ($wfTemplates as $k => $v) {
                                              if (isset(${$k})) {
                                                  $scriptProperties[$v] = ${$k};
                                              }
                                          }
                                          //---
                                          
                                          /** @var pdoMenu $pdoMenu */
                                          $fqn = $modx->getOption('pdoMenu.class', null, 'pdotools.pdomenu', true);
                                          $path = $modx->getOption('pdomenu_class_path', null, MODX_CORE_PATH . 'components/pdotools/model/', true);
                                          if ($pdoClass = $modx->loadClass($fqn, $path, false, true)) {
                                              $pdoMenu = new $pdoClass($modx, $scriptProperties);
                                          } else {
                                              return false;
                                          }
                                          $pdoMenu->pdoTools->addTime('pdoTools loaded');
                                          
                                          $cache = !empty($cache) || (!$modx->user->id && !empty($cacheAnonymous));
                                          if (empty($scriptProperties['cache_key'])) {
                                              $scriptProperties['cache_key'] = 'pdomenu/' . sha1(serialize($scriptProperties));
                                          }
                                          
                                          $output = '';
                                          $tree = array();
                                          if ($cache) {
                                              $tree = $pdoMenu->pdoTools->getCache($scriptProperties);
                                          }
                                          if (empty($tree)) {
                                              $data = $pdoMenu->pdoTools->run();
                                              $data = $pdoMenu->pdoTools->buildTree($data, 'id', 'parent', $specified_parents);
                                              $tree = array();
                                              foreach ($data as $k => $v) {
                                                  if (empty($v['id'])) {
                                                      if (!in_array($k, $specified_parents) && !$pdoMenu->checkResource($k)) {
                                                          continue;
                                                      } else {
                                                          $tree = array_merge($tree, $v['children']);
                                                      }
                                                  } else {
                                                      $tree[$k] = $v;
                                                  }
                                              }
                                              if ($cache) {
                                                  $pdoMenu->pdoTools->setCache($tree, $scriptProperties);
                                              }
                                          }
                                          if (!empty($tree)) {
                                              $output = $pdoMenu->templateTree($tree);
                                          }
                                          
                                          if ($modx->user->hasSessionContext('mgr') && !empty($showLog)) {
                                              $output .= '<pre class="pdoMenuLog">' . print_r($pdoMenu->pdoTools->getTime(), 1) . '</pre>';
                                          }
                                          
                                          if (!empty($toPlaceholder)) {
                                              $modx->setPlaceholder($toPlaceholder, $output);
                                          } else {
                                              return $output;
                                          }
                                          
                                          1. Илья Уткин 13 марта 2017, 10:53 # 0
                                            Решение точно такое же, как и для Wayfinder

                                            <nav>
                                            [[pdoMenu? &startId=`0` &hereClass=``]]
                                            </nav>
                                            <script type="text/javascript">
                                            var tagLimit = 'nav';     // нужен, чтобы ограничить подъём по дереву DOM
                                            var className = 'active'; // какой клас будем проставлять
                                            var setActiveMenuItem = function ($li) {
                                                var $liParent = $li.addClass(className).parent('ul').closest('li');
                                                if ($li.closest(tagLimit).length && $li.length) {
                                                    setActiveMenuItem($liParent);
                                                }
                                            }
                                            setActiveMenuItem($(tagLimit +' [href="[[~[[*id]]]]"]').closest('li'));
                                            </script>
                                          2. Андрей 24 августа 2017, 22:40 # 0
                                            Добрый день.
                                            А если сайт полностью на fenom (ну или почти полностью:) ), то как-то изменится синтаксис оптимизации?
                                            1. Илья Уткин 24 августа 2017, 23:51 # 0
                                              На Fenom будет как-то так:
                                              {'chunk' | snippet : ['name' => 'header']}
                                            2. Кирилл Киселев 09 октября 2017, 18:39(Комментарий был изменён) # 0
                                              Илья, здравствуйте! Спасибо за статью. Вопрос, если сниппет принимает AJAX запросы, то стоит вызывать его кэшируемым? А так же сниппеты, которые обрабатывают $_SESSION массив?
                                              1. Илья Уткин 10 октября 2017, 08:56 # 0
                                                Такие сниппеты, конечно, надо некешированными вызывать — иначе они не смогут корректно работать.
                                              2. Олег 05 января 2018, 01:02 # 0
                                                Илья, добрый день! Подскажите, может сталкивались с такой проблемой, после включения кэширования на сайте, страницы отображаются с одинаковыми title. Сайт eurostroy.ru
                                                1. Илья Уткин 08 января 2018, 15:05 # 0
                                                  Чанк head не надо кешировать, потому что в нём как раз и находятся разные title для разных страниц.
                                                2. Дмитрий 10 марта 2018, 11:18 # 0
                                                  Идея со сниппетом chunk — из разряда «вредных советов».
                                                  Зачем использовать какой-то лишний сниппет?
                                                  И писать постоянно его вызов. Когда проще и быстрее написать вызов чанка. Причем всё чаще начинаю встречать бездумное применение этого совета в поддерживаемых сайтах.

                                                  Прироста скорости и уменьшения нагрузки — ну не смеши. На 0.0001 сек. быстрее?)) И прям пррисходит великая нагрузка при генирации кэша чанка?

                                                  Лучше посоветуй перейти с устаревшего wayfinder на pdoMenu.
                                                  1. Илья Уткин 19 марта 2018, 09:39 # 0
                                                    Ты пишешь комментарий к статье, которая написана 10 апреля 2013 г. Сниппета pdoMenu тогда ещё не существовало. Сейчас я и сам сниппет chunk не применяю. Больше того — я использую и всем советую использовать Fenom, а не нативный парсер MODX.
                                                  2. NDruce 21 марта 2018, 19:55 # 0
                                                    Этот плагин удаляет все сгенерированные ранее картинки во время очистки кеша (а это происходит постоянно — при сохранении любых документов, например
                                                    Ложь! Великая и огромная ложь. Кэш phpThumbOf не удаляется при сохранении документов (ресурсов), он удаляется при очищении кэша из меню «Управление -> Очистить кэш». При сохранении документов чистится только кэш документов, phpThumbOf при этом не затрагивается.

                                                    Я это всё проверял. Исправьте
                                                    1. Lana 23 июля 2020, 20:41 # 0
                                                      Добрый день. Подскажите пожалуйста, почему тормозит Formit (modx revo).
                                                      Письмо уходит, но очень долго. 30 сек.висит страница. Намучилась уже. как ускорить процесс?
                                                      1. Илья Уткин 27 июля 2020, 09:59 # 0
                                                        Попробуйте настроить отправку на сайте через SMTP. Или обратитесь в техподдержку к вашему хостингу.
                                                        1. Lana 30 июля 2020, 15:33 # 0
                                                          после чистой установки modx revo последней версии и установки formit, отправка почты через smtp дает задержку 26с. как можно ускориться?

                                                          1. Илья Уткин 30 июля 2020, 17:16 # 0
                                                            Я не знаю
                                                      2. Lana 31 июля 2020, 12:36 # 0
                                                        решена проблема.
                                                        в строке &hooks=`spam,email` надо убрать spam.
                                                        т.к. в formit идет обращение к api.stopforumspam.org/, а он недоступен и вылетает по таймауту.
                                                        1. Илья Уткин 31 июля 2020, 15:49 # 0
                                                          Ого, вот это да. Ну, спасибо, что решение здесь написали)
                                                        2. Сергей 18 сентября 2022, 18:57 # 0
                                                          Есть еще такой Кэширующий плагин для MODX modxExtraCachePlugin, время отклика падает до 40 мс.

                                                          Авторизация

                                                          через сервис Loginza:


                                                          Шаблоны MODX

                                                          1 2 Дальше »

                                                          Объектная
                                                          модель
                                                          MODX