Como Sports.ru escreveu seu editor WYSIWYG

Em meados de 2018, o Sports.ru pensou em mudar para um novo editor de texto WYSIWYG para postagens de usuários. Desde junho de 2019, o editor está no modo beta. Durante esse período, resolvemos muitos problemas associados ao design da arquitetura de todo o serviço e à implementação do próprio editor no navegador baseado na biblioteca ProseMirror , e decidimos compartilhar nossa experiência.



Índice


1. Introdução
1.1. Por que você precisou do WYSIWYG
1.2. Descrição da tarefa que os desenvolvedores enfrentaram
2. Como escolher a ferramenta
3. O que aconteceu
3.1. Arquitetura de serviço
3.2. Quais são os desafios
4. Resultados dos testes beta


1. Introdução



1.1 Por que você precisava do WYSIWYG


Sports.ru é uma mídia sobre esportes com uma audiência de 20 milhões de usuários por mês. Nossas principais diferenças em relação à mídia clássica são a comunidade e o UGC . O conteúdo do usuário - classificações, comentários, chats, postagens - não apenas complementa o valor editorial, mas também cria uma plataforma para que os usuários interajam. Todo mês, nossos usuários escrevem quase 10 mil posts. Os melhores são enviados para a página principal do site, juntamente com os editoriais, enviados para aplicativos móveis, redes sociais. O conteúdo do usuário é responsável por cerca de 40% de todas as leituras nas páginas do Sports.ru.

Queremos ser a plataforma mais conveniente para autores de esportes, para ajudar a criar conteúdo e entregá-lo a um público interessado. 10 anos usamos o editor TinyMCE- e, no final, ficou desatualizado, deixou de atender tanto à equipe quanto aos usuários que estavam acostumados aos editores modernos.


FIG. 1. A interface do antigo editor baseado no TinyMCE

Dos autores dos blogs recebidos regularmente sobre as seguintes reclamações:

  • Escrevi por um longo tempo, fechei acidentalmente a guia e tudo se foi;
  • escrever textos longos é muito inconveniente;
  • É irritante que, para inserir cada imagem, você deve primeiro enviá-la para a hospedagem de imagens.

A equipe também teve suas próprias reclamações:

  • no TinyMCE, você não pode fazer upload de imagens diretamente de um arquivo, você só pode anexar links a imagens e, devido ao fato de os usuários não conseguirem fazer upload de imagens em nosso armazenamento, se os links morrerem, não poderíamos fazer nada a respeito;
  • as possibilidades de editar e formatar o texto não são balanceadas. Por um lado, não havia estilos suficientes, por exemplo, para cabeçalhos internos no texto. Por outro lado, foi possível usar as ferramentas disponíveis em qualquer combinação. Como resultado, as postagens não pareciam uniformes (no Sports.ru, começaram os trabalhos de implementação do sistema de design e as postagens dos usuários devem estar de acordo com ele);
  • o conteúdo é criado e armazenado em HTML, por isso é difícil gerenciar estilos em postagens em diferentes clientes e apenas fazer alterações no layout das postagens.

A seguir, é apresentada uma história sobre como resolvemos o problema de criar um novo editor no front-end. É verdade que também haverá algo sobre o produto, o design e as partes de back-end, porque sem isso será difícil entender por que uma decisão específica foi tomada no front-end.


1.2 Descrição da tarefa enfrentada pelos desenvolvedores


Em resumo, a tese da qual nos baseamos inicialmente: blogar no Sports.ru é uma dor. Em princípio, seria possível não criar um novo editor, mas simplesmente adicionar salvamento automático e a capacidade de fazer upload de fotos para seu próprio armazenamento - e a maioria das reclamações de usuários e funcionários desapareceria. Mas eu ainda queria não oferecer suporte à ferramenta em tecnologias antigas, mas criar um novo editor moderno que possamos facilmente desenvolver e dimensionar.

Além da interface inconveniente, um dos principais problemas técnicos do editor antigo era que o conteúdo da postagem era salvo imediatamente como uma string HTML, e as alterações na aparência da publicação exigiam a intervenção de desenvolvedores de back-end ou eram implementadas em tempo de execução no cliente (por exemplo, a colocação de blocos de anúncios no corpo do post). Nossa tarefa, entre outras coisas, era separar os dados de sua apresentação e, consequentemente, deixar o layout e a interface no código do cliente e trabalhar com os dados no código do servidor.

