We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
<div><p></div>
parser 如何处理 <div><p></div> 的 edge case?
如 HcySunYang 所说
当用户没有以预期的方式使用框架时,是否应该打印合适的警告信息从而提供更好的开发体验,让用户快速定位问题?
我们考虑 isEnd 的条件如下:
function isEnd(context: any, parentTag) { // 结束标签 if (parentTag && context.source.startsWith(`</${parentTag}>`)) { return true } // context.source 为空 return !context.source }
很容易陷入如下的死循环中,因为此时的 parentTag 依然是 p。而 </div> 进入 parseChildren 中,不以 {{ 和 <[a-z] 开头会进入 parseText 从而死循环。
parentTag
p
</div>
parseChildren
{{
<[a-z]
分析下来主要是由于 isEnd 的判断过于严厉,只和 parentTag 进行比较,如果不是理想的 happy path,那么就会陷入死循环。
isEnd
那么尝试放宽判断条件,只判断是否是 结束标签 是否可行呢?
function isEnd(context: any, parentTag) { // 结束标签 if (context.source.startsWith('</')) { return true } // context.source 为空 return !context.source }
那显然也是不行的,在正常情况下 <div><p></p><div> 就会提前退出循环
<div><p></p><div>
考虑这两种方案的特性:
严格的方案:判断是 结束标签 且等于 parentTag
宽松的方案:仅判断 结束标签
那么我们应该提出一个折衷的方案:
Tag
因此我们将 parseElement 中的 parentTag 修改为 ancestors ,数据类型是 stack,并且在 parseElement 保存所有的 祖先
parseElement
ancestors
function parseElement(context: any, ancestors): any { // StartTag const element = parseTag(context, TagType.Start) // 入栈 element {tag,type} ancestors.push(element) element.children = parseChildren(context, ancestors) // parseChildren 递归结束 出栈 ancestors.pop() // EndTag parseTag(context, TagType.End) return element }
最后将 isEnd 修改为:
function isEnd(context: any, ancestors) { let s = context.source // 判断为结束标签 if (s.startsWith('</')) { // 和祖先标签进行对比 for (let i = ancestors.length - 1; i >= 0; i--) { const tag = ancestors[i].tag if (s.slice(2, 2 + tag.length) === tag) { return true } } } // context.source 为空 return !s }
错误信息应该在 parseElement 中处理 endTag 之前,对 endTag 进行合法性验证
endTag
function parseElement(context: any, ancestors): any { // StartTag const element = parseTag(context, TagType.Start) ancestors.push(element) element.children = parseChildren(context, ancestors) ancestors.pop() // EndTag // 判断消费后的 context.source 最前面的 tag 是否等于当前 element 的 tag if (context.source.slice(2, 2 + element.tag.length) === element.tag) { parseTag(context, TagType.End) } else { throw new Error(`缺少结束标签: ${element.tag}`) } return element }
当然后续可以将判断条件和 isEnd 进行抽离方法的重构,不在本篇的讨论中了
vue3 中的 parser 如何处理 <div><p></div> 的 edge case?
EndTag
实际开发中的 用户体验 同样重要,当用户没有以预期的方式使用时,需要从 设计 层面决定 Error 信息或者 Warning 信息是由底层浏览器抛给用户,还是由我们的产品抛给用户。
所以有时候一些条件的 缩放 平衡就很值得玩味了,happy path 有时候条件比较严厉,会导致用户的未按照预期使用的行为触发死循环等。因此可以适当放宽条件,并在合适的时机抛出 错误 让用户得知
The text was updated successfully, but these errors were encountered:
No branches or pull requests
问题
parser 如何处理
<div><p></div>
的 edge case?前言
如 HcySunYang 所说
死循环分析
我们考虑 isEnd 的条件如下:
很容易陷入如下的死循环中,因为此时的
parentTag
依然是p
。而</div>
进入parseChildren
中,不以{{
和<[a-z]
开头会进入 parseText 从而死循环。死循环解决方案
分析下来主要是由于
isEnd
的判断过于严厉,只和parentTag
进行比较,如果不是理想的 happy path,那么就会陷入死循环。那么尝试放宽判断条件,只判断是否是 结束标签 是否可行呢?
那显然也是不行的,在正常情况下
<div><p></p><div>
就会提前退出循环考虑这两种方案的特性:
严格的方案:判断是 结束标签 且等于
parentTag
宽松的方案:仅判断 结束标签
那么我们应该提出一个折衷的方案:
Tag
中被找到因此我们将
parseElement
中的parentTag
修改为ancestors
,数据类型是 stack,并且在parseElement
保存所有的 祖先最后将
isEnd
修改为:错误信息
错误信息应该在
parseElement
中处理endTag
之前,对endTag
进行合法性验证当然后续可以将判断条件和
isEnd
进行抽离方法的重构,不在本篇的讨论中了总结
vue3 中的 parser 如何处理
<div><p></div>
的 edge case?parseChildren
的死循环问题parentTag
相同parseElement
中处理EndTag
前作判断感想
实际开发中的 用户体验 同样重要,当用户没有以预期的方式使用时,需要从 设计 层面决定 Error 信息或者 Warning 信息是由底层浏览器抛给用户,还是由我们的产品抛给用户。
所以有时候一些条件的 缩放 平衡就很值得玩味了,happy path 有时候条件比较严厉,会导致用户的未按照预期使用的行为触发死循环等。因此可以适当放宽条件,并在合适的时机抛出 错误 让用户得知
The text was updated successfully, but these errors were encountered: