從 Vue parseHTML 來學習正則表達式
從 Vue parseHTML 所用正則來學習常用正則語法
Vue parseHTML 中所用的所有正則如下。常見正則規則可參見附錄 1,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 匹配包括下劃線的任何單詞字元