Como modelo, adotamos o Medium , às vezes espionando idéias do Google Doc . Além de resolver os problemas já identificados, decidimos adicionar vários novos recursos que tornariam o uso do editor mais confortável:

  • WYSIWYG, .. what you see is what you get (. « , », ), , . , ;
  • ( , , , , ; , ) .

Ao mesmo tempo, o próprio editor não deveria estar vinculado aos recursos do Sports.ru, porque o Sports.ru, embora o projeto principal em nossa empresa, ainda não seja o único. A empresa também está desenvolvendo a mídia esportiva internacional Tribuna , uma rede social para entusiastas de apostas Betting Insider , e lançou recentemente seu próprio estúdio de produção envolvido em projetos de publicidade. Desenvolver um editor on-line é caro o suficiente para não querer reutilizar esse código em outro site com diferentes tipos de letra e estilos, com seu próprio conjunto de ferramentas para edição e formatação.

Temos muito conteúdo de texto e, antes de começar a trabalhar na criação de um novo editor de postagem, pensamos em como esse conteúdo deveria ser armazenado. O TinyMCE não nos deu uma escolha e o conteúdo teve que ser armazenado apenas em HTML, que, como mencionado acima, não se adequava à equipe. Como resultado, criamos nosso próprio formato para armazenar dados de texto que atendem aos nossos requisitos e chamamos de corpo estruturado.

Corpo estruturado é uma matriz de objetos que reflete a estrutura do conteúdo. Nesse caso, o conteúdo é dividido em elementos que são blocos independentes, por exemplo, parágrafo, lista, imagem. Um elemento armazena informações sobre que tipo e quais propriedades ele possui. Por exemplo, o bloco de legendas descreve o título no texto, deve conter os campos de texto e nível. Assim, o texto contém o texto deste cabeçalho e o nível contém o nível (de 1 a 4). Um corpo estruturado, consistindo em um cabeçalho de segundo nível, pode ter, por exemplo, o seguinte:

const structuredBody = [
    {
        type: 'subtitle',
        value: {
            text: '    ',
            level: 2,
        },
    },
];

A transição para o corpo estruturado nos permitiu iniciar o processo de separação da lógica de negócios, dados e sua apresentação. Por fim, queremos que o servidor e os clientes troquem apenas dados. E como e por que exibir esses dados para o usuário final, cada cliente determinará independentemente.

O conteúdo no formato formatd body é armazenado em JSON e, para validar seu conteúdo, criamos um esquema JSON chamado esquema de corpo estruturado. Este diagrama descreve todos os elementos válidos e suas propriedades. Assim, podemos ter certeza de que sempre que um corpo estruturado for necessário, um conjunto de chaves e valores será usado.

Além disso, ele permite que equipes diferentes usem os mesmos serviços para processar o conteúdo nesse formato. Por exemplo, um serviço para gerar HTML a partir de um corpo estruturado para exibir conteúdo ou um editor para criar conteúdo. Isso reduz significativamente o custo de desenvolvimento e suporte de todo o núcleo de serviços relacionados à criação e exibição de conteúdo.

Supunha-se que o novo editor deveria aceitar conteúdo de entrada e saída exclusivamente no formato de corpo estruturado. E aqui era necessário levar em conta o ponto sutil: como anteriormente as postagens foram salvas imediatamente em HTML, essa string HTML do banco de dados foi transmitida ao cliente para exibição (a seguir, por cliente, queremos dizer apenas o navegador, a menos que especificado de outra forma). Agora queremos armazenar o conteúdo de todas as postagens no corpo estruturado, mas os clientes podem processar apenas HTML. Portanto, juntamente com a tarefa de mudar para um novo editor, a tarefa de implementar uma nova maneira de os clientes exibirem postagens para leitura diretamente do corpo estruturado está acontecendo simultaneamente. Decidimos que é melhor comer um elefante aos poucos, então primeiro você precisa abandonar completamente o TinyMCE e só então assumir a lógica de exibir postagens para leitura. Além disso,nem todas as postagens antigas conseguiram traduzir o conteúdo para um novo formato, o que significa que essas postagens sempre serão armazenadas apenas em HTML e é necessário que elas também mantenham a capacidade de ler.

