Magento 2.3 - Вкладки Topmenu с неправильным (кэшированным) "активным" классом
У меня небольшая проблема с меню. Активные вкладки не соответствуют активному классу. Если я отключу кэш static_block
, он снова заработает, так что на самом деле похоже, что меню кэшируется и продолжает отображать содержимое, которое кэшируется, независимо от выбранной вкладки.
Я попытался поместить cacheable=false
в блок соответствующего верхнего меню в макете default.xml
, но это ничего не изменило.
если у кого-то уже была эта проблема или есть зацепка...
Редактировать :
Я исправил это быстро, удалив topmenu из кэша (если я правильно понял, что сделал), но, как вы можете видеть, это не страшно, но сейчас работает очень хорошо
<?php
namespace MyNamespace\Indo\Block\Html;
use Magento\Framework\Data\Tree\NodeFactory;
use Magento\Framework\Data\TreeFactory;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\LayoutFactory;
use TemplateMonster\Megamenu\Helper\Data;
class Topmenu extends \Magento\Theme\Block\Html\Topmenu
{
public $_helper;
public $_layoutFactory;
public function __construct(
Template\Context $context,
NodeFactory $nodeFactory,
TreeFactory $treeFactory,
Data $helper,
LayoutFactory $layoutFactory,
array $data = []
)
{
parent::__construct($context, $nodeFactory, $treeFactory, $data);
$this->_helper = $helper;
$this->_layoutFactory = $layoutFactory;
}
/**
* Get block cache life time
*
* @return int
* @since 100.1.0
*/
protected function getCacheLifetime()
{
return 0;
}
}
3 answers
Насколько я понимаю, идеального решения этой проблемы не существует. Какое бы исправление вы ни решили внедрить, оно будет иметь свои недостатки.
Я думаю, что также могут быть 2 других метода, которые вы можете использовать: ESI (включая боковую сторону) или пробивание отверстий. Хотя у меня нет никакого опыта или знаний об этом, так что, если это правильные методы для этой цели. Я позволю кому-нибудь другому, кто лучше их понимает, упомянуть и объяснить их.
TLDR
Если вы хотите, чтобы навигатор всегда отображал правильное активное состояние и не возражал против небольшого снижения производительности, затем отключите кэширование в блоке навигации и постарайтесь, чтобы он был как можно ближе к корневому блоку.
Если вам нужна максимальная производительность, отключите рендеринг на стороне сервера и надейтесь, что у посетителя включен JS.
Обзор
В основном Magento существует логика как на стороне сервера, так и на стороне клиента для установки активного состояния элементов навигации. И по умолчанию оба сервера и логика на стороне клиента по какой-то причине активна.
Логика на стороне сервера
Проблема с использованием логики на стороне сервера для этого заключается в том, что вы не можете кэшировать блок, так как вы также собираетесь кэшировать это активное состояние. Когда вы отключаете кэширование для меню навигации, в лучшем случае вы аннулируете только полный кэш страницы, в худшем случае вы аннулируете некоторые другие блоки, такие как заголовок и т. Д., В зависимости от того, как настроена ваша тема.
Логика на стороне сервера можно найти в Magento\Theme\Block\Html\Topmenu::_getMenuItemClasses()
Он использует свойства is_active
и has_active
для переданного атрибута узла дерева,
которые задаются в Magento\Catalog\Plugin\Block\Topmenu::getCategoryAsArray
Логика на стороне клиента
Недостатком этого метода является отсутствие активного состояния, отмеченного до загрузки JS. И если у клиента отключен JS, то у них вообще не будет активного индикатора.
Функция, которая обрабатывает логику на стороне клиента, - это lib/web/mage/menu.js::_setActiveMenu()
Способ работы этой функции заключается в том, что она выполняет поиск по href элементов навигации, проверяя, соответствует ли один из них текущий URL-адрес.
Решения
Отключить кэширование
Вы можете отключить кэширование в блоке навигации, это также отключит кэширование в родительских блоках. Поэтому, если ваш блок навигации находится в блоке заголовка, ваш заголовок также не будет кэшироваться.
Вы можете сделать это, установив TTL
на xml-узле catalog.topnav
значение 0
, которое совпадает с перезаписью блока и перезаписью времени жизни кэша, как вы это сделали, - это ваше редактирование.
<reference name="catalog.topnav" ttl="0"/>
Использовать логика на стороне клиента
Другой способ, который у вас есть, - отключить активное состояние, отображаемое на стороне сервера, и полагаться на логику на стороне клиента.
Поскольку метод Magento\Theme\Block\Html\Topmenu::_getMenuItemClasses()
является закрытым, мы не можем использовать плагин для изменения его результата.
Вы можете перезаписать класс, используя предпочтения, и либо
- вызовите родительский метод и удалите
active
иhas-active
из конечного массива - повторно объявите метод, исключающий логику, в которой он добавляет активный класс.
Решение @arno в исходном вопросе отлично работает для меня, но я хотел немного подробнее рассказать о том, как реализовать код. А также упрощение ненужного кода.
Создайте пользовательский модуль для изменения
App/code/VendorName/ModuleName/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'VendorName_ModuleName',
__DIR__
);
App/code/VendorName/ModuleName/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="VendorName_ModuleName" setup_version="1.0.0">
<sequence>
<module name="Magento_Theme"/>
</sequence>
</module>
</config>
App/code/VendorName/ModuleName/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Theme\Block\Html\Topmenu" type="VendorName\ModuleName\Block\Html\Topmenu" />
</config>
App/code/VendorName/ModuleName/Block/Html/Topmenu.php
<?php
namespace VendorName\ModuleName\Block\Html;
class Topmenu extends \Magento\Theme\Block\Html\Topmenu
{
/**
* Get block cache life time
*
* @return int
* @since 100.1.0
*/
protected function getCacheLifetime()
{
return 0;
}
}
Ваше решение выглядит хорошо, но, насколько мне известно, если вы установили CacheLifetime
ноль, то он отключит кэш для этой страницы, он работает так же, как и cacheable=false
Поэтому я хотел бы рекомендовать удалить кэш программно, а не устанавливать CacheLifetime
ноль.
Вы можете очистить кэш программно, используя следующий способ.
Определите конструктор – передайте Magento\Framework\App\Cache\TypeListInterface
и Magento\Framework\App\Cache\Frontend\Pool
в конструктор вашего файла, как определено ниже.
public function __construct(
Context $context,
\Magento\Framework\App\Cache\TypeListInterface $cacheTypeList,
\Magento\Framework\App\Cache\Frontend\Pool $cacheFrontendPool
) {
parent::__construct($context);
$this->_cacheTypeList = $cacheTypeList;
$this->_cacheFrontendPool = $cacheFrontendPool;
}
Теперь добавьте следующий код в метод, в котором вы хотите clear/flush
кэшировать
$types = array('config','layout','block_html','collections','reflection','db_ddl','eav','config_integration','config_integration_api','full_page','translate','config_webservice');
foreach ($types as $type) {
$this->_cacheTypeList->cleanType($type);
}
foreach ($this->_cacheFrontendPool as $cacheFrontend) {
$cacheFrontend->getBackend()->clean();
}
Таким образом, таким образом, вы можете очистить и очистить кэш.
Примечание: В приведенном выше коде $types=
я определил все типы кэша. вы можете определить тип кэша в соответствии с вашими требованиями.
Существует еще один способ очистки кэша, если вы не хотите записывать жестко закодированный тип кэша
public function __construct(
\Magento\Framework\App\Cache\Manager $cacheManager
) {
$this->cacheManager = $cacheManager;
}
private function whereYouNeedToCleanCache()
{
$this->cacheManager->flush($this->cacheManager->getAvailableTypes());
// or this
$this->cacheManager->clean($this->cacheManager->getAvailableTypes());
}
Надеюсь, это поможет!