AICon 上海站|日程100%上线,解锁Al未来! 了解详情
写点什么

闲鱼易用高可扩的文章发布工具建设

  • 2021-05-12
  • 本文字数:3856 字

    阅读完需:约 13 分钟

闲鱼易用高可扩的文章发布工具建设

背景

闲鱼会玩社区是一个以分享个人趣味生活为主的内容社区,在社区的运营过程中,经常有一些文章形式的内容在会玩广场上投放。此前文章创作是在一个主要承载营销搭建的平台上完成的,这种方式首先不是专门为文章场景服务的,导致搭建出来的页面是一个静态页面,数据无法被理解,也无法进入审核和分发链路。而且搭建工具无法对外使用,从而无法让外部的创作者参与文章创作。因此,我们想从零开始开发一个能快速发布且高可扩展文章结构形式的发布工具。


null

调研

业界也有许多同类型的产品,如常见的知乎、掘金、微信公众号等。微信公众号的文章无论从发布体验、消费体验以及内容结构扩展上都做得很好。我们总结微信公众号文章有以下几个特点:


1.文章内容形式丰富度高;

2.浏览体验极好;

3.支持第三方内容模版;


闲鱼会玩文章希望能在内容形式,浏览体验上尽可能对齐微信公众号文章的标准。但闲鱼会玩文章与微信公众号也有一定的区别,微信公众号面向不同的自媒体,不同垂类的自媒体对文章的展示调性是不同的,因此对文章的内容样式不会做太多限制,而闲鱼的社区有着自己的调性以及自己的品牌主题色,内容形式会更加收敛。

null

目标

文章内容结构自由度高,内容排列可以是标题、文字段落、图片以及交互组件(如投票器、链接卡片等)的排列组合,因此高可扩展性非常重要。此外,发布器作为文章创作入口,需要存储创作者全部内容信息,包括字体、字号、颜色、段落、图片等。还有展示页在端上的性能体验也是非常重要的指标。因此我们定了以下几个系统的关键目标:


1.信息还原度百分之百

2.内容结构可扩展

3.展示页秒开体验


文章发布链路大致流程如下所示:


null

方案

围绕既定的设计目标,如何实现呢?我们的方案是根据一套约定的 schema 协议来表达文章内容的所有信息,协议记录和表达所有需要在客户端还原展示的信息,最后通过这套协议把发布器和展示页连接起来。可以看出,设计一套通用、简洁的文章内容协议是方案的关键。文章发布器负责产生 schema,结构化存储后,文章展示页获取到该 schema 然后在端上解析协议并展示出对应的内容信息。方案大图如下所示,标橘黄色表示协议相关的部分,它出现在了几乎整个链路中。


null

协议设计

梳理出协议设计目标如下:

1.规则简洁易懂。规则越简单,则根据协议创建 schema 数据以及解析 schema 数据就越容易。

2.可扩展。未来的文章形式可以是非常丰富的,要求用这套协议表达任意的内容形式。

3.结构化存储。将图文内容可以进行结构化抽取和存储,文章内容的 schema 可能非常大,而数据库存储

字段一般会有字符上限,需要压缩 schema。此外文章的实体内容需要进入安全审核链路,这也要求我们必须进行内容结构化抽取。


围绕着以上三个诉求,我们我们基于钉钉的富文本协议,定制了一套符合要求的协议规则。

协议逻辑

null


现有元素标签

•root - 根元素

•p - 正文标签

•span - 文字标签

•img - 图片标签

•h2 - 二级段落标签

•card - 卡片标签

•idleVideoCard - 视频卡片

现有属性

1.常规属性

null

2.图片属性

null

3.视频属性

null

如何扩展

我们规定未来所有新增的插件,都作为一种子类的卡片,可以自定义卡片类型,卡片数据统一放到 metadata 字段中,然后端上根据卡片类型做对应的组件,并将 metadata 中的卡片数据信息作为参数导入组件中去,从而做到未来任意插件,都能映射到协议里。

结构化存储

协议是一种 json schema,这种在关系型数据库中存储是一个比较头疼的问题。图文内容存储会将内容分成三个字段,分别是文本、图片数组和自定义扩展字段。文本和图片的结构化信息会用于安全审核以及算法推荐识别,自定义字段用来存储业务上的其他信息。我们的第一版方案是将文本和图片内容提取出来单独存储,并将整个 json 字符串全部放到自定义字段中,这样只需要一存一读就可以了。然而,真实场景中,自定义字段有字符数限制。因此需要对 json 字符串进行一定的转译和压缩,只保留必要的样式和排版信息即可。

null

发布器

发布器的核心是一个富文本编辑器,市场上主流的 react 编辑器有开源的 slate.js、facebook 的 draft.js,阿里内部比较成熟的富文本工具有语雀富文本编辑器和钉钉文档团队的 we-editor。从协议的复杂度和可扩展性上考虑,我们选型了钉钉的 we-editor。