Total: parte das postagens (todas as novas e antigas que foram transferidas com sucesso para o novo formato) serão armazenadas em dois formatos - HTML e corpo estruturado - até que a nova lógica de exibição da leitura seja implementada e o restante (a maioria das antigas e muito muito antigas) posts) permanecerá apenas em HTML.


2. Como escolher uma ferramenta


Tivemos que perceber a capacidade de editar e criar uma postagem no cliente, levando em consideração os recursos e limitações acima. Como sempre, você pode escolher uma solução pronta ou criar a sua própria.

Para começar, examinamos o que são as bibliotecas prontas para a criação de editores WYSIWYG e se elas são adequadas para nós. Optamos por Slate , Draft.js e ProseMirror .

Além de armazenar conteúdo em uma estrutura de dados, o momento crítico para nós também foi a capacidade de trabalhar com o Vue ou JS puro, porque já começamos a mover o site para uma nova pilha tecnológica usando o Vue + Vuex. Além disso, eu gostaria de expandir os recursos da biblioteca concluída com a ajuda de novos módulos (de terceiros ou auto-gravados), se necessário.

Aba. 1. Comparação das bibliotecas revisadas pelos parâmetros mais importantes do Sports.ru


Como você pode ver na tabela, o ProseMirror atendeu totalmente aos nossos requisitos, por isso não consideramos mais a idéia de escrever nossa própria biblioteca para editar o conteúdo do texto, mas começamos a estudá-la com mais detalhes. Ainda existe um Quill bastante popular , que não entrou em nossa comparação apenas porque honestamente o esquecemos na fase de seleção de uma ferramenta. De acordo com nossos principais requisitos, ele também passa, mas aconteceu. Já falamos sobre o que é o ProseMirror e como trabalhar com ele em outro artigo .


3. O que aconteceu



3.1 Arquitetura de Serviço


O próprio editor de conteúdo no cliente está longe de tudo. Você precisa colocar o editor em um projeto existente, exibi-lo em algum lugar da página da web e também considerar a interação com o back-end, resolver o problema de oferecer suporte simultâneo a dois editores (não era possível abandonar imediatamente o antigo) e armazenar o conteúdo em dois formatos (HTML e estruturado). corpo).

Todas essas tarefas podem ser divididas nas relacionadas ao frontend, backend e sua integração. Estamos preocupados principalmente com problemas de front-end e integração, embora também mencionemos alguns aspectos importantes das tarefas de back-end.

Os serviços de front-end para o editor podem ser divididos em vários níveis:

  1. página da web para criar e editar uma postagem;
  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- .

O processo de salvar a postagem é mostrado na Fig. 2. O conteúdo da postagem no formato do corpo estruturado e seus metadados são transferidos para o back-end. O back-end envia conteúdo para o serviço SB2HTML, recebe HTML pronto na resposta, coloca tudo isso no banco de dados e informa ao cliente que a postagem foi salva com sucesso ou relata um erro.


FIG. 2. Esquema para salvar uma postagem ao criar ou editar em um editor WYSIWYG


3.2 Que dificuldades você enfrentou?


Houve muitas dificuldades, surgiram constantemente e frequentemente nos momentos mais inesperados.

Como já dissemos, o editor de conteúdo está localizado dentro do formulário, o que permite inserir dados adicionais necessários para criar uma postagem, como título, anotação etc. Para anotação, deve ser possível baixar imagens de um arquivo e através de um link da Internet. Mas para o conteúdo também queremos carregar imagens de um arquivo e por referência, além disso, de acordo com as mesmas regras. E aqui estamos diante de um dilema: por um lado, o conteúdo da postagem é editado a partir do formulário externo e editado usando as ferramentas ProseMirror, mas, por outro lado, quero observar o princípio DRYe não duplique o mesmo código. Resolvemos isso da seguinte maneira: descrevemos o carregamento de imagens como um conjunto de métodos em um objeto no nível do formulário Vue e passamos esse objeto como um dos parâmetros para o construtor do editor WYSIWYG.

