设计改版 | 腾讯问卷文本编辑模式重构与改进

三分设

共 4130字,需浏览 9分钟

 ·

2022-02-11 09:02

点击"三分"关注,回复"社群"加入我们
欢迎来到专业设计师学习交流社区
三分设|连接知识,助力全球 1 亿设计师成长
转自:腾讯CDC体验设计
编辑:张梦如
共 3383 字 15 图  预计阅读 9 分钟


Background

背景 


腾讯问卷有两种创建问卷的模式,分别是高级编辑与文本编辑。
高级编辑提供方便的 UI 界面,用户通过拖拽形式即可完成问卷创建。


而文本编辑则是通过纯文本来生成问卷,适合需要大量创建题目的用户。


当前文本编辑模式的解析器基于正则表达式实现,代码逻辑十分分散且复杂,使得想添加新语法会变得非常困难。
而且并没有实现文本与问题 ID 的绑定,每次对文本内容修改时,会重新解析并实时生成新的问题 ID 。
问题 ID 的变化会使之前设置的 DSL (根据 ID 来处理逻辑)全部失效,同时还会导致以往关联的答案数据因为 ID 的丢失,而变得无效。

 

Part 1

任务


基于以上背景介绍的问题,本次任务的目标是:
  • 解决每次编辑会重新生成 ID 的问题
 

Part 2

前置工作


在行动前,我们还组织了一次方案评审会议,在这次会议中,也提到了一些没有考虑到的问题,比如:
  • 复制题目时 ID 冲突,解决方法:粘贴时先触发 diff ,若文本中已有相同 ID ,则将当前要粘贴的文本 ID 去除。
  • 改题型时导致已回收数据无法使用,解决方法:修改题型时,重新分配 ID 。
是一次十分有意义的会议。

 

Part 3

行动


基于 PEG.js 改造

 

Part 4

动机


由文本生成问卷的结构化对象( SurveyJson ),这无异于是在做类似 Markdown 的事,说白了,就是写一门问卷语言。
既然是要创建一门新语言,经过我们的研究, PEG.js 是一项比较好的选择。
从维基百科我们可以了解到, PEG 在实现一门语言的方面,十分有优势。
  • 因为 PEG 更加严格更加强大, PEG 可以成为很好的正则表达式的替代品。例如,一个正则表达式本身是无法匹配嵌套的括号对,因为正则表达式不是递归的,但是 PEG 却能做到这点。
  • 所有的 PEG 都能通过使用 Parkrat Parser 达到线性时间解析,如同上文所述。
  •  CFG 表达的解析器,比如 LR 解析器,需要首先进行一个单独的断词步骤。这个步骤根据空白的位置或者发音等等因素把输入分成词。分词是必要的,因为这类解析器使用向前检查来判断上下文无关文法是否匹配要求。PEG 不需要单独的断词步骤,断词的规则和其他文法规则可以用同样的方式写在一起。
  • 许多 CFG 固有的存在二义性,即使它们原本要描述的东西并不具有二义性。C, C++ ,  Java 里面著名的悬空 else 问题就是一个例子。这个问题通常都是应用文法之外的一个规则解决。而在 PEG 里面,因为使用了优先权,所以根本不存在这种问题。
基于上述优势,使用 PEG.js 来实现解析器还是很有说服力的。

 

Part 5

思路 


由 PEG.js 来实现文本到 SurveyJson 的话,大概会经历这么几个步骤:


由上述时序图我们可以清楚得知道,
Parser 仅负责输出问卷文本的 AST ,而 Transformer 则将 AST 转换成 SurveyJson 即可。
要转换成 SurveyJson ,还需要校验用户输入的文本是否正确,即 ASTQuestion 是否正确。按照单一职责原则的话,是需要单独遍历一遍 AST 来校验后,再转换成  SurveyJson 的,但是为了性能考虑,我还是选择了在一次遍历中,完成两件事情的操作,即有了如下的流程:


按照上述流程,我们很快就完成了基于 PEG.js 的解析器改造。

 

Part 6

解决 ID 问题


要解决每次 ID 重新生成的问题,我们想到最好的方法是在文本中显式写入 ID ,即使文本被编辑,也能根据显式写入的 ID 实现与 SurveyJson 中已有的问题绑定。
首先 ID 的来源有以下几个:
  • 有 ID 文本时,使用文本所记录的 ID 。
  • 无 ID 文本时,自动按照规则生成 ID。
