Как пишут frontend на более менее больших проектах?
Ваш код вполне себе нормальный, если его разделять на модули, как вы и написали и не мешать всё в 1й куче.
Если у вас нету никаких новомодных фреймворков, то сценарий простой. Берём вебпак, в него суём бабель, раз у вас импорты и экспорты с классами, настраиваем сборку файлов.
Разделяете всё на модули, например, из описанных вами://MobileMenu.js
export default class MobileMenu {
constructor() {
this.isVisible = false;
}

toggle () {
this.isVisible = !this.isVisible
}
}
 с остальными модулями по аналогии.
Далее, создаёте файлик, где нужно запускать нужные модули. Например, у вас есть страница каталога, а есть страница товара. Для каждой страницы создавать свой файлик, в котором вы будете запускать свои модули, если это требуется.// Catalog.js
import MobileMenu from '/path/MobileMenu'

(() => {
$(document).on('ready', () => {
let mobileMenu = new MobileMenu();
});
})()
ну а для детальной каталога у вас будет свой файл, который будет что-то запускать, нужное вам. А так же, должен быть глобальный файл, в котором будут запускаться модули, нужные на всём всём сайте. Надеюсь понятно описал.
Это грубый пример, но надеюсь, сможете что-то подчерпнуть.
P.S. У нас есть некоторые, большие проекты, на которых мы используем +- такой подход - это старые проекты, написанные давно, но до сих пор живущие. В один миг, была такая каша из хер пойми какого кода, не пойми, что за что отвечает, багов куча, т.к. где-то запускалось то, что не должно в принципе, код обрастал кучей костылей, условий. Было 2 варианта, как проекту жить дальше, переписать на rest + vue или сделать как я написал в ответе. Выбор пал на 2й вариант. Т.к. 1й вариант потребовал бы титонических усилий, что бы всё переписать, денег на это не дали бы. 
Получилось вполне не плохо. Сейчас, при заходе нового проекта, мы сразу делаем rest api + vue. Не только потому, что это модно и т.п., а очень разделяет всё всё на компоненты, модули и т.п, отделяет бек от фронта, что уже облегчает разработку, ведь тебе уже не нужно бекенд разворачивать, делать постоянно миграции, бегать к бекендерам, если на их стороне что-то сломалось и т.п. удобства
Сниппет кода который вы привели, очень похож на общее представление о веб-компоненте. Если класс CategoiesFilter расширит класс React.Component, вам будет нужно реализовать метод render. Тогда вы сможете избавиться от селекторов по заранее написанному html.
Опционально вы сможете реализовать методы т.н. хуки жизненного цикла, тогда вы получите более тонкий контроль.
Говорят пользователи Vue используют очень похожий подход к моделированию, но пользуются более гибким синтаксисом рендер функции (они просто пишут html и css.)
Если речь идёт о более крупных приложениях, часто испозуют redux и mobx. Они реализуют похожие паттерны flux и observable. Но это уже совсем другая история. 
Смысл всех этих технологий в Virtual DOM. Он позволяет не писать все состояния вашего приложения сразу в html, а вычислят его на основе данных в JavaScript и только тогда создавать html наиболее оптимальным образом.