As entidades que descrevem o conteúdo - Nó e Fragmento - são definidas no modelo ProseMirror. No entanto, apenas índices são usados ​​para uma transação para determinar o intervalo de caracteres aos quais essa transação é aplicada (os índices são contados desde o início do documento e desde o início do nó pai). A indexação de caracteres é um dos conceitos centrais do ProseMirror, mas ao editar e formatar o texto, é muito mais conveniente pensar em entidades do modelo ProseMirror. Como resultado, para um trabalho confortável com o conteúdo, escrevemos nossos auxiliares para simplificar a interação com um documento para transações. Após o início do nosso trabalho, apareceu a biblioteca de dicas , que é um conjunto de ajudantes semelhantes.

O próximo problema foi que, na fase de criação do esquema, percebemos que já possuímos um formato interno aprovado para armazenar conteúdo - um corpo estruturado que atende às nossas necessidades, e o ProseMirror armazena o conteúdo em seu próprio formato em uma história. Mudar para o formato ProseMirror foi difícil e impraticável. Nos encontramos em uma situação em que dados em um formato chegam ao cliente por meio da API e outro precisa ser exibido. Uma situação semelhante surge quando é necessário salvar o conteúdo modificado ou criado. Para isso, implementamos um conversor que converte formatos para frente e para trás. Eles escreveram um teste simples para ele, que pega o conteúdo de uma postagem no formato de corpo estruturado, converte-o no formato ProseMirror, depois volta e já compara a versão original com a recebida. Acabou de forma rápida e fácil.

Mais tarde, à medida que o esquema de documentos mudou e, em regra, se tornou mais complicado, ficou claro que a menor alteração poderia levar a erros no editor, e esse teste parece dar uma cobertura muito ruim. Como resultado, tive que escrever testes em quase todas as combinações de nós e marcas em dois pequenos métodos de conversão. Agora, sem esses testes, é impossível determinar se a próxima mudança no circuito quebrará algo ou não.

O próximo problema está novamente relacionado à necessidade de compatibilidade com versões anteriores de novas e antigas tecnologias. Nosso editor WYSIWYG é implementado apenas em navegadores (desktop e, em breve, mobile). Assim, para editar o conteúdo no cliente é fornecido em JSON no formato corpo estruturado, no entanto, a leitura de postagens em navegadores é realizada apenas a partir de HTML. Ao mesmo tempo, a maioria dos aplicativos móveis já mudou para a exibição de postagens de usuários diretamente do corpo estruturado.

Para aplicativos móveis, era necessário prever o caso em que o cliente não pode processar algum elemento do corpo estruturado. Por exemplo, se um novo elemento for adicionado ao corpo estruturado, cuja exibição será implementada apenas em uma versão mais recente do aplicativo. Como nem todos os usuários atualizam seus aplicativos ao mesmo tempo, era necessário fornecer um plano “B” para versões mais antigas: em vez de criar HTML a partir do corpo estruturado, insira um fragmento HTML pronto para o elemento desejado. A presença de fragmentos HTML para cada elemento não foi fornecida no esquema do corpo estruturado, porque a própria idéia dessa estrutura era recusar-se a armazenar dados em HTML. Mas, no final, chegamos à conclusão de que precisamos de dois esquemas de corpos estruturados - um para exibição e outro para edição. As diferenças entre os esquemas sãoque o corpo estruturado para edição contém apenas o conteúdo do artigo e, para exibição, adicionamos alguns elementos adicionais. Em particular, um fragmento HTML para cada elemento é criado quando uma postagem é salva no serviço SB2HTML e é adicionada apenas ao corpo estruturado para exibir a postagem. Além disso, o corpo estruturado também exibe espaço publicitário no conteúdo para exibição.

Quando abrimos o conteúdo para edição em um navegador, basicamente não conseguimos encontrar um elemento desconhecido, porque todas as postagens são criadas e exibidas da mesma maneira. Mas eles decidiram prever esse caso também para o futuro. Para fazer isso, adicionamos um elemento stub padrão ao esquema ProseMirror. Nomeamos esse elemento como não suportadoBlock. O esboço aparece no lugar de um item não suportado. Nós o estilizamos como um retângulo cinza com texto informando que este elemento não é suportado e não pode ser editado. Quando uma postagem é salva, esse elemento permanece inalterado no corpo estruturado. O usuário pode alterar sua localização em relação a outros elementos, mas o conteúdo interno de um elemento desconhecido não pode ser alterado ou editado. No entanto, o usuário pode excluir esse elemento e, é claro,não será salvo no documento final.

