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;
    }
}
Author: arno, 2019-05-20

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 из конечного массива
  • повторно объявите метод, исключающий логику, в которой он добавляет активный класс.
 4
Author: Sam, 2019-06-10 19:19:29

Решение @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;
    }
}
 2
Author: RLTcode, 2019-08-20 23:16:24

Ваше решение выглядит хорошо, но, насколько мне известно, если вы установили 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());
}

Надеюсь, это поможет!

 1
Author: Chirag Patel, 2019-06-10 09:45:35