Здравствуйте, меня зовут… Человек. Число рук равно 2. Число ног равно 2. Группа крови равна 1. Резус равен истине. Вам может показаться, что лишь по этой информации, без имени, фамилии и даже никнейма меня сложно отличить от множества других авторов статей. И будете правы. Однако, во фронтенде я часто вижу, как имя элемента заменяется его описанием. И это никого не волнует.  Присаживайтесь поудобнее, впереди вас ждёт увлекательное путешествие по серьёзным проблемам серьёзных проектов, которые, тем не менее, зачастую недооценивают. Представьте себя в роли писателя автоматических End-To-End тестов. Вам нужно протестировать, что, например, в Яндексе в правом верхнем углу выводится правильное имя залогиненного пользователя.  Дайте заверстать эту страницу типичному верстальщику и он родит вам нечто такое: Вопрос на засыпку: как найти дом-элемент, где выводится имя пользователя?  Тут у тестировщика есть выбор из двух стульев: Как видно, один вариант хуже другого — страдают либо разработчики, либо тестировщики. Причём, как правило перекос в сторону последних. Потому как они вступают в дело как правило, когда первые уже закончили разработку этой фичи, и вовсю пилят другую. Посмотрим, как справились с вёрсткой этого простого блока верстальщики из Яндекса (пришлось удалить 90% мусора, чтобы была видна суть): Благодаря БЭМ, у нужного нам элемента есть в имени класса информация о контексте на 1 уровень вверх (какое-то имя пользователя в какой-то карточке). Но можно ли быть уверенным, что такая карточка всегда будет одна на странице? Смелое предположение. А значит, опять приходится выбирать между двумя табуретами.  А вот другой пример с полем поиска, где разработчика заставили поставить идентификатор. Ну он и поставил: Разработчик плохой? Нет, плоха архитектура, не помогающая генерировать глобально уникальные идентификаторы. Представьте себя в роли аналитика. Вам нужно понять по чему пользователи чаще кликают для открытия меню аккаунта в боковой панели Яндекса: по имени пользователя или по его аватарке.  Информация нужна вам вот прямо сейчас, поэтому скамья у вас есть только одна — работать с той статистикой, что уже собрана. Даже если разработчики озаботились тем, чтобы уже записывались все клики по всем страницам с сохранением всей иерархии элементов в момент клика, вы всё-равно не сможете составить правильный селектор, чтобы отфильтровать нужные клики. И даже если вы попросите это сделать разработчика — он тоже скорее всего где-то ошибётся. Особенно, если вёрстка менялась со временем. То есть вам нужны глобальные уникальные идентификаторы нужных вам элементов. Причём проставленные сильно заблаговременно. Но так как разработчики в массе своей плохо справляются с точным предсказанием будущего, то выглядит это как правило так: аналитик приходит к разработчикам и просит поставить идентификатор, а информацию получает в лучшем случае через месяц.  В случае с Яндексом плод героических усилий разработчиков выглядит так: Эх, а как было бы классно, чтобы у любого элемента всегда были такие идентификаторы. Чтобы они были так же понятны человеку. И при этом были стабильными, а не менялись при любом изменении вёрстки. Но с ручным приводом счастья не достигнуть. Представьте себя в роли верстальщика. Вам нужно по особенному стилизовать имя пользователя в карточке в боковой панели Яндекса. Сделаем первый подход к снаряду, без использования БЭМ. Вот ваш компонент: А вот пачка компонент, поддерживаемая совсем другими ребятами: Всё в сумме даёт такой результат:  Если используется изоляция стилей, то присесть вам попросту не на что. Стойте и ждите, пока эти другие ребята добавят в свои компоненты прокидывание кастомного класса через LegoSidebar, до LegoUsername: И хорошо, если они вложены друг в друга непосредственно, а не через десяток промежуточных компонент. Иначе они помрут под грузом лапши из копипасты.   Если же изоляция не используется, то добро пожаловать в кресло из хрупких селекторов: Однако, если бы у нас был инструмент, который бы брал локальные имена: Склеивал бы их с учётом вложенности компонент, генерируя классы вплоть до корня приложения: То мы могли бы стилизовать любой элемент, как бы глубоко он ни находился: Не, это фантастика. Такого не бывает.  Представьте себя в роли разработчика библиотеки рендеринга. Высокоэффективные реактивные алгоритмы с применением VirtualDOM, IncrementalDOM, DOM Batching и прочих WhateverDOM позволяют вам всего лишь за считанные секунды генерировать такого вида DOM для скрам-доски: Из такого вида стейта: И вот незадача: пользователь начинает дрегендропить задачи туда-сюда и ожидает, что происходить это будет быстро. Казалось бы, нужно просто взять DOM элемент задачи и переместить его в другое место в DOM-e. Но это придётся вручную работать с DOM и быть уверенным, что во всех местах задачи всегда рендерятся в адсолютно одно и то же DOM дерево, что зачастую не так — мелкие различия обычно есть. Короче, менять DOM вручную — это как сидеть на одноколёсном велосипеде: одно не осторожное движение и ничто вас уже не спасёт от силы тяжести. Надо как-то объяснять системе рендеринга, чтобы она понимала, где задача перенесена, а где одна была удалена, а другая добавлена.  Чтобы решить эту проблему, необходимо снабжать вьюшки идентификаторами. Если идентификаторы совпали, то отрендеренную вьюшку можно просто перенести в новое место. Если не совпали, значит это разные сущности и нужно одну уничтожить, а другую создать. При этом важно, чтобы идентификаторы не повторялись, и не могли совпасть случайно. Пока вы переносите элементы в рамках одного родительского DOM элемента, вам могут помочь аттрибут key из Реакта, параметр ngForTrackBy из Ангуляра и аналогичные штуки в других фреймворках. Но это слишком частные решения. Стоит перенести задачу в другую колонку, и все эти оптимизации перестают работать. Но если бы у каждого DOM элемента был был глобально уникальный идентификатор, не зависящий от того, куда этот элемент отрендерен, то использование getElementById позволило бы быстро переиспользовать существующее DOM дерево при переносе сущности из одного места в другое. В отличие от упомянутых выше костылей к рендерингу списков, глобальные идентификаторы решают проблему системно и не сломаются, даже если в колонках появятся группироки или ещё какая дичь:  Представьте себя в роли верстальщика. И вам нужно добавить div вот туда. Представили? Теперь убейте себя. Вы уже испорчены html-ом. На самом деле вам нужно не div добавить, а блок названия карточки. title — имя этого блока, отражающее его семантику в месте использования. div — тип блока, отражающий его внешность и поведение независимо от места использования. Если бы мы верстали на TypeScript, то это выражалось бы так: Мы можем тут же создать экземпляр типа: И позволить тайпскрипту вывести тип автоматически: Ну а тут уже и до HTML не далеко: Обратите внимание, что Title — это не просто случайное имя переменной, которой воспользовался и выбросил. Это первичная семантика данного элемента. И чтобы её не терять она должна быть отражена в результате: И снова избавляемся от тавтологии: Добавим остальные элементы: Учтём, что кроме заголовка карточки задачи может быть много других заголовков и в том числе заголовков карточек других задач: Таким образом у нас для каждого элемента формируются человекопонятные идентификаторы предельно точно отражающие их семантику и потому являющиеся глобально уникальными. Обратите внимание, что семантика определяется по принадлежности, а не по расположению. Хотя карточка задачи /dashboard/task=fh5yfp6e и находится в колонке /dashboard/todo, но принадлежит она дашборду /dashboard. Именно он её создал. Он её настроил. Он дал ей имя и обеспечил уникальность её идентификатора. Он ею полностью управляет. Он же её и уничтожит.  А вот использование "правильных html тегов" — это не семантика, это типизация: Обратите внимание на два тега time имеющих совершенно разную семантику. Представьте себя в роли переводчика. У вас предельно простая задача — перевести строку текста с английского на русский. Вам досталась строка "Done". Если это действие, то переводить надо как "Завершить", а если состояние, то как "Завершено", но если это состояние задачи, то "Выполнено". Без информации о контексте употребления невозможно правильно перевести текст. И тут у разработчика опять есть две лавки:  Но что если мы не хотим абы как сидеть, а хотим гордо стоять на твёрдой основе из лучших практик? Тогда нам нужно в качестве ключа использовать комбинацию из имени типа (компонента, шаблона), локального имени элемента в рамках этого типа и имени его свойства. Для текста "Done" как названия колонки таким ключом будет github_issues_dashboard:column-done:title. А для текста "Done" на кнопке завершения задачи в карточке задачи идентификатор будет уже github_issues_task-card:button-done:label). Это, конечно, не те идентификаторы, о которых мы говорили ранее, но формируются эти ключи из тех же имён, которые мы явно или неявно даём составным элементам. Если мы именуем их явно, то у нас есть возможность автоматизировать генерацию различных ключей и идентификаторов. Но если неявно, то приходится задавать эти ключи и идентификаторы вручную и надеяться, что не вспыхнет бардака с именованием одной и той же сущности в разных местах по разному. Представьте себя в роли прикладного разработчика. Прилетает вам баг-репорт: — Ага, та самая известная f из какой-то кнопки, на какой-то панели, — заявил бы диванный эксперт, — Всё ясно.  Или нет? А если бы это был всего один уникальный идентификатор: — Ага, кнопка закрытия сайдбара сломалась. ВасяП, это к тебе, шевели булками. Садится Вася за приложуху, вбивает полученный идентификатор в девелоперской консоли, и тут же получает экземпляр компонента, где сразу видно, что кнопке в качестве обработчика нажатия кто-то умный передал строку:  Хорошо, что у каждого компонента есть идентификатор. И по этому идентификатору этот компонент легко получить, без плясок вокруг дебаггера. Правда нужен инструмент, позволяющий найти компонент по идентификатору. А что если сам идентификатор будет программным кодом, для получения компонента? Тогда этот код можно скопипастить прямо в консоль для быстрого доступа к состоянию компонента, сколько бы раз мы ни перезагружили страницу и не изменяли код. Если вы используете $mol, то вам ничего делать не надо — просто садитесь и получаете вибромассаж по полной: Программист просто синтаксически не сможет не дать компоненту уникальное имя. Из этого описания компонент генерируется следующий DOM: Код в идентификаторах мало того, что глобально уникальный, так ещё и являет собой API через который можно обратиться к любому компоненту. Ну а стектрейсы — это просто сказка:  Если вы подсели на Реакт, то можете пересесть на кастомный JSX трансформер для генерации глобально уникальных идентификаторов по локальным именам вложенных в компонент элементов. Можете сделать себе такой же да ещё и с генерацией классов для стилизации. На примере с дашбордом шаблоны выглядят примерно так: На выходе генерируя:  Если же вы в заложниках у какого-либо другого фреймворка, то создавайте его авторам issue на добавление опциональной возможности генерации идентификаторов и классов. Или хотя бы на добавление API, через который такие возможности можно было бы реализовать самостоятельно. Резюмируя, напомню, зачем надо давать уникальные имена всем элементам: Адвокат Дьявола