Todos os problemas descritos estavam relacionados às dificuldades de implementar o próprio editor WYSIWYG. Porém, embora existisse no modo beta, não pudemos abandonar o editor antigo no TinyMCE e fomos forçados a oferecer suporte a ambos os editores, fornecendo compatibilidade com versões anteriores. Por exemplo, você pode criar uma postagem no editor WYSIWYG, salvar e editá-la no TinyMCE, salvar, abri-la novamente no WYSIWYG e assim por diante. Como resultado, ao abrir no WYSIWYG, vimos o mesmo conteúdo do salvamento anterior no TinyMCE. Para implementar a compatibilidade com versões anteriores, era necessário enviar o conteúdo HTML para o TinyMCE, que já aprendemos a criar a partir do corpo estruturado e salvar no banco de dados enquanto salvava a postagem. E ao salvar uma postagem através do TinyMCE, o conteúdo criado no servidor é executado através do serviço HTML2SB,Como resultado, podemos salvar HTML fresco e corpo estruturado.

HTML2SB é o oposto do que SB2HTML faz, ou seja, converte conteúdo de HTML em corpo estruturado. Cronologicamente, esse serviço apareceu mais cedo do que qualquer outra coisa, porque antes da criação do editor WYSIWYG, a única maneira de obter conteúdo da postagem no formato de corpo estruturado era a análise direta do HTML. O HTML2SB fazia parte da infraestrutura de back-end em torno do editor de postagem, mas depois de abandonar o TinyMCE, ele não era mais necessário.


4. Resultados beta


Agora, o editor WYSIWYG está disponível para todos os usuários na versão beta e em breve se tornará o principal editor de postagens do Sports.ru. Já recebemos uma ferramenta para criar e editar postagens que atendem à maioria dos nossos requisitos:

  • a interface do editor tornou-se clara, concisa e moderna, escrever posts longos ficou muito mais fácil;
  • Agora você pode baixar imagens de um arquivo e por um link que é imediatamente colocado em nosso armazenamento;
  • adicionou a opção de incorporar incorporações das principais redes sociais e sites de hospedagem de vídeo;
  • estilos de formatação de texto limpos;
  • os aplicativos para dispositivos móveis já mudaram para exibir postagens do corpo estruturado e podem definir seus próprios estilos de conteúdo.

Obviamente, o editor ainda não está totalmente depurado, periodicamente detectamos novos bugs. As seguintes atualizações estão chegando:

  • salvamento automático;
  • Versão WYSIWYG para usuários com direitos estendidos (administradores, editores em tempo integral);
  • criar e editar postagens de navegadores móveis;
  • Mensagens sobre edição paralela de um documento por vários usuários;
  • dicas e integração;
  • widgets estatísticos para equipes esportivas, partidas e escalações.

No momento da redação deste artigo, mais de 13.000 postagens já foram publicadas na versão beta do editor, que representa cerca de 20% do número total de textos de usuários no Sports.ru no período de junho de 2019 a fevereiro de 2020, inclusive. O compartilhamento de postagens criadas e publicadas por meio do novo editor está crescendo constantemente.


FIG. 3. A proporção de postagens de usuários criadas e publicadas no novo editor

Parece que o crescimento orgânico na participação de posts de usuários criados e publicados por meio do novo editor é um sinal de satisfação dos usuários com a atualização, o que também é confirmado pelo feedback no anúncio de seu lançamento em testes beta (alguns deles são mostrados na Figura 4). Portanto, nos próximos meses, planejamos transferir completamente a criação de postagens para o novo editor, de modo a focar apenas em seu suporte e desenvolvimento. 
A propósito, que funcionalidade você adicionaria ao nosso editor WYSIWYG?


FIG. 4. Comentários do usuário em um post com o anúncio da atualização do editor WYSIWYG 

All Articles