Помните об использовании и глубине компонента Ant Design Menu

React.js Ant Design

1. Спрос

В недавнем проекте нужно изменить исходное меню, интерфейс проекта — antd, а меню навигации antd выглядит так:

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

Левое и правое не выровнены, это слишком некрасиво, если отпустишь, тебя будут ругать.

2. Устраните проблему

Начните просматривать элементы, сначала проверьте стили, с которыми обычно может совпадать официальная демонстрация:

Давайте посмотрим на стили, которые моя демонстрация не может выровнять:

Нашел недостающее в моем менюmin-width, то есть antd добавил атрибут стиля в официальную демку на определенном этапе, но не в мое меню.

Почему бы не добавить мой! ! ! ?

Давай, давай первымMutationObserver, подробности см. в документации MDN.

ты хочешь бытьantdдай этоulдобавить тегиstyle="min-width"сказать когда? Можете даже поставить точку останова для отладки, но не знаете, как это сделать? Перейдите непосредственно к коду:

var ele = document.getElementById('item_2$Menu')   // 先找出该元素
var config = {attributes: true, attributeFilter: ['style']}
var callback = function (mutationsList) {
  console.log(mutationsList)
}
var observer = new window.MutationObserver(callback)
observer.observe(ele, config)

Приведенный выше код означает, что вitem_2$MenuизstyleПри изменении свойства выведитеmutationsList. Итак, когда я перемещаю мышь в меню, печатается следующее:

Какая от этого польза? Не волнуйтесь, это означает, что при перемещении мыши код здесь будет выполняться, так почему бы не сделать точку останова?

При перемещении мыши браузер останавливается в точке останова, и праваяcall stackстек вызовов, показывающий выполнениеcallbackфункция, см. нижеadjustWith, то есть код выполняется первымadjustWith, который затем попал в нашу точку останова. Давайте нажмем наadjustWith, нашел следующий код:

Кажется, это вызвано этим кодом. Неудобно смотреть в браузере, это все скомпилированный код, иди воюйVscode, посмотрите в нашем каталоге node_modules, сначала найдите этот файлnode-modules/rc-menu/es/SubMenu.jsизadjustWidthметод:

this.adjustWidth = function () {
  /* istanbul ignore if */
  if (!_this3.subMenuTitle || !_this3.menuInstance) {
    return;
  }
  var popupMenu = ReactDOM.findDOMNode(_this3.menuInstance);
  if (popupMenu.offsetWidth >= _this3.subMenuTitle.offsetWidth) {
    return;
  }

  /* istanbul ignore next */
  popupMenu.style.minWidth = _this3.subMenuTitle.offsetWidth + 'px';
};

Получается, что он сначала определит ширину, еслиpopupMenuшире родительскогоTitleШирина будет возвращена напрямую и будет добавлена, когда она меньшеmin-widthАтрибуты.

Потребовалось столько усилий, чтобы наконец найти его!

Но нашли и что дальше? Вопрос как его выровнять? Поскольку длина копии непостоянна, центр выравнивается.

3. Решить

продолжить просмотрantd, чтобы увидеть, отсутствуют ли какие-либо методы параметров, и обнаружил, что есть небольшой угол документа менюMore options in rc-menu ,нажмите вЗатем я открыл для себя более широкий мир (на самом деле, я спросил своих коллег раньше, что и все еще зависит отreact-component, это библиотекаantdКомпонентная реализация).

После некоторых поисков я нашел реквизит под названиемbuiltinPlacementsочень подозрительно, описаниеDescribes how the popup menus should be positioned(描述popup的菜单如何被定位), параметрdom-alignОбъект конфигурации, продолжить просмотрdom-alignизпредставлятьЯ обнаружил, что это небольшая библиотека, которая обрабатывает позиционирование, имея дело с позиционными отношениями между domA(sourceNode) и domB(targetNode):

const alignConfig = {
  points: ['tl', 'tr'],        // align top left point of sourceNode with top right point of targetNode
  offset: [10, 20],            // the offset sourceNode by 10px in x and 20px in y,
  targetOffset: ['30%','40%'], // the offset targetNode by 30% of targetNode width in x and 40% of targetNode height in y,
  overflow: { adjustX: true, adjustY: true }, // auto adjust position when sourceNode is overflowed
};

domAlign(domA, domB, alignConfig);

Таким образом, домА左上角(tl)и домби右上角(tr)Выровнять. скажи мне интуитивноbuiltinPlacementsСвойства решили мою проблему с выравниванием.

Далее продолжить отладку, сначала ввести средство отладки (мне коллега сказал). Подумайте об этом, до того, как мне пришлось отлаживать интерфейсный код, было кучаconsole.logили в браузереsourceОтладка точки останова в середине, вы также можете отлаживатьnode_modulesКод внутри (тоже узнал от коллег), но минус в том, что код скомпилирован и запакован, а читабельность не очень. Так что естьVscodeплагинDebugger for Chrome, с помощью этого плагина вы можете напрямуюVscodeВнутри внешнего кода jsточка останова! .

Следующие возможные прыжки:

Найти непосредственно под node_modulesrc-menu/es/submenuКаталог — это компонент меню под реактивным компонентом. Искать сначала в папкеbuiltinPlacementsПосмотрите, где используется это слово. Найдите следующее:

  1. rc-menu/es/submenu.js
...
var builtinPlacements = props.builtinPlacements;
...
React.createElement(
    Trigger,
    {
        ...
        builtinPlacements: _extends({}, placements, builtinPlacements),
        ...
    }

Получается, что Submenu получил входящийbuiltinPlacementsиспользуется для созданияTrigger, продолжай искатьTriggerОбнаружить:

  1. rc-trigger/es/index.js
Trigger.prototype.getPopupAlign = function getPopupAlign() {
    ...
    var builtinPlacements = props.builtinPlacements;
    ...
    return getAlignFromPlacement(builtinPlacements, popupPlacement, popupAlign)
};

...
var align = _this5.getPopupAlign();
...
return React.createElement(
      Popup,
      _extends({
        align: align,
    })
)

Получил, снова использовал его для созданияPopupДа, но сначала запомните эту функциюgetAlignFromPlacement(builtinPlacements, prefixCls, align, alignPoint):

  1. rc-trigger/es/Popup.js
import Align from 'rc-align';
...
React.createElement(
  Align,
  {
    ...
    align: align
    ...
  },
}

взяться за созданиеAlignсейчас:

  1. rc-align/es/Align.js
import { alignElement, alignPoint } from 'dom-align';  // 你总算出来了
...

var align = _this$props.align
...
if (element) {
  result = alignElement(source, element, align);
} else if (point) {
  result = alignPoint(source, point, align);
}
...

Так что, вероятно, отношенияAntd Menu => rc-menu => rc-trigger => rc-align => dom-align ...

где тыMenuвходящийbuiltinPlacementsПараметры, которые будут переданы как параметры в rc-triggergetAlignPopupClassName(builtinPlacements, prefixCls, align, alignPoint), результат в конечном итоге будет переданdom-alignизalignElementилиalignPointсередина.

ноbuiltinPlacementsКак передать значение этого параметра?

  1. function getAlignFromPlacement()

Это означает, что нам нужно передать такой объект:

builtinPlacements: {
    bottomLeft: {
        // alignConfig对象
        points: ['tl', 'tr'],
        offset: [10, 20],
        ...
    },
    leftTop: {
        ...
    }
}

И знайте: getAlignFromPlacement(builtinPlacements, PlacementStr, align) вplacementStrВ настоящее времяbottomLeft, поэтому наше Меню становится:

<Menu builtinPlacements={
    {
        bottomLeft: 
        {
            points: ['tc', 'bc'], // 子菜单的 "上中" 和 对应菜单的title "下中" 对齐。
            overflow: {
              adjustX: 1,
              adjustY: 1
            },
            offset: [0, 5]
        }
    }
}>
{this.renderMenuItems(menuItems)}
</Menu>

Что касаетсяplacementStrзначениеbottomLeft,Фактически:

rc-menu/es/SubMenu.js

var popupPlacementMap = {
  horizontal: 'bottomLeft',
  vertical: 'rightTop',
  'vertical-left': 'rightTop',
  'vertical-right': 'leftTop'
};

var popupPlacement = popupPlacementMap[props.mode];
// 这个值最终作为"placementStr"的值,水平菜单Menu的"mode"为"horizontal"时,"placementStr"即为"bottomLeft"。  

bottomLeft , rightTopУмеренная карта местоположения, которую я думаю, может обратиться к Andd'sPopconfirm.

Финальные рендеры:

4. Резюме

В этой статье в общих чертах рассказывается о проблеме выравнивания компонентов меню, возникшей в работе.调试思路,antd组件结构,dom-align, MutationObserver 和 Debuggr for Chrome插件, Используемый код не сложен, и я надеюсь, что читатели смогут что-то понять после его прочтения.

Спасибо моим всезнающим коллегам.