从 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 = /^<!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 至多次除
空白字符"'=<>` 外的字符
例如:
<div id="mydiv" class="myClass" style="color: #ff0000" >
在 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>
<a v-on:[eventName]="doSomething"> ... </a>
在 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 至多个除 > 以外的任何字符,最后接 > 。
如:</div>
doctype
const doctype = /^<!DOCTYPE [^>]+>/i
匹配以 <!DOCTYPE 开头,后接 1 至多次除 > 外的所有字符,后接 >。注意该匹配模式不区分大小写。
如:<!DOCTYPE html>
comment
const comment = /^<!\--/
匹配以 <!-- 开头的字符串。
如:<!--STATUS OK-->
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匹配包括下划线的任何单词字符

浙公网安备 33010602011771号