然后,既然显式地写入 ID ,那就会有这几个问题:
  • 什么时候生成 ID
  • 怎么判断 ID 是否需要写入
  • 怎么写入 ID
其实,第一和第二个问题是可以放在一起解决的。
Parser 解析文本时,大概会生成这样的 ASTQuestion 结构:
type Location = { start: { offset: number, line: number, column: number } end: { offset: number, line: number, column: number }}type ASTQuestion = { id: string | null title: { content: object[], location: Location }}const astQuestion = { id: 'q-1-abcd' || null, title: { content: ['a', 'b'], location: { start: { offset: 0, line: 1, column: 1 }, end: { offset: 2, line: 1, column: 3 }, } }}
而经过 Tramsformer 转换后,会变成这样的 Question :
type Question = { id: string, title: string,}const question = { id: 'q-1-abcd', title: 'ab',}


我们可以看出,只有在 Question 中, ID 才是一定要存在的,因此,我们可以在 Transformer 中,实现 ID 的绑定或重新生成,即 astQuestion.id === null 为 True 时,自动生成 ID ,否则,沿用 astQuestion.id 。这样也给解决第三个问题打下了非常好的基础。
现在我们可以明确整个文本解析会有如下流程:


为了解决ID回填的问题,我们设计了以下函数:

const refillQid = (text: string, astQuestion, question: Question) { // 旧文本中没有 id if (astQuestion.id === null) { text = insert(text, question.id, astQuestion.title.location) } return text}

这样,我们就可以做到仅向没有ID的题目写入新生成的ID了。


Part 7

衍生问题


但是也带来了一个新问题:文本中有 ID 的存在,若是直接显示且允许编辑会造成更大的混乱。
幸运的是,文本编辑器基于 CodeMirror 实现,给这个问题带来了比较好的解决方案。我们可以用 CodeMirror 提供的 markText 方法来实现 ID 的隐藏,然后通过各种输入或删除事件的拦截,实现 ID 总是在处于正确位置。
比如在输入完一道题目后要动态的插入 ID :


类型变化时,重新生成 ID 要替换文本中的 ID :


题目变成选项,或者选项变成题目时 ID 类型的改变:


还有在输入时,自动略过 ID :


在内容删除完后,自动删除 ID :


在上面的输入情况的处理之后,使用 CodeMirror 隐藏 ID 选项,即可实现几乎无感知的输入:


 

Part 8

迁移


鉴于该功能在业务端较为重要,且具有较高使用量,因此要迁移到新版本,必须要有足够的测试度。
我们选择了由文本编辑创建,回收量最多的前 1000 份问卷作为测试材料,对比新旧两个编辑器解析出的结果,发现能全部通过测试。
这也使我们有了迁移的底气。

 

The End

总结 


经过上述努力,现在已经实现了 PEG.js 的改造,以及 ID 丢失问题的解决。
在用户输入体验上,基本可以做到无感知地迁移,对于用户来说,可以非常平滑地过渡到新版本中。
算是一次比较成功的重构和改进。
但是,通过事件拦截来确保 ID 能处在一个正确的位置是一个比较困难的问题,毕竟有输入输出的形式有很多,不一定能覆盖到所有情况。
因此,这是一个需要长期测试,发现 BUG 并改进的任务。

—  The end  —
以下三分设文章,你可能也感兴趣
 
📚 原创文章精选📚
告别加班!使用设计系统方法更快地构建产品
如何评估设计质量,提升设计技能
你常常忽略的 7 个具有破坏性的体验鸿沟
一篇文章告诉你服务设计到底能做什么?
看 Apple CarPlay 如何提升驾驶安全和驾驶体验
一起看看《对马岛之魂》如何打造初次对抗体验
制定 “小目标”,了解产品管理中的结构化思维
35+ 的 Windows 系统到底有什么了不起?
导师与学员的高效交流 APP 设计 —— Lightship

🙋 我们一起聊设计 🙋‍♂️
高质量,学设计行交流微信群
期待与更多优秀用户体验设计师一起成长
PS:欢迎大家关注三分设,每天早上9点,准时充电。分享优质设计、创意灵感、新知新识,定期大咖老师直播分享,零距离连麦,答疑解惑。 添加小小虫微信号【 Lil_Bug 】,备注【 三分设 】加入!(只面向星标了公众号三分设的粉丝) 
点亮『在看』,百万年薪
浏览 67
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报