Как в Sports.ru писали свой WYSIWYG-редактор

    В середине 2018 года в Sports.ru задумались о переезде на новый WYSIWYG-редактор текста для пользовательских постов. С июня 2019 года редактор работает в режиме бета-версии. За это время мы решили множество проблем, связанных как с проектированием архитектуры всего сервиса, так и с реализацией самого редактора в браузере на основе библиотеки ProseMirror, и решили поделиться своим опытом.



    Оглавление


    1. Введение
    1.1. Почему понадобился WYSIWYG
    1.2. Описание задачи, которая стояла перед разработчиками
    2. Как выбирали инструмент
    3. Что получилось
    3.1. Архитектура сервиса
    3.2. С какими сложностями столкнулись
    4. Результаты бета-тестирования


    1. Введение



    1.1. Почему понадобился WYSIWYG


    Sports.ru – медиа о спорте с аудиторией 20 млн пользователей в месяц. Наши главные отличия от классических СМИ – сообщество и UGC. Пользовательский контент – оценки, комментарии, чаты, посты – не только дополняет ценность редакционного, но создает платформу для взаимодействия пользователей друг с другом. Каждый месяц наши пользователи пишут почти 10 тысяч постов. Лучшие из них выносятся на главную страницу сайта вместе с редакционными, отправляются в мобильные приложения, социальные сети. На пользовательский контент приходится около 40% всех прочтений страниц Sports.ru.

    Мы хотим быть самой удобной платформой для авторов о спорте, помогать создавать контент и доставлять его заинтересованной аудитории. 10 лет мы использовали редактор TinyMCE – и в конце концов он устарел, перестал устраивать и команду, и пользователей, привыкших к современным редакторам.


    Рис. 1. Интерфейс старого редактора на основе TinyMCE

    От авторов блогов регулярно приходили примерно такие жалобы:

    • долго писал, потом случайно закрыл вкладку и все пропало;
    • писать длинные тексты очень неудобно;
    • раздражает, что для вставки каждой картинки ее надо сначала загрузить на хостинг изображений.

    У команды тоже были свои претензии:

    • в TinyMCE нельзя загружать картинки напрямую из файла, можно только прикреплять ссылки на изображения, а из-за того, что пользователи не имели возможности загружать картинки в наше хранилище, то если ссылки на них умирали, мы ничего не могли с этим сделать;
    • возможности для редактирования и форматирования текста не сбалансированы. С одной стороны, не хватало каких-то стилей, например, для внутренних заголовков в тексте. С другой – использовать имеющиеся инструменты можно было в каких угодно сочетаниях. В результате посты выглядели неединообразно (в Sports.ru началась работа по внедрению дизайн-системы и пользовательские посты должны выглядеть в соответствии с ней);
    • контент создается и хранится в HTML, поэтому сложно управлять стилями в постах на разных клиентах, да и просто вносить изменения в верстку постов.

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


    1.2. Описание задачи, которая стояла перед разработчиками


    Если коротко, то тезис, от которого мы изначально отталкивались: ведение блога на Sports.ru – это боль. В принципе, можно было бы не делать новый редактор, а просто добавить автосохранение и возможность загрузки картинок в собственное хранилище – и большая часть жалоб пользователей и сотрудников отпала бы. Но хотелось все же не поддерживать инструмент на старых технологиях, а создать новый современный редактор, который мы сможем легко развивать и масштабировать.

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

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

    • сама концепция WYSIWYG, т.е. what you see is what you get (англ. «что видишь, то и получишь», подробнее можно посмотреть в Википедии), когда пользователи в процессе создания поста буквально видят его точно таким, каким он будет после публикации. И не только самого поста, а также его превью в ленте;
    • вставка эмбедов (эмбедом мы называем мультимедийный элемент или виджет, не зависимый от остального контента на странице, с которым можно взаимодействовать так же, как на ресурсе, где он был создан; например, эмбед поста в инстаграме позволяет пролистать карусель фотографий или поставить лайк) постов из соцсетей и видеохостингов.

    При этом сам редактор не должен был быть завязан на особенности Sports.ru, потому что Sports.ru хоть и флагманский проект у нас в компании, но все же не единственный. В компании также разрабатываются международное спортивное медиа Tribuna, социальная сеть для любителей ставок Betting Insider, а недавно начала работу собственная продакшн-студия, занимающаяся рекламными проектами. Разработка онлайн-редактора – это достаточно дорогая штука, чтобы не захотеть переиспользовать этот код на другом сайте с другими версткой и стилями, со своим набором инструментов для редактирования и форматирования.

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

    Structured body – это массив объектов, отражающий структуру контента. При этом контент разбивается на элементы, представляющие собой независимые блоки, например, paragraph, list, picture. Элемент хранит информацию о том, какого он типа и какие у него свойства. Например, блок subtitle описывает заголовок в тексте, он обязательно должен содержать поля text и level. Соответственно, в text содержится текст этого заголовка, а в level — уровень (от 1 до 4). Structured body, состоящий из одного заголовка второго уровня, может выглядеть, например, так:

    const structuredBody = [
        {
            type: 'subtitle',
            value: {
                text: 'Мой новый заголовок второго уровня',
                level: 2,
            },
        },
    ];
    

    Переход на structured body позволил нам начать процесс разделения бизнес-логики, данных и их представления. В конечном итоге мы хотим, чтобы сервер и клиенты обменивались только данными. А как и для чего отобразить конечному пользователю эти данные, каждый клиент будет определять самостоятельно.

    Контент в формате structured body хранится в JSON и для валидации его содержимого мы создали JSON-схему, которую назвали structured body schema. В этой схеме описаны все допустимые элементы и их свойства. Таким образом мы можем быть уверены, что везде, где нужен structured body, используется один набор ключей и значений.

    Более того, это позволяет разным командам использовать одни и те же сервисы для обработки контента в этом формате. Например, сервис для генерации HTML из structured body для отображения контента или редактор для создания контента. Это заметно сокращает затраты на разработку и поддержку всего ядра сервисов, связанных с созданием и отображением контента.

    Предполагалось, что новый редактор должен принимать на вход и отдавать на выход контент исключительно в формате structured body. И тут надо было учесть тонкий момент: поскольку раньше посты сразу сохранялись в HTML, то для отображения на клиент передавалась эта HTML-строка из базы данных (здесь и далее под клиентом мы имеем в виду исключительно браузер, если не указано иное). Теперь мы хотим хранить контент всех постов в structured body, но клиенты умеют обрабатывать только HTML. Значит, вместе с задачей переезда на новый редактор одновременно идет задача реализации нового способа отображения клиентами постов для чтения напрямую из structured body. Мы решили, что слона лучше есть по частям, поэтому для начала надо полностью отказаться от TinyMCE, а уже потом браться за логику отображения постов для чтения. Более того, не для всех старых постов удалось перевести контент в новый формат, а значит эти посты всегда будут храниться только в HTML и необходимо для них тоже сохранить возможность чтения.

    Итого: часть постов (все новые и старые, которые были успешно переведены на новый формат) будет храниться в двух форматах – HTML и structured body – пока не будет реализована новая логика отображения для чтения, а остальные (большая часть старых и очень-очень старых постов) так и останутся только в HTML.


    2. Как выбирали инструмент


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

    Для начала мы изучили, какие есть уже готовые библиотеки для создания WYSIWYG-редакторов и подходят ли они нам. Мы остановились на Slate, Draft.js и ProseMirror.

    Кроме хранения контента в структуре данных, критичным моментом для нас также была возможность работы с Vue или чистым JS, потому что мы уже успели начать переводить сайт на новый технологический стек с использованием Vue+Vuex. Кроме того, хотелось бы при необходимости расширять возможности готовой библиотеки с помощью новых модулей (сторонних или самописных).

    Табл. 1. Сравнение рассмотренных библиотек по наиболее важным для Sports.ru параметрам


    Как видно из таблицы, ProseMirror вполне соответствовал нашим требованиям, поэтому идею написать свою библиотеку для редактирования текстового контента мы уже не рассматривали, а начали изучать эту библиотеку подробнее. Есть еще довольно популярный Quill, который не попал в наше сравнение только потому, что мы честно забыли про него на этапе выбора инструмента. По нашим ключевым требованиям он тоже проходит, но так уж сложилось. О том, что такое ProseMirror и как с ним работать, мы уже рассказали в другой статье.


    3. Что получилось



    3.1. Архитектура сервиса


    Сам редактор контента на клиенте — это еще далеко не все. Редактор нужно поместить в уже существующий проект, отобразить его где-то на веб-странице, а также продумать взаимодействие с бэкендом, решить проблему одновременной поддержки двух редакторов (от старого тоже нельзя было мгновенно отказаться) и хранения контента в двух форматах (HTML и structured body).

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

    Сервисы фронтенда для редактора можно разбить на несколько уровней:

    1. web-страница для создания и редактирования поста;
    2. Vue-app, которое запускается на клиенте. По сути, это форма, написанная на Vue, помимо самого контента поста она сабмитит еще множество мета-данных, например, название, аннотацию, привязку к разделу и подразделам и т.д., а также валидирует данные, выводит различные сообщения для пользователя и делает прочие вещи, которые делают все уважающие себя формы;
    3. WYSIWYG-редактор на основе ProseMirror, который инициализируется внутри формы на Vue. Это и есть наш редактор текста, работающий примерно так, как описано в статье;
    4. SB2HTML – сервис для рендера HTML из structured body, живущий на отдельном сервере. Как мы уже упоминали, structured body – это наше внутреннее соглашение о формате данных, в котором хранится контент. Необходимость такого сервиса родилась из идеи, что между сервером, имеющим доступ к базе данных, и клиентом нужно передавать только данные. Но пока для Sports.ru клиенты не умеют генерировать HTML из structured body, придется по-прежнему хранить в базе актуальный HTML для отображения. Именно для этого мы реализовали генерацию HTML на сервере на Node.Js, чтобы в будущем можно было переиспользовать этот JS-код для отображения постов.

    Процесс сохранения поста изображен на рис. 2. На бэкенд передается контент самого поста в формате structured body и его мета-данные. Бэкенд отправляет контент в сервис SB2HTML, получает в ответе готовый HTML, кладет все это в базу и отвечает клиенту, что пост успешно сохранен, или сообщает об ошибке.


    Рис. 2. Схема сохранения поста при создании или редактировании в WYSIWYG-редакторе


    3.2. С какими сложностями столкнулись


    Сложностей было много, возникали они постоянно и часто в самые неожиданные моменты.

    Как мы уже говорили, редактор контента находится внутри формы, которая позволяет вводить дополнительные данные, необходимые для создания поста, такие как название, аннотация и т.д. Для аннотации должна быть возможность загрузки изображений из файла и по ссылке из интернета. Но ведь для контента мы тоже хотим загружать изображения из файла и по ссылке, причем по одним и тем же правилам. И тут мы оказались перед дилеммой: с одной стороны, контент поста при редактировании изолирован от внешней формы и обслуживается средствами ProseMirror, но с другой, хочется соблюсти принцип DRY и не дублировать один и тот же код. Решили это следующим образом: описали загрузку изображений как набор методов в объекте на уровне Vue-формы и передали этот объект как один из параметров в конструктор WYSIWYG-редактора.

    В модели ProseMirror определены сущности, описывающие контент — Node и Fragment. Однако для транзакции используются исключительно индексы для определения диапазона символов, к которым эта транзакция применяется (отсчет индексов ведется как от начала документа, так и от начала родительской ноды). Индексирование символов — это одна из центральных концепций ProseMirror, но при редактировании и форматировании текста гораздо удобнее думать о сущностях из модели ProseMirror. В итоге для комфортной работы с контентом мы написали свои хелперы, упрощающие взаимодействие с документом для транзакций.  Уже после начала нашей работы появилась библиотека tiptap, которая представляет собой набор похожих хелперов.

    Следующая проблема заключалась в том, что на этапе создания схемы мы поняли, что у нас уже есть утвержденный внутренний формат хранения контента – structured body, отвечающий нашим потребностям, а ProseMirror хранит в стейте контент в своем собственном формате. Переходить на формат ProseMirror было сложно и нецелесообразно. Мы оказались в ситуации, когда на клиент по API приходят данные в одном формате, а для их отображения нужен другой. Аналогичная ситуация у нас возникает при необходимости сохранить измененный или созданный контент. Для этого мы реализовали конвертер, который преобразует форматы туда и обратно. Написали для него несложный тест, который берет контент одного поста в формате structured body, переводит его в формат ProseMirror, потом обратно и уже сравнивает исходный вариант с полученным. Получилось быстро и просто.

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

    Следующая проблема снова связана с необходимостью обратной совместимости старых и новых технологий. Наш WYSIWYG-редактор реализован только в браузерах (десктопных и, в скором времени, мобильных). Соответственно, для редактирования контент на клиент отдается в JSON в формате structured body, однако чтение постов в браузерах осуществляется только из HTML. При этом большая часть мобильных приложений уже перешла на отображение пользовательских постов непосредственно из structured body.

    Для мобильных приложений необходимо было предусмотреть случай, когда клиент не может обработать какой-то элемент из structured body. Например, если в structured body добавлен новый элемент, отображение которого реализовано только в более новой версии приложения. Так как не все пользователи одновременно обновляют свои приложения, то необходимо было предусмотреть план «Б» для более старых версий: вместо создания HTML из structured body для нужного элемента вставлять готовый фрагмент HTML. Наличие фрагментов HTML для каждого элемента не было предусмотрено в схеме structured body, потому что сама идея этой структуры заключалась в том, чтобы отказаться от хранения данных в HTML. Но в итоге мы пришли к выводу, что нам нужно две схемы structured body — одна для отображения и одна для редактирования. Различия между схемами заключаются в том, что structured body для редактирования содержит исключительно контент статьи, а для отображения мы добавляем некоторые дополнительные элементы. В частности, фрагмент HTML для каждого элемента создается при сохранении поста в сервисе SB2HTML и добавляется только в structured body для отображения поста. Кроме того, в structured body для отображения также размечаются места для рекламы в контенте.

    При открытии контента для редактирования в браузере мы пока в принципе не можем столкнуться с неизвестным элементом, потому что все посты создаются и отображаются по одной схеме. Но решили на будущее предусмотреть такой случай тоже. Для этого мы добавили в схему ProseMirror дефолтный элемент-заглушку. Мы назвали этот элемент unsupportedBlock. Заглушка отображается на месте неподдерживаемого элемента. Мы стилизовали ее под серый прямоугольник с текстом о том, что этот элемент не поддерживается и его нельзя редактировать. При сохранении поста такой элемент в неизменном виде остается в structured body. Пользователь может поменять его расположение относительно других элементов, но внутреннее содержание неизвестного элемента изменить или отредактировать нельзя. Однако пользователь может удалить такой элемент, тогда, конечно, в итоговом документе он не сохранится.

    Все описанные проблемы касались сложностей реализации самого WYSIWYG-редактора. Но пока он существовал в бета-режиме, мы не могли отказаться от старого редактора на TinyMCE и вынуждены были поддерживать оба редактора, обеспечивая между ними обратную совместимость. Например, можно было создать пост в WYSIWYG-редакторе, сохранить, потом отредактировать его в TinyMCE, сохранить, снова открыть в WYSIWYG, и так до бесконечности. В итоге при открытии в WYSIWYG мы видели тот же контент, что и при предыдущем сохранении в TinyMCE. Для реализации обратной совместимости необходимо было отдавать в TinyMCE контент в HTML, который мы уже научились создавать из structured body и сохранять в базу данных при сохранении поста. А при сохранении поста через TinyMCE созданный контент на сервере прогоняется через сервис HTML2SB, в результате чего мы можем сохранить как свежий HTML, так и structured body.

    HTML2SB – это сервис, обратный тому, что делает SB2HTML, то есть преобразует контент из HTML в structured body. Хронологически этот сервис появился раньше всего, потому что до создания WYSIWYG-редактора единственной возможностью получить контент поста в формате structured body был прямой парсинг из HTML. HTML2SB являлся частью бэкендной инфраструктуры вокруг редактора постов, но после отказа от TinyMCE необходимость в нем отпала.


    4. Результаты бета-тестирования


    Сейчас WYSIWYG-редактор доступен для всех пользователей в бета-версии, а скоро станет основным редактором постов Sports.ru. Мы уже получили инструмент для создания и редактирования постов, который отвечает большинству наших требований:

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

    Безусловно, редактор еще пока не до конца отлажен, мы периодически обнаруживаем новые баги. На подходе следующие обновления:

    • автосохранение;
    • версия WYSIWYG для пользователей с расширенными правами (администраторы, штатные редакторы);
    • создание и редактирование постов из мобильных браузеров;
    • сообщения о параллельном редактировании документа несколькими пользователями;
    • подсказки и онбординг;
    • статистические виджеты спортивных команд, матчей и расстановок.

    На момент написания этой статьи через бета-версию редактора было опубликовано уже более 13000 постов, это около 20% от общего числа пользовательских текстов на Sports.ru за период с июня 2019-го по февраль 2020-го включительно. Доля как созданных, так и опубликованных через новый редактор постов, стабильно растет.


    Рис. 3. Доля пользовательских постов, созданных и опубликованных в новом редакторе

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


    Рис. 4. Комментарии пользователей в посте с анонсом обновления WYSIWYG-редактора 
    Sports.ru | Tribuna Digital
    Компания
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 41

      0

      Разработчики очень любят создавать проблемы и их решать. А больше всего нам нравится писать велосипеды.

        +1
        Вы забыли про костыли.
        Как здорово, что не разработчики являются бизнес-оунерами, вот мы бы наворотили иначе.
          +5
          Можете, пожалуйста, пояснить, что вы имеете в виду под велосипедом? Это была большая задача по переезду на новый стек, в проектировании решения которой было задействовано много людей от продакт-менеджеров и дизайнеров до девопс-инженеров, суть которой мы постарались отразить в разделе «1.2. Описание задачи, которая стояла перед разработчиками». Смысл был в том, что нам не нужен был просто какой-нибудь онлайн-редактор. Нам нужен был редактор, который:
          • соответствует нашей дизайн-системе,
          • удовлетворяет потребностям наших пользователей,
          • интегрируется в действующий проект,
          • может быть переиспользован на других проектах в компании,
          • весит как можно меньше.


          Разработчики из The New York Times в своей статье про то, как они разрабатывали свой WYSIWYG-редактор, где описывается похожий кейс, прямо говорят, что было не очень-то просто.
          Text editors are much more complex than many know.
            –4
            Text editors are much more complex than many know.

            Написание сложного кода с нуля это и есть велосипедостроение:
            • Сложные пользовательские сценарии ведут к багам
            • Большое количество элементов разметки и их сочетаний
            • Юникод и эмодзи, лигеатуры
            • Еще миллион пунктов

            Однако, если целью было опредено занять программеров, то тогда имело смысл изобретать)
              +1
              Кажется, не очень поняла последний коммент. Сложный код ведет к багам, да. Если можно не разрабатываться что-то сложное с нуля, то лучше этого не делать, конечно. Но бывают также случаи, когда готовые решения не подходят и приходится самим писать что-то большое и сложное.
          +1
          «в TinyMCE нельзя загружать картинки напрямую из файла» — для этого есть несколько плагинов разных разработчиков. Мы писали свой, и это несколько проще, чем менять редактор.
            +3
            Дело вообще не в Tiny, дело в формате данных. Главная задача была «не хранить HTML». Нужна была довольно плоская структура, удобная для всех клиентов и бэка в виде JSON.
              +1

              Но, насколько можно понять, пришлось сделать конвертер HTML > SB и SB > HTML. Если так, то можно ехать дальше на старом добром TinyMCE. Верно?

                +2
                Конвертеры нужны, потому что невозможно было мгновенно отказаться от старого редактора и хранения постов в HTML. Сейчас это скорее временное решение, пока мы находимся на этом переходном этапе. В будущем, когда мы полностью перейдем на новый онлайн-редактор, а также все клиенты смогут рендерить HTML из structured body, конвертеры станут не нужны.
                  +1

                  Tiny в таком случае не гарантирует консистентности. У нас точно будут потери на этапе html>sb, а значит это уже не wysiwyg. Структура, которую он выдает, не очень похожа на плоское представление информации. Плюс есть достаточное количество абсолютно лишних тэгов, которые он вставляет, особенно при копипасте из гугл-доков. Ну и к этому всему в tiny нельзя поставить каких-то жестких рамок ни по структуре данных, ни по представлению определенных типов.
                  Например: картинка с подписью. В tiny мы либо пишем плагин который будет работать с картинками именно так, как нам нужно(и я не уверен, что получится в полной мере), либо редактор (блогер) будет каждый раз сам придумывать, как ему сделать эту подпись(в каждом материале будет не по дизайну).

                    +1

                    Смотрели на https://trix-editor.org/?
                    Понял, но всегда есть стоимость решения. Иногда готовое, но слегка допиленное лучше, чем сделать большое и постоянно инвестировать в допиливание. В вашем случае наверное это было целесообразно.

                      +1

                      Нет. Мы рассматривали только описанные выше 3, из-за хранения в json. Конвертация html>sb довольно непростой процесс, жить с которым постоянно было бы больно.
                      В целом, мы и взяли готовый фреймворк для визивига. Без proseMirror разработка бы не была обоснована совсем. Я уверен, что с trix мы бы решали несколько больше проблем, чем с proseMirror.

                +4
                Все именно так, как написал buyanzashitkarman.
                В принципе, можно было бы не делать новый редактор, а просто добавить автосохранение и возможность загрузки картинок в собственное хранилище – и большая часть жалоб пользователей и сотрудников отпала бы. Но хотелось все же не поддерживать инструмент на старых технологиях, а создать новый современный редактор, который мы сможем легко развивать и масштабировать.

                То есть, мы понимали, что можно было остаться с TinyMCE, но хотелось именно отказаться от его главного недостатка — хранение контента в HTML.
                +2

                Городская легенда гласит, что когда-то и на Хабре появится визивиг редактор. Но легенда на то и легенда...

                  +1
                  Если я правильно понял, вы придумали свой велосипед в виде формата structured body, были ли причины не взять mobiledoc?
                  github.com/bustle/mobiledoc-kit/blob/master/MOBILEDOC.md
                  github.com/alidlo/vue-mobiledoc-editor

                  Например, редактор в ghost.org использует mobiledoc формат.
                    +1

                    Мы, разумеется, сначала накидали примерную схему sb, которая отвечала бы нашим требованиям, и искали что-то подобное, может недостаточно тщательно.
                    Обсудить и реализовать формат было довольно быстро. Основная работа с форматом была в специфике проекта. Различные эмбеды, обязательные поля, требования к данным разных клиентов. Это было бы в одном объеме сделано вне зависимости от стартового формата, так что ничего страшного не произошло.
                    И это немного не про визивиг. Когда мы пришли к непосредственной разработке его у нас не стоял выбор формата уже.

                    0
                    Чумной доктор? #pareidolia

                    image
                      +3

                      Уже больше года практически везде использую editor.js от ребят из vc.ru и иже с ними, всем доволен, плюс сделать связку с react, vue или ещё с чем достаточно просто.

                        0
                        Спасибо! Выглядит действительно как инструмент, который мог бы нам подойти сейчас. Когда мы выбирали библиотеку 1.5 года назад, мы про него не подумали. Возможно, он был не такой популярный, как остальные. Кроме того, опыт The New York Times и The Guardian, остановившихся на ProseMirror подкупал. Мы ни в коем случае не настаиваем на том, что наше решение лучшее из имеющихся. Как раз поделились своим опытом, чтобы кто-то не наступил на какие-то наши грабли.
                        0
                        Вот почитаешь такие посты и думаешь «не фига себе как всё круто! какие там профессионалы работают». А потом пытаешься пользоваться этим порталом и понимаешь, что… ну неюзабельно это!
                        В полях комментариев экранируется символ перевода строки.
                        Страницы с большим количеством комментариев начинают дико тупить при попытке прогрузить эти комментарии (причём чем дальше мотаешь, тем сильнее тупит — то ли там какие-то манипуляции со всем DOMом, то ли ещё что).
                        Вообще, бесконечный скролл для ленты комментариев — офигенное дизайнерское решение! Особенно когда этих комментариев за тысячу.
                        А «тупящий» аякс, который уведомление о новых ответах показывает почему-то спустя некоторое врем после загрузки страницы.
                        А как вы загородили рекламой навигационное меню?
                        Вообще у ресурса такого уровня, на продакшене, в консоли должно быть девственно чисто:
                        image
                        Зато про неработающий(!) «AntiAdBlock» не забыли))

                        sports.ru — это крайне сырой, некачественный продукт с точки зрения фронтенда.
                          +2
                          Благодарю за разбор нашего сайта. Действительно, у нас далеко не все идеально, проект старый, с кучей легаси. Мы о своих проблемах знаем и работаем над ними. В том числе описанный в статье кейс является частью работ по приведению всего в порядок. Если вы знаете, как справиться лучше и быстрее, то у нас как раз открыто несколько вакансий.
                            +1

                            На скрине только ошибки от Вашего эдблока Ладно бы Вы показали что-то действительно полезное. Могли бы и повнимательнее посмотреть на ошибки.

                              –2
                              А я ничего и не выбирал. Скриншот как есть. Понятно что ERR_BLOCKED_BY_CLIENT — это адблок. Но парсер json-то почему ошибки выбрасывает?

                              Вот с отключенными блокировщиками:
                              image

                              Ну и заодно — консольные сообщения для дева, перед продакшеном-то стоит всё-таки вычищать. А то что это за хвосты «не выполнены условия показа попапа»?
                                0

                                Почему Вы решаете за нас, какие сообщения для дева, а какие нет? Разве мы не можем пользоваться этими сообщениями на проде? Например кто-то видит поведение, которое считает неправильным, мы просим показать консоль, а потом объясняем, что так и должно быть и почему или фиксим.

                                  0

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

                              0

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

                                0
                                Блокировщики рекламы — это сторонние приложения, которые переопределяют стили, блокируют запросы и всячески хозяйничают на странице. При этом их поведение может зависеть от того, какие у вас настройки выбраны, какие белые и черные списки подключены и т.д. На данный момент бизнес-модель Sports.ru такова, что мы получаем деньги от рекламы, поэтому можем гарантировать корректную работу сайта только с отключенными блокировщиками. Мы не игнорируем совсем подобные проблемы, но как правило приоритет у них довольно низкий.
                              0

                              За tinyMCE обидно было :) Список претензий команды к нему не оч актуальный. tinyMCE позволяет сделать собственную обработку вставки картинки и грузить её хоть с диска сразу в хранилище, хоть с выбором готовой из хранилища — как угодно. Список стилей там тоже конфигурируется. Из коробки можно добавить свой стиль для внутренних загов.
                              Но для современного визивига лабания статей и логридов, когда нужна тильда, он конечно не подойдет. Мы тоже с него соскочили.

                                0
                                Важнее всего было уйти от сохранения контента в HTML. Тут tiny уже ничего не мог предложить, к сожалению.
                                +1

                                Решали подобную задачу с помощью editor.js. Странно, что его нет в списке, все что вам было нужно, там из коробки + 0 зависимостей + отечественные разрабы.

                                  0
                                  К сожалению, мы на него наткнулись около года назад, когда у нас уже был готов прототип и мы готовились к запуску своей беты. Тут важно понимать, что выбор инструмента и проектирование происходили где-то в середине 2018 года. Мы рассмотрели те библиотеки, которые были популярны в момент исследования. С конца 2018 года уже начался активный этап разработки.
                                  +1
                                  в проекте заюзали вот такое решение github.com/scrumpy/tiptap. Пока полет нормальный
                                    0
                                    Да, стабильный релиз этой библиотеки вышел уже, когда мы уже готовили бету. А так бы с удовольствием сами использовали
                                    +1
                                    Спасибо за ваш опыт.

                                    Мне интересно, вы видите сейчас смысл в своей созданной схеме «structured body», когда у ProseMirror есть своя схема?

                                    У вас же вроде лишние конвертации между этими схемами идут и ваша схема очень похожа на ту что в ProseMirror если судить по примеру.
                                      0
                                      Если бы у нас был один небольшой проект, то наверное формата ProseMirror нам бы и правда хватило (на одном небольшом промо-проекте, где нужен был редактор текста, так и сделали). Проблема тут в том, что формат ProseMirror не очень удобен для хранения данных и последующей работы с ними, все же задумывался он для другого.
                                      И кроме того, когда мы начинали работу над редактором, наш формат structured body, который нам подходил и по поводу которого мы были уверены, что мы с ним надолго, уже был утвержден. buyanzashitkarman уже писал про это в комментарии чуть выше
                                      0

                                      Tui.editor работает с markdown разметка и классический html одновремено.


                                      Конвертит из буфера автоматом картинки в base64
                                      Загрузку на сервер тоже есть


                                      На сервере храню в markdown меньше объем.

                                        0

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

                                          +1
                                          Мы в Atlassian тоже используем ProseMirror как основу для редакторов в BitBucket, Confluence, Jira, etc…  Пример можно посмотреть на нашем сайте дизайн системы: atlaskit.atlassian.com/examples/editor/editor-core/kitchen-sink

                                          Тоже переходили с TinyMCE так как менеджить его очень тяжело, HTML не самый удобный формат для хранения данных.

                                          Вообще ваш опыт очень похож на то что мы делаем. Мы также пытались решить forwards compatibility с использование unsupportedBlock который, по описанию, выглядит один в один как ваш :) Но мы также используем подмножество ProseMirror JSON как дата формат. Интересно послушать почему по Вашему мнению он не удобен для хранения данных? И у нас также есть json schema для валидации документов.

                                          Одна из самых больших проблем для нас – это переиспользование реакт компонентов из дизайн системы внутри контента редактора. Как я понимаю вы делаете что-то похоже но с Vue, или у вас только plain ProseMirror?
                                            +1

                                            Спасибо за комментарий. Я не совсем понял про использование реакт компонентов в контенте. Вы про использование их в момент использования редактора, или про саму контентную страницу? С контентными страницами кажется всё довольно простым: мы итерируем элементы в массиве данных и вольны выбирать как их отрисовывать, как компонент или как строку. В самом редакторе мы Vue компоненты ещё пока не использовали, но, скорее всего, будем это делать как и эмбеды. У proseMirror в схеме написано, что нужно вставить вот такой с такими дата-аттрибутами и потом вызвать вот эту функцию. В функции можно выбросить событие или воспользоваться заранее прокинутым API с react. Так как proseMirror уже не сильно важно, что происходит внутри atom ноды, там может происходить что угодно. Но это простой вариант, в котором односторонняя связь proseMirror->что-нибудь. Если из компонентов нужно иметь воздействие на контент то уже не подойдёт. Для двусторонней придется попотеть с плагином, его я уже так быстро не опишу, но и супер-сложного там нет.
                                            Почему формат proseMirror не удобен? На это я не смогу ответить. Основная проблема, что формат хранения был у нас уже до выбора решения для визивига. Предполагалось, что мы возьмём визивиг, который будет в нашем формате данные брать:) Оказалось, что таких нет, а клиенты уже пользуются нашим форматом.

                                              +1

                                              У proseMirror в схеме написано, что нужно вставить вот такой <div> с такими дата-аттрибутами и потом вызвать вот эту функцию

                                              +1
                                              Про удобство могу ответить я. Это мое личное мнение, которое я плохо выразила. Схема structured body предполагает меньше уровней вложенности и глядя на контент в этом формате можно просто быстрее понять структуру DOM-дерева, которое будет построено. Полагаю, что у автора ProseMirror были причины делать, например, марки в виде отдельного массива объектов со своими типами и атрибутами, а также включать ссылку в список марок. Это все детали, конечно. Безусловно, у нас не стоял выбор между форматом ProseMirror и structured body, потому что последний уже был принят. Но лично мне разбираться в схеме формата ProseMirror было немного сложнее.

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

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