company_banner

Как не утонуть в мегабайтах javascript кода? // Доклады с Форума Технологий Mail.Ru 2011: текст доклада, видео, презентации

    Доклад Андрея Сумина (хабрапрофиль) «Как не утонуть в мегабайтах JS-кода?» — очередной в серии расшифровок с Форума Технологий Mail.Ru 2011. Подробности о том, как работает система расшифровки докладов — см. в статье «Изнанка» Форума технологий Mail.Ru: Хай-тек в event-management. Там же, а также на сайте Форума (http://techforum.mail.ru) — ссылки на расшифровки других докладов.


    (Скачать видеоверсию для мобильных устройств — iOS/Android H.264 480?368, размер 170 Mb, видеобитрейт 500 кбит/с, аудио — 64 кбит/с )
    (Скачать видеоверсию большего разрешения H.264 624?480, размер 610 Mb, видеобитрейт 1500 кбит/с, аудио — 128 кбит)
    (Скачать слайды презентации, 4.7Мб)

    Скажу сразу, в этом посте не будет даже упоминаний про последние, внедренные браузерами «фишки». Более того, первый раз эту тему я освещал в 2007 году. Я расскажу про некоторые приемы организации кода, которые вот уже 5 лет помогают мне и моим коллегам успешно разрабатывать проекты с большим количеством JS-кода.

    Давайте вернемся немного назад, примерно в 2002 год. Сайты тогда содержали мало JS-кода. По большей части, на Javascript делали небольшие «рюшечки», вроде смены фона по наведению мышкой. Структура проекта была проста и прозрачна.



    С 2005 года мы видим стремительное развитие, и уже тогда в Рунете появляются вакансии с заголовком «javascript-программист». Проекты тоже стали немного сложнее.



    Я common.js выделил не потому, что он такой важный, а потому, что он стал большим и неуправляемым. Если бы работал не один человек, а команда, каждый бы туда дописывал что-то свое, в конец файла. Поддержка проекта, конечно, усложнилась.



    В итоге мы имеем путаницу, огромный файл и, самое страшное, на мой взгляд — нет code reuse.

    Конкретно у меня было так: какие-то функции по эскейпингу строк, например, html, никто еще тогда не умел этого делать. Ты приходишь на проект или берешь проект другого человека, видишь функцию эскейпинга, пытаешься ее применить, она не работает. Либо баги, либо не то эскейпит все, что надо, либо лишнее эскейпит. Все заканчивалось тем (про тесты уж точно никто не знал), что я очень боялся поменять эту функцию, и просто рядом писал еще одну. Через какое-то время, думал, ну вот везде, где вспомнил, поменял на свою, и она точно лучше, быстрее, в ней нет багов, и, вроде бы, старую можно удалить. Но в последний момент рука дрогнет, ладно оставлю. Так и лежит этот код для следующих поколений.



    Следующие шаги были очевидны: люди стали просто делить один большой файл по каким-то логическим частям. И хорошо, если по логическим — обычно выходило «как получится».



    И будем откровенными — проблемы это, на самом деле, не решило. Размер остался, только раньше это был один файл, а теперь это папка. У нас появились новые проблемы — проблемы с подключением. Если мы бьем на несколько файлов, естественно, мы хотим, чтобы на страницах, где нужна какая-нибудь часть файлов, мы подключали только эту часть файлов. Эту проблему надо было как-то решать. И проблему с сопровождением подключения, потому что по факту получалось так: на всех страницах подключали все скрипты.

    Следующий слайд очень важен и отражает самую главную мысль моего доклада.



    Идея следующая. Да, у нас много JavaScript, да он как-то разбит, да нам надо подключать часть его на страницах по каким-то правилам. Пусть результирующий HTML, который получился в результате отработки всей нашей логики, и будет конфигом того, что нужно подключить.

    Поясню. У нас есть тег div, включающий какой-нибудь законченный блок — допустим, это будет список папок, т. е. в этом диве лежит список папок. Первый шаг, мы помечаем с помощью класса — этот див является компонентом. Это означает, что js-движок будет искать его по этой отметке. Вторая строчка — onclick, там указан return, и какой-то хеш. В этом хеше находится описание того, что это за компонент.

    В результате нам надо найти компонент в DOM-дереве. Определить, что это за компонент (в моем случае информация о типе находится в onclick). Подключить js-файлы, чтобы все это заработало.

    Первая задача решается, по крайней мере в современных браузерах, очень легко. CSS-селекторы работают много где нативно, а там, где не нативно, у вас есть в распоряжении соответствующие библиотеки.

    Теперь самая интересная часть, то что записано в onclick. Там можно зашить любую информацию, которая вам нужна для инициализации. В моем случае это тип компонента, по содержимому хеша я понимаю, что это компонент по работе с папками. Достать его очень легко.



    Таким образом, мы получаем сразу хеш с входными данными для компонента.



    Require — эта та самая функция — то сердце, которое заставит работать всю нашу схему. Естественно, она на каком-то продакшн решении сильно сложнее, но идеи это не меняет.



    Вот этот require записан в какой-то библиотеке, в каком-то компоненте, т.е., в каком-то javaScript-файле, который отвечает за функционал. Javascript-файл с ее помощью говорит ядру, что для моей работы нужен file1 и file2. Это передается первым аргументом, а вторым аргументом ядро вызывает функцию которая выполнится после того, как оно загрузит обе либы.

    Очень важный момент. В function написано loaded(file). Естественно, идея уже давно не нова, естественно, есть реализации, помимо той, которую я описываю. Но в некоторых реализациях есть одно отличие, они пытаются понять, что функционал загружен по каким-то метаданным. Допустим, срабатывание события onload у script или чего-либо подобного. Я принципиально не стал этого делать: тот код, который находится в javascript, вызвав метод loaded, сам точно скажет, что я готов. И не потому, что там что-то загрузилось, не потому, что кто-то думает, что я загрузился. Да, я загрузился, я точно получил то, что мне нужно, я точно готов. Если я по цепочке вызову, кому я нужен дальше, то об этом можно смело сказать и ничего не бояться.



    Вот как это выглядит на небольшой связке. Папкам нужны библиотеки по работе с датами.



    В библиотеке по работе с датами нужны строки, чтобы красиво выводить, например, какая-нибудь реализация printf есть в strings. А strings самодостаточны, им ничего не нужно. И у нас получается цепочка require. В folders.js мы просим dates, по мере загрузки dates у нас вызывается функция, в которой сами folders говорят ядру, что у меня dates есть, ничего не надо, я готова к работе. У дат тоже самое со strings. А strings ничего не нужно, когда они загрузились, они говорят в конце — все, мы загрузились, давай работать дальше.

    Мы рассмотрели полный набор задач для того, чтобы наш пример заработал.



    Первым делом из DOM-дерева мы вычленяем все компоненты, которые нужны. Вторым этапом определяем, что это за компонент, в хеше у нас это будет компонент по работе, давайте скажем, что это компонент по работе со списком писем. Дальше функция require, вот там есть первым аргументом getFileName(type). По строке ядро может понять настоящий путь к файлу, чтобы прописать тегу script в src. Соответственно, в нашем случае вот это будет folders.js. Script folders загрузится. Он скажет в require, что мне нужны dates, dates скажут, что мне нужны strings. Сгенерируются 3 тега script, в обратном порядке сработают loaded и в результате у нас функция, в которой написано — window[type].init(), выполнится и компонент начнет инициализироваться.

    Но отдыхать еще рано, мы получили еще одну проблему. У нас есть загрузка компонентов довольно изолированных, и представим такую ситуацию — вроде бы все хорошо. К разработчику приходит менеджер и говорит: «я хочу, чтобы ты сделал календарь».



    Разработчик говорит, вот будет у меня отдельный div, будет у меня отдельный файл. У меня есть библиотека по работе со строками, которая покрыта тестами с датами, дата, которая покрыта тестами, и я за 2 дня делаю календарь. Показывает менеджеру, он счастлив, показывает коллегам в других проектах. Они приходят опять к вам.



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



    Дальше у вас вот какие варианты: можно заниматься очень долгим проектированием, делать абстракции, делать абстракции над абстракциями, но поверьте мне, когда через три месяца, а если вы блестящий программист, то через полгода, на ваше место придет другой программист работать, он удивится.



    Все мы люди, все мы понимаем, что всё учесть невозможно, поэтому есть не новый паттерн, очень давно используется в программировании. Более того, с самого начала, с появления JavaScript’а, который вовсю используется в браузерах, – это события. Вы делаете календарь, делаете его системным, делаете его неприкосновенным, но делаете к нему событийное API.



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

    Казалось бы все, теперь уже точно все замечательно. У нас есть конфиг в html, у нас есть загрузка компонентов, по запросам, по тому html который есть. У нас есть интерфейс для общения компонентов друг с другом. Но возникла еще одна проблема.

    Если вы знаете сегодня, то завтра вы точно не знаете порядок инициализации компонентов. Представьте себе такую ситуацию: встретился календарь. Он начал загружаться, загрузился, проинициализировался. После инициализации кинул событие, что я сейчас на дате выставлю 7 ноября. Все он это уже сделал, только после этого по DOM встречается маленький компонент, который должен написать 7 ноября. Он вешается на событие, а событие уже произошло. Его уже нет.

    Решение оказалось из раздела серверного программирования, мне его подсказали. Оно существует очень давно и называется — очереди.



    Мы к методу dispatch добавляемеще один аргумент, я выделил его красным,. Это размер очереди. Календарь при своей инициализации делает dispatch, раз уж у нас есть объект callback. Он ему говорит, что размер очереди таких событий один, т.е. если кто-то подписывается на подобное событие и еще ничего не получал, то по стеку first in first out он получит события. Длина очереди будет равна как раз размеру цифры, указанной на последнем аргументе. И он, соответственно, все эти события получает.

    Что касается mail.ru, я тут не год работаю, а всего лишь 4 месяца, я думал, что я сейчас приду с длинным плащом, и всем тут покажу, как надо жить. И в первый же день, когда увидел исходники JavaScript mail.ru понял, что они написали практически то же самое. Местами даже до букв.



    Данный текст основан на докладе Сумина Андрея на Форуме технологий Mail.Ru 2011, проходившем 16 ноября в центре Инфопространство. Подробности о технологии создания текстов докладов по видеозаписям см. здесь: «Изнанка» Форума технологий Mail.Ru: Хай-тек в event-management. Видеоверсии прочих докладов (включая версии для мобильных устройств) доступны на сайте Форума — techforum.mail.ru. Текстовые варианты докладов будут публиковаться здесь и на сайте Форума каждую неделю или немного пореже в похожем формате. Пожалуйста, сообщайте в «личку» об опечатках в тексте.
    Метки:
    Mail.Ru Group 844,39
    Строим Интернет
    Поделиться публикацией
    Похожие публикации
    Комментарии 36
    • +1
      Ну да, всё логично и стройно.
      Только я малость не догнал, зачем писать в onclick?
      Вполне же можно свой аттрибут завести на крайний случай.
      • +3
        onclick дает из коробки всю выразительность JS. Как только вы начинаете передавать данные на вход одних строк становится мало, захочется отдать структуру. Если свой аттрибут, то однозначно строка которую потом захотите прогнать через eval. onclick это все инкапсулирует.
        • +1
          а если кроме onclick есть onkeypress? Опять туда писать то же самое?
          не удобнее ли сделать обертку для работы с DOMElements который бы сделал получение служебных данных прозрачным?
          • +2
            Смысл в том что, нет навешиваний событий через аттрибуты. Поэтому атрибуты освободились и оказались кстати для прокидывания входных данных. Какой именно использовать без разницы.
            • +3
              Да, спасибо — теперь понял.
              Интересное решение, но очень уж не нравится, что это вешается на onclick.
              А так как JS-ядро обходит DOM дерево и данные о элементе стираются из результирующего DOMа, мне кажется, что проще его(ядро) обучить читать и eval'ить атрибут, чем удалять за собой костыли.

              Другими словами, этот малюсенький profit в одном месте, нарушает семантику языка, лишает некоторых возможностей(например реинициализация объекта уже невозможна, т.к. данные из тегов уже удалены) и меня это сильно коробит.
              • 0
                Я рад что мои аргументы понятны, но в целом это не самая принципиальная часть доклада. Есть места намного интереснее.
                • +1
                  Ну, для меня пожалуй этот один из самых интересных моментов. Остальные пришлось уже решать и мы пришли к тем же решениям :)

                  Можно 2 вопросика. Один по теме, а второй околотопиковый.
                  Как же вы все же теперь разделяете и именуете js-файлы. По какому принципу?

                  И вы «гадите» в window? т.е. позволяете ли себе такие конструкции:

                  function a() {...};
                  b = new a(); /* window.b */
                  • 0
                    Разделяем и именуем отталкиваясь от функционала, который файл реализует.

                    Про window, естественно кинуть в window один объект и все за него спрятать это правильно и мы к этому идем. Но на данный момент не могу этим похвастать очень много легаси кода и путь будет длинный.
          • 0
            Ну да, разве что для более красивой и удобной обертке атрибутов в JSON.
            Но лично мне не попадалось ситуаций, где нужны были бы сложные структуры, максимум 2-3 дополнительных атрибута.
            • 0
              Тогда этот вопрос не принципиален. Просто я уже давно не делал проекты которым меньше 10 лет и которые еще поддерживать и поддерживать. В этих условиях лучше сразу заложиться на структуру.
          • +2
            согласен, я для таких целей использую в тегах атрибут data-type
            • 0
              data-type хороший вариант, но опять же это строки. Плюс не забываем про старые браузеры, для них нужно будет писать поддержку. Повторюсь, onclick не имеет этих недостатков.
              • +1
                > Плюс не забываем про старые браузеры, для них нужно будет писать поддержку
                То есть? Как бы нарушается только корректность XHTML'a, в DOM'e атрибуты появятся даже без всяких танцев и не для 'data-*', а просто для 'my_type', скажем.
                А корректность XHTML'a правится введением кастомных DTD.
                Но я хз, зачем добавиться верификации всяких стандартов, когда мы имеем парк браузеров со своими «особенностями». ИМХО, пусть сначала браузеры придут к идентичной работе, тогда можно будет уже стандартизировать страницы.
                • 0
                  Я не про валидность. Нужно писать код, который будет обрабатывать атрибуты. Вопрос, зачем если это код есть в браузере?
                  • +3
                    Не знаю, mis-use, есть такое слово.
                    Да можно, например, ходить за продуктами с туристическим рюкзаком. Удобно, практично (сколько в пакетах унесете? а в рюкзаке килограмм 50 — легко). Одни плюсы, но как-то странно всё это. Ведь не для этого же он разрабатывался :)
                    By design onclick() — интерфейсное событие, генерируемое браузером в ответ на действия юзера. Здесь же предлагается забыть о том что это событие, забыть о том когда событие генерируется, использовать лишь как кастомный метод DOMElement'a.
                    Да и, скажем, вероятность ошибки появляется. Повесим ненароком onclick на этот виджет в процессе исполнения кода, и всё сломается.
                    • +1
                      Нет, ошибок не будет, если события вешаются ТОЛЬКО в компонентах. js ядро находит элемент до инициализации обрабатывает onclick и стирает все упоминания о нем. Дальше в компонент попадает чистенькая dom node.
          • 0
            Я правильно понял, что все библиотеки загружаются в момент инициализации страницы?
            Как, например, решается вопрос ленивой загрузки?
            • +2
              Тут все зависит от проекта, но.

              Как правило, важно в разработке иметь мало маленьких файлов, а на продакшене один большой. В данном случае конфигом может быть не результирующий html, а шаблоны (так сделано на почте mail.ru). Синтаксис require простой и однозначный, поэтому написать на сервере скрипт, которых обходит шаблоны страниц и строит нужный js файл не сложно. Если что-то удаляется из шаблона это автоматом пропадает из собраного js.

              Про ленивую загрузку. Эта задача решается параллельно и никак не противоречит подходу. Если в загруженном компоненте по таймауту или действию пользователя вызвать require, то все догрузится и выполнится. Более того если с сервера получить html размеченный компонентами, вставить его в дом и полученную ноду пропустить через инициализирующий скрипт, скрипт найдет компоненты, поймет что надо догрузить из еще не догруженного и все заработает из коробки.
            • –6
              В Mail.ru наконец-то изобрели виджеты? наверно все же использование onclick — тут не нужно, data-* точно так же работает даже в 6 IE. Доклад описывает какие-то устаревшие несколько лет назад подходы.

              Обрабатывается ли у вас ошибка загрузки JS-файла? Обрабатываются ли JS-ошибки? Информируется ли о них пользователь? Или он сидит перед переставшей работать страницей и гадает, в чем дело?

              Или еще тонкий момент. У вас есть метод observe(). Удаляются ли слушатели при удалении виджета из DOM?

              По поводу скриптов — по мне, так проще в шаблонизаторе у виджета писать зависимости (хотя вы можете не согласиться конечно) вроде этого:

              [widget type=«calendar»]
              [dependencies]
              [jsclass name=«CalendarController» /]
              [css href=«calendar.css» /]
              [/dependencies]
              [body]
              // html -код
              [/body]
              [/widget]

              И при сборке страницы это позволяет, во-первых, строить дерево виджетов, во-вторых, список нужных ресурсов. Если нагрузка не позволяет это делать, можно собирать шаблоны и зависимости при деплойменте.

              Еще момент. Если у вас информация о компоненте хранится в onclick, вам приходится в момент загрузки страницы обходить все дерево DOM в поисках таких компонентов. Также, при загрузке AJAX-контента надо его инициализирвоать. Это же неуклюже, и замедляет инициализацию страницы. Не проще ли назначить например уникальный id каждому виджету и при загрузке написать initWidgets([id1, id2, id3]). Мне кажется, это правильнее. Но опять же, кому как виднее.

              Ну а если вы скажете, что то, что я пишу, сложно, нереализуемо, и т.д. Ок, ну посмотрите тогда как устроен фейсбук или вконтакте. А то, что вы расписали, тут, это каменный век клиентсайд разработки, не хватает еще хаков для netscape.
              • +6
                Вижу сарказм. Сразу говорю что зря. Я понимаю что хочется показать свое я, но это не профессионально. На первый раз отвечу с полной ответственностью, сделав вид что сарказм не заметил.

                Подходы не устарели несколько лет назад, а появились несколько лет назад и эти годы показали что не зря.
                Цитата из статьи «первый раз эту тему я освещал в 2007 году». За 5-ть с лишним лет методология выкристаллизовалась и стало понятно что лишнее, а что нет. Более того я в статье несколько раз говорил что если у вас возникла задача на client-side посмотрите на sever-side 99% она была решена еще в 60-х.

                Про mail.ru, написано что придя в mail.ru я увидел что этими паттернами давно и активно пользуются, что меня еще раз порадовало.

                Про ошибки это вообще отдельная большая тема, надеюсь расскажем. У нас в принципе мониторятся ошибки много и часто. Более того мы контактируем в той или иной степени с разработчиками браузеров. Опере посчастливилось иметь прямо представительство в России. Так вот из Вадима Макеева я пару раз душу вынимал по поводу багов оперы.

                Зависимости в шаблонизаторе. Тут мне не понятно, я предлагаю хранить зависимости внутри виджета, что не мешает строить дерево зависимостей. Вы предлагаете снаружи, да еще и копировать заивисимости при каждом подключении компонента в новом шалоне?

                По поводу initWidgets([id1, id2, id3]), я выкинул это из статьи но упоминание такого подхода есть в видео. Это нормально и часто правильно, но это детали не меняющие подход.

                По поводу фейсбук и остальных. Конкретно с разработчиками фейсбук я не общался, пока, но часто общаюсь с разработчиками лидирующих Российских сайтов, например Яндекс, разработчиками Google, NodeJS и т.д. Уж поверьте мы внимательно друг друга слушаем. Потому что не слушать разработчиков которые отвечают за работоспособность сайта перед 20 000 000 уникальных пользователей в день странно. Более того я внимательно выслушал вас и ответил. Поэтому, на будущее, в разговоре со мной не указывайте на решения других приводите в пример свои решения с продакшена.
                • +1
                  Да, вы правы, с сарказмом я переборщил. Извиняюсь, что мой комментарий получился слишком язвительным и невежливым. Это, конечно, неправильно.

                  Тем не менее, подход, с очередью сообщений, мне не нравится, так как он выглядит как неаккуратное решение и провоцирует трудноповторяемые баги (потому, что у тестера или разработчика виджеты могут грузиться в другой последовательности и работать хорошо, а у пользователя — нет). Мне кажется, логичнее дождаться загрузки всех виджетов, а только потом их инициализировать.

                  Также, несмотря на все красивые слова, на главной mail.ru куча скриптов и стилей просто вставлена в тело страницы, хотя наверняка они для всех пользователей одинаковы.
                  • 0
                    Главная не трогалась очень давно. И в принципе главная это не типичный проект.
                    Мы ее оптимизируем, но вставка скиптов и стилей не всегда плохо. Часть скиптов и стилей у нас в коде для того, чтобы пользователь как можно быстрее увидел некоторые блоки. В ущерб остальным.

                    Плюс есть портальная навигация, выносить стили и скипты в файлы из нее может быть плохим решением. Дело в том, что на сервере проекты берут портальную навигацию как кусок html из одного места с системой доставки и кеширования, а раздают сами (я про сервера). В случае вставки ссылок на скрипт весь mail.ru и одноклассники будут брать этот скрипт из одного места, а это опасно. Если что-то случается с этим сервером, то миллионы пользователей увидят побитые страницы.
              • +1
                Не поясните ли насчет очереди длиной в 1?
                Насколько я понял, единичная длина очереди «прошедших» эвентов специфична именно для даты: наверное кроме события «change_date», редко что-то будет еще использоваться для оповещения отображения.

                Но, например, в случае с элементом какого-то списка опций, может быть необходимо хранить несколько (или все) прошедшие изменения (если изменения происходят по одной из опций)? Правда, состояния опций можно передавать (или модуль отображения может запрашивать) целиком.

                Может быть есть что рассказать про случаи с хранением очереди более 1?

                Также интересна примерная реализаций этой очереди. Я так понимаю, в каждом observe надо сопоставить object c объектами эвентов в очереди (можно проверять на идеинтичность, а можно и на вложенность). Далее сэмулировать выработку эвента? Тогда может получиться, что этот эвент опять получат еще раз те, кто уже слушал до этого. Т.е. надо эмульровать только для конкретного слушатель, как вариант просто вызвать callback этого нового observe().
                Спасибо.
                • 0
                  Лично я выбрал такие значения:
                  0 — нет очереди
                  >0 очередь указанной длинны
                  -1 в принципе хранить все что пришло.

                  Но на практике мне кроме 0 и 1 ничего не понадобилось.

                  Про доставку, в очередь надо смотреть только если появился новый слушатель и отдать ему все что в очереди.
                  Если слушатель уже висит то очередь бесполезна, он сразу ловит события как только они произошли.

                  Про реализацию. Как только в callback приходит observe или dispatch я проверяют что у объекта нет __UNIQID и дописываю его используя инкрементирующийся счетчик. Если есть, то использую имеющийся. Дальше у вас есть __UNIQID строкой, имя события строкой и данные. Из первых двух составляется ключ, вторыми наполняется массив, длина которого ограниченна длинной очереди. Ключ указывает на массив.
                  • 0
                    Спасибо
                    • +1
                      Добрый день, Андрей. Про очередь было не очень понятно. Суть в том, что объект ответственный за предачу эвентов хранит последние n эвентов и при появлении нового подписчика высыпает ему их, при этом сохраняя информацию в очереди? (мне почему-то сначала казалось что очередь очищается при передаче сообщения слушателям).

                      В чем преимущество такого подхода перед простой «приостановкой» отдачи событий слушателям до полной инициализации страницы. Пока страница не инициализирована события пишутся в буффер, после инициализации раздаются, и больше буффер не используется. Мне кажется так проще и наглядней, но вы очевидно решение с очередью не просто так выбрали. Расскажете?
                      • 0
                        Еще есть аякс и инициализация объектов по действиям пользователя. Поэтому нет такого события, по которому однозначно можно понять что все компоненты проинициализировались и больше ничего не появится.
                        • 0
                          Да, так явно намного гибче. Спасибо.
                  • 0
                    Автор не будет так любезен раскрыть тему нет не сисек реализации очереди событий? Премного благодарен.
                    • 0
                      Вроде все выяснили в личной переписке.
                      • 0
                        Подразумевал конкретную реализацию методов объекта. Т.к. Вы уже учли большинство нюансов — многие не строили бы свои велосипеды, а видели бы сразу как надо.
                        • 0
                          Моя реализация шести или семилетней давности github.com/AndrewSumin/jsx/blob/master/CallBacks.js
                    • +1
                      Моя реализация шести или семилетней давности github.com/AndrewSumin/jsx/blob/master/CallBacks.js
                      • 0
                        Спасибо+1
                        • +1
                          Вопросик про инициализацию компонентов. Подход при котором компоненты помечаются определённым классом, а данные храняться в onclick (или кому-то больше нравится data-*) понятен.
                          Какие недостатки у варианта 2: сразу после html кода компонента в вёрстке вставляется тег script с вызовом функции ядра (которая точно уже загружена) initComponent(htmlId, data)?
                          Может это устарело или имеет очевидные недостатки? Спрашиваю, т.к. хочу понять best practice на сегодняшний день. Если дадите ссылку, что почитать про современные паттерны программирования клиентской части на JavaScript — спасибо.
                          • 0
                            Подход актуален иначе бы я его не рассказывал.
                            Дальше все зависит от ваших задач, если большой сайт с десятками страниц то очень советую.
                            Если страниц мало, то только накладные расходы.

                            Что касается инструментов, я бы посоветовал какой-нибудь requirejs.org/

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое