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
Посмотрите, где используется это слово. Найдите следующее:
- rc-menu/es/submenu.js
...
var builtinPlacements = props.builtinPlacements;
...
React.createElement(
Trigger,
{
...
builtinPlacements: _extends({}, placements, builtinPlacements),
...
}
Получается, что Submenu получил входящийbuiltinPlacements
используется для созданияTrigger
, продолжай искатьTrigger
Обнаружить:
- 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)
:
- rc-trigger/es/Popup.js
import Align from 'rc-align';
...
React.createElement(
Align,
{
...
align: align
...
},
}
взяться за созданиеAlign
сейчас:
- 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
Как передать значение этого параметра?
- 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插件
, Используемый код не сложен, и я надеюсь, что читатели смогут что-то понять после его прочтения.
Спасибо моим всезнающим коллегам.