null


富文本编辑器的使用上,除了要处理在编辑器上手写文章场景外,从外部粘贴内容进编辑器的场景复杂度会更高。因为内容本身就带有富文本样式,从而导致文章样式不可控,生成的富文本协议内容混乱,不利于结构化审核和端上展示页渲染。

粘贴场景处理

粘贴进来的内容是带上内联样式属性的标签,如 div、span、a、h1、h2、img、video 等。我们的做法是,在粘贴的时候,将所有的内联样式清除,并只处理格式范围内的标签。对于 img、video 标签,则需要更多的处理,因为 img 和 video 的 src 是一个地址链接,这些链接如果是站外连接,就会存在访问跨域,同时对平台来说也存在安全风险。做法是对站外资源做转存处理,即将站外链接下载后通过内部服务将资源转存到可靠的资源服务器上。


null

插件扩展

插件扩展通过定制编辑器插件来完成。我们只保留了基础的 redo/undo、字体加粗、字体对齐、添加图片等编辑器自带能力,其他如视频、连接能力通过自定义插件完成。通过对 we-editor 插件体系封装,开发者可以像开发 react 组件一样开发插件。封装过程是将扩展插件都当作一种卡片,在 schema 里指定工具栏内容,对应点击事件,以及插入富文本的卡片样式等,即可插入任意插件。以一个插入视频的插件为例:


null

点击该插件工具栏按钮的时候,选择插入 idleVideoCard 类型的卡片即可。


null


null

函数服务层

在发布器的发布链路中,我们设计了一层 faas 函数服务层。主要考虑以下几个原因。


1.安全问题。结构化信息抽取算法应该放到服务端计算,否则会存在刷接口绕过安审链路的漏洞。

2.抽取和还原都通过 js 实现。从而保证一套规则技术栈统一。


下图为大致的数据流转示意图:


null

文章展示

文章展示的原理是通过协议规则将经过处理的 schema 还原成真正的 schema 信息,并解析信息转化成对应的可视化组件。我将重点讲协议解析和性能优化。

协议解析

理论上只要能正确解析出富文本 schema 协议表达的富文本信息,在端上可以还原成任意对应的设计规范,这也是未来我们可以做集团统一文章发布工具的基础。即只要保证协议一致,端上的展示可以大不相同。还原函数伪代码如下所示:

体验优化

前端的性能性能优化话题是一个老生常谈的话题,限于篇幅有限,这里只讲这次在文章详情页的几个主要优化方案。性能数据结果上,跨端首屏渲染时间从 1700ms 优化到 1000ms 左右,做到了秒开。先上优化前后效果对比:

null

在讲具体方案前,我们先来看一下一个 h5 页面在 webview 里是加载流程:


null


在这个链路中,最消耗时间的是各种资源的 IO,包括页面文档的 IO,样式文件、js 文件和图片的 IO 以及数据接口请求的 IO。其次耗时的是 webview 的启动耗时。因此我们的优化主要围绕减少 IO 以及提前 IO 的思路去进行。


  1. 资源 combo

    页面加载中除了包含业务的 js 文件之外,还包含 jstracker 的资源,rax 的框架资源,安全相关的 js 等,将这些资源合并成一个资源请求,则可以减少很多次请求 IO,从而降低首屏渲染时间。

  2. 图片懒加载

    文章中一般会有很多图片,这些图片大部分不会在第一屏中就出现,因此可以将未出现在屏幕中的图片先不加载,等用户下滑至该图片出现在屏幕中时,才请求该图片资源。

  3. 本地资源缓存

    文档资源下载以及 js 资源一般来说是一个长时间不变的东西,如果这些资源提前在客户端空闲的时候就已经下载好,等到请求这些资源的时候,客户端发现本地已经有了同名的资源,就拦截这次资源请求,返回本地缓存好的资源,则可以大大降低首屏渲染时间。

  4. 数据预取

    首屏渲染的时间长短有一部分取决于第一次调用的接口的返回速度,而接口请求一般要等到 js 逻辑触发接口请求时才发出。如果首屏需要获取的请求是一个确定的参数,那么是否可以将请求接口这个时机提前呢?我们数据预取的方案就是在用户点开页面的请求 URL 中就带上首屏需要请求的接口参数,然后客户端获取到这个参数后异步地请求这个数据,将结果缓存到客户端上。等到 js 逻辑需要发出请求的时候,判断当前请求是否已经被请求过,若请求过,则直接返回缓存在客户端上的接口数据。

  5. 去掉客户端 loading

    客户端 loading 本来是 webview 自带的能力,表示当前页面还有资源在加载,但其实虽然有资源在加载,但首屏的页面信息其实已经加载出来的。去掉客户端的 loading,可以给用户带来更快的体感,虽然实际上并没有加快这个流过程。优化后的加载流程如下图所示:


null


性能优化无止尽,因此文章展示页的优化也会一直做下去,可以考虑提前启动 webview 容器,ESR、NSR 等方式优化。

展望

到目前为止已经完成了文章发布工具从 0 到 1 的建设,未来需要做的事情还有很多。基于这套高可扩展的协议,可以扩展出更多更丰富的文章内容形式以及交互玩法,如投票器、弹幕、文章模版等,最终可以沉淀出一套可开放的基于当前协议的模版插件开发体系,以适应不同的文章内容体系。此外,性能体验优化会继续以高标准的要求不断进行优化。 


再往上层看,文章内容只是会玩社区内容的其中一种承载形式,我们希望有一个包含更多创作能力的创作者站点工具。pc 侧的创作者中心和手机端上的创作者发布中心呈一个互补的态势。普通的创作者更倾向于即来即发,随手发,随时发,而对内容质量有更高追求的一些 pgc 创作者则更关注内容质量,发布效率,内容消费数据等比较专业的指标。 


因此在完成了基本的发布能力后,我们会逐渐完善整个创作者发布链路,集普通图文创作、视频内容创作、文章创作、内容管理、数据中心、热点内容发现于一体的完整专业创作者发布工具。


本文转载自:闲鱼技术(ID:XYtech_Alibaba)

原文链接:闲鱼易用高可扩的文章发布工具建设

2021-05-12 14:001791

评论

发布
暂无评论
发现更多内容

提升编程效率:重构

Page

高效工作 敏捷开发 重构 高效

spring注入bean的几种策略模式

王德发

Java Spring Boot 测试驱动开发实战营

多多益善的MacOS文件管理方案

陈东泽 EuryChen

macos Mac apple 提升效率 文件管理

BVR 才是变革的核心

Yanel 说敏捷产品

团队管理 项目管理 敏捷 敏捷开发 敏捷精髓

MySQL备份基础

一个有志气的DB

MySQL 数据

敏捷团队成员的工作量指标真的那么重要吗?

金生水起

敏捷开发 Scrum精髓 敏捷精髓 Agile

Kafka零数据丢失的配置方案

奈学教育

kafka kafka配置 kafka数据

2020 敏捷产品基本盘

Yanel 说敏捷产品

产品 敏捷 产品设计 产品推荐

MySQL索引知识介绍

Simon

MySQL 索引结构

工程师、程序员和产品经理

王泰

程序员 产品经理 IT 软件工程师 工程师思维

KK日知录20200515

kimmking

看完这篇操作系统,和面试官扯皮就没问题了

苹果看辽宁体育

操作系统 计算机基础

Android | Tangram动态页面之路(一)需求背景

哈利迪

android

【大咖说问大咖】关于开源的那些事 —— PingCAP CTO 黄东旭 Q&A 交流帖

InfoQ写作社区官方

开源 写作平台 大咖说 技术交流 热门活动

何时开始重构?

Page

敏捷开发 重构

【Howe学爬虫】全国统计用区划代码爬取

Howe

Java 爬虫

从ClickHouse的名字由来讲起

nauu

数据库 大数据 分布式 OLAP Clickhouse

数据分析的利器-clickhouse概述

流沙

数据库 Clickhouse

原创 | 使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (七)CORRECT边界条件

编程道与术

Java 编程 软件测试 TDD 单元测试

Intellij IDEA2020.x如何安装Lombok插件

龙眼果

开发者工具

学习型组织的修炼之道

Yanel 说敏捷产品

团队管理 项目管理 敏捷 团队协作 组织转型

常见的主从报错集锦

一个有志气的DB

MySQL 主从配置 主从同步

搜商:高效的使用搜索引擎

石云升

高效搜索 搜索技巧 搜商

为什么我喜欢的大V拉黑我?

lmymirror

经历 后真相时代 日常思考

太赞了,VSCode 上也能画流程图了!

GitHubDaily

visual-studio GitHub 程序员 vscode 开发者工具

真的!只需 “六步” 实现图像特定物体识别!!!

攀岩飞鱼

Python OpenCV 计算机视觉 图像识别 物体检测

揭秘!开源软件背后的神秘组织

Apache Flink

大数据 flink 流计算 实时计算

近期工作的几点感想

Leiy

app退出登录

Serverless: 2020年函数计算的冷启动怎么样了

刘宇

用"结构性张力"构建自驱力

Yanel 说敏捷产品

团队管理 敏捷 团队协作 项目

Java实现Base64

Java

闲鱼易用高可扩的文章发布工具建设_架构_闲鱼技术_InfoQ精选文章