从 Vue parseHTML 来学习正则表达式

写作本文的起源在于,在分析 Vue 源码中 parseHTML 方法时,发现网上对其中正则的解析文章较少,找到的几篇文章也有些语焉不详。于是静下心逐个表达式分析了其中的正则,以备查看。

常见正则规则可参见附录 1,Vue parseHTML 正则所用规则均可在其中找到定义。

Vue parseHTML 中所用的所有正则如下:

const attribute = /\s*([\s"’<>/=]+)(?:\s*(=)\s*(?:"(["]*)"+|’([’])’+|([^\s"’=<>]+)))?/ const dynamicArgAttribute = /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>]+)))?/
const ncname = [a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeRegExp.source}]*
const qnameCapture = ((?:${ncname}\\:)?${ncname})
const startTagOpen = new RegExp(^<${qnameCapture})
const startTagClose = /^\s
(/?)>/
const endTag = new RegExp(^<\\/${qnameCapture}[^>]*>)
const doctype = /^]+>/i
const comment = /^<!–/
const conditionalComment = /^<![/
接下来一个个通过拆解表达式,来分析上述正则规则。

attribute
const attribute = /\s*([\s"’<>/=]+)(?:\s*(=)\s*(?:"(["]*)"+|’([’]*)’+|([^\s"’=<>`]+)))?/
分析其结构:

^\s* 匹配 0 至多个以空白字符开头的字符串空白字符的部分

捕获组:([^\s"’<>/=]+) 匹配并捕获 1 至多次除 空白字符 " ’ < > / = 以外的所有字符

非捕获组:(?:\s(=)\s(?:"(["])"+|’([’])’+|([^\s"’=<>`]+)))?

\s* 匹配 0 至多个空白字符
捕获组:(=) 匹配并捕获 =
\s* 匹配 0 至多个空白字符
非捕获组:(?:"(["]*)"+|’([’]*)’+|([^\s"’=<>]+))"([^"]*)"+ " 匹配 " ([^"]*) 匹配并捕获 0 至多个除 " 外的字符 "+ 匹配 1 至多次 " '([^']*)'+ ' 匹配 ' ([^']*) 匹配并捕获 0至多个除 ' 外的字符 '+ 匹配 1 至多次 ' ([^\s"'=<>]+) 匹配并捕获 1 至多次除 空白字符 " ’ = < > ` 外的字符
? 匹配 3 中非捕获组 0 次或 1 次
小结
attribute 表达式匹配的是:

以 0 至多个空白字符开头;

紧接着 1 至多个除 空白字符 " ’ < > / = 以外的字符;

紧接着 0 至多个空白字符;

紧接着 =;

紧接着 0 至多个空白字符;

紧接着匹配 0 次或 1 次:

(1) " + 0 至多个除 " 外的字符 + ";

(2) 或 ’ + 0 至多个除 ’ 外的字符 + ';

(3) 或 1 至多次除 空白字符 " ’ = < > ` 外的字符

例如:

在 Vue 的 parseHTML 时,就能将 id="mydiv"、class="myClass"、style="color: #ff0000"提取出来。

dynamicArgAttribute
const dynamicArgAttribute = /\s*((?:v-[\w-]+:|@|:|#)[[=]+][\s"’<>/=]*)(?:\s*(=)\s*(?:"(["]*)"+|’([’]*)’+|([\s"’=<>`]+)))?/
分析其结构:

^\s* 匹配以 0 至多个空白字符开头

捕获组:((?:v-[\w-]+:|@|:|#)[[=]+][\s"’<>/=]*)

非捕获组:(?:v-[\w-]+:|@|:|#) 匹配:

(1)v- + 1 次或多次包括下划线在内的任意单词字符 + :

(2)或 @

(3)或 :

(4)或 #

[[^=]+] 匹配 以 [ + 1 次或多次除 = 外的所有字符 + ]

[^\s"’<>/=]* 匹配 0 次或多次除 空白字符、"、’、<、>、/、= 以外的字符

非捕获组:(?:\s(=)\s(?:"(["])"+|’([’])’+|([^\s"’=<>`]+)))?
已在 attribute 章节分析过。

小结
dynamicArgAttribute 用于匹配:

以 0 至多个空白字符开头

紧接着:

(1)v- + 1 次或多次包括下划线在内的任意单词字符 + :;

(2)或 @

(3)或 :

(4)或 #

紧接着以 [ + 1 次或多次除 = 外的所有字符 + ]

匹配 0 次或多次除 空白字符、"、’、<、>、/、= 以外的字符

紧接着 0 至多个空白字符;

紧接着 =;

紧接着 0 至多个空白字符;

紧接着匹配 0 次或 1 次:

(1) " + 0 至多个除 " 外的字符 + ";

(2) 或 ’ + 0 至多个除 ’ 外的字符 + ';

(3) 或 1 至多次除 空白字符 " ’ = < > ` 外的字符

例如:

<a v-bind:[attributeName]=“url”> …
<a v-on:[eventName]=“doSomething”> …
在 Vue 的 parseHTML 时,就能将 v-bind:[attributeName]=“url” 这种动态参数提取出来。

ncname
const ncname = [a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeRegExp.source}]*
首先看 unicodeRegExp

const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/
定义了一系列合法字符,通过 Unicode 字符集范围匹配。

unicodeRegExp.source 用于拿到正则表达式 unicodeRegExp 的字符串。

ncname 即是一系列合法字符的集合。

qnameCapture
const qnameCapture = ((?:${ncname}\\:)?${ncname})
表示匹配 xxx:xxx 或 xxx 模式的字符。

startTagOpen
const startTagOpen = new RegExp(^<${qnameCapture})
startTagOpen 可匹配标签开始部分,即:<xxx:xxx 或 <xxx 的模式。

<xxx:xxx 代表的是带命名空间的 html 标签,文可参见这里,这种类型的标签主要作用是可以指定标签的命名空间,避免冲突。Vue 对这类的标签也做了解析。

如:<div 或 <math:div

startTagClose
const startTagClose = /^\s*(/?)>/
^\s*(/?)> 匹配以 0 至多个空白字符开头,接 0 或 1 个 / ,紧接 > 的字符串。

如: />

endTag
const endTag = new RegExp(^<\\/${qnameCapture}[^>]*>)
匹配以 </ 开头,后接合法字符,后接 0 至多个除 > 以外的任何字符,最后接 > 。

如:

doctype
const doctype = /^]+>/i
匹配以 <!DOCTYPE开头,后接 1 至多次除 > 外的所有字符,后接 >。注意该匹配模式不区分大小写。

如:

comment
const comment = /^<!–/
匹配以 <!-- 开头的字符串。

如:

conditionalComment
const conditionalComment = /^<![/
匹配以 <![ 开头的字符串。条件注释主要用于做浏览器兼容等,文可参见这里

总结
本文以 Vue 源码中 parseHTML 方法为例,分析了其中定义的正则表达式,将常见的正则规则做了梳理,同时可以备查 parseHTML 方法的正则规则,方便后续继续分析该方法。

附录 1 常见正则规则
正则里的特殊字符

  • ? + . [ ] ( ) { } | ^ $,共 13 个。
  • 表示匹配前面字符(或括号括起来的表达式,或方括号括起来的字符集)0 次或 多 次;
    ?(1)? 表示匹配前面字符(或括号括起来的表达式,或方括号括起来的字符集)0 次或 1 次;(2)? 紧跟在其它任何一个限制符(如 * + 等)后时,匹配模式为非贪婪,尽可能少地匹配;
  • 表示匹配前面字符(或括号括起来的表达式,或方括号括起来的字符集)1 次或 多 次;
    . 表示匹配除换行符以外的任意字符 1 次;
    [] 字符集,表示匹配方括号内字符中的其中一个,其中的特殊字符会被当做普通字符处理;
    () 捕获组,表示匹配其中的子表达式;
    {n,m} 匹配前面表达式最少 n 次,最多 m 次
    | 或,常用在捕获组中;
    ^ 匹配字符串的开始位置
    $ 匹配字符串的结束位置
    表达式 () 中常见写法
    (pattern) 匹配 pattern 并获取该匹配;
    (?:pattern) 匹配 pattern 但不获取该匹配;
    pattern1(?=pattern2) 匹配 pattern1 后面跟 pattern2 的字符串,不获取该匹配
    pattern1(?!pattern2) 匹配 pattern1 后面不跟 pattern2 的字符串,不获取该匹配
    (?<=pattern2)pattern1 匹配 pattern1 前面为 pattern2 的字符串,不获取该匹配
    (?<!pattern2)pattern1 匹配 pattern1 前面不为 pattern2 的字符串,不获取该匹配
    字符集 [] 常见写法
    [x|y] 匹配 x 或 y,可为字符串
    [xyz] 匹配 x 或 y 或 z 的字符
    [^xyz] 匹配 非 x 或 y 或 z 的字符
    [a-z] 匹配 a 到 z 的任意小写字符
    常见特殊字符
    \b 匹配单词边界
    \d 匹配一个数字字符
    \n 匹配一个换行符
    \r 匹配一个回车符
    \t 匹配制表符
    \s 匹配任何空白字符
    \w 匹配包括下划线的任何单词字符
    USB Microphone https://www.soft-voice.com/
    Wooden Speakers https://www.zeshuiplatform.com/
    亚马逊测评 www.yisuping.cn
    深圳网站建设www.sz886.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值