从 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"'=<>`]+)))?/

分析其结构:

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

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

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

  • \s* 匹配 0 至多个空白字符
  • 捕获组:(=) 匹配并捕获 =
  • \s* 匹配 0 至多个空白字符
  • 非捕获组:(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>\]+))`
    • "([^"]*)"+

      • " 匹配 "
      • ([^"]*) 匹配并捕获 0 至多个除 " 外的字符
      • "+ 匹配 1 至多次 "
    • '([^']*)'+
      • ' 匹配 '
      • ([^']*) 匹配并捕获 0至多个除 ' 外的字符
      • '+ 匹配 1 至多次 '
    • ([^\s"'=<>`]+) 匹配并捕获 1 至多次除 空白字符 " ' = < > ` 外的字符
  • ? 匹配 3 中非捕获组 0 次或 1 次

小结

attribute 表达式匹配的是:

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

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

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

  4. 紧接着 =

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

  6. 紧接着匹配 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"'=<>`]+)))?/

分析其结构:

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

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

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

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

    (2)或 @

    (3)或 :

    (4)或 #

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

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

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

已在 attribute 章节分析过。

小结

dynamicArgAttribute 用于匹配:

  1. 以 0 至多个空白字符开头

  2. 紧接着:

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

    (2)或 @

    (3)或 :

    (4)或 #

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

  4. 匹配 0 次或多次除 空白字符"'<>/= 以外的字符

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

  6. 紧接着 =

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

  8. 紧接着匹配 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:xxxxxx 模式的字符。

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 个。
  1. * 表示匹配前面字符(或括号括起来的表达式,或方括号括起来的字符集)0 次或 多 次;
  2. (1)? 表示匹配前面字符(或括号括起来的表达式,或方括号括起来的字符集)0 次或 1 次;(2)? 紧跟在其它任何一个限制符(如 * + 等)后时,匹配模式为非贪婪,尽可能少地匹配;
  3. + 表示匹配前面字符(或括号括起来的表达式,或方括号括起来的字符集)1 次或 多 次;
  4. . 表示匹配除换行符以外的任意字符 1 次;
  5. [] 字符集,表示匹配方括号内字符中的其中一个,其中的特殊字符会被当做普通字符处理;
  6. () 捕获组,表示匹配其中的子表达式;
  7. {n,m} 匹配前面表达式最少 n 次,最多 m 次
  8. | 或,常用在捕获组中;
  9. ^ 匹配字符串的开始位置
  10. $ 匹配字符串的结束位置

表达式()中常见写法

  1. (pattern) 匹配 pattern 并获取该匹配;
  2. (?:pattern) 匹配 pattern 但不获取该匹配;
  3. pattern1(?=pattern2) 匹配 pattern1 后面跟 pattern2 的字符串,不获取该匹配
  4. pattern1(?!pattern2) 匹配 pattern1 后面不跟 pattern2 的字符串,不获取该匹配
  5. (?<=pattern2)pattern1 匹配 pattern1 前面为 pattern2 的字符串,不获取该匹配
  6. (?<!pattern2)pattern1 匹配 pattern1 前面不为 pattern2 的字符串,不获取该匹配

字符集[]常见写法

  1. [x|y] 匹配 x 或 y,可为字符串
  2. [xyz] 匹配 x 或 y 或 z 的字符
  3. [^xyz] 匹配 非 x 或 y 或 z 的字符
  4. [a-z] 匹配 a 到 z 的任意小写字符

常见特殊字符

  1. \b 匹配单词边界
  2. \d 匹配一个数字字符
  3. \n 匹配一个换行符
  4. \r 匹配一个回车符
  5. \t 匹配制表符
  6. \s 匹配任何空白字符
  7. \w 匹配包括下划线的任何单词字符

从 Vue parseHTML 来学习正则表达式的更多相关文章

  1. vue 和 react 学习 异同点

    vue 和 react 学习 异同点 本文不做两个框架比较,只对比了两个框架的语法对比,不代表任何观点,盗版必究,本人唯一qq:421217189 欢迎大家一起来学习探讨,壮我大前端(本文markdo ...

  2. Vue 超快速学习

    Vue 超快速学习 基础知识: 1.vue的生命周期: beforeCreate/created. beforeMount/mounted. beforeUpdate/updated. beforeD ...

  3. Vue源码学习1——Vue构造函数

    Vue源码学习1--Vue构造函数 这是我第一次正式阅读大型框架源码,刚开始的时候完全不知道该如何入手.Vue源码clone下来之后这么多文件夹,Vue的这么多方法和概念都在哪,完全没有头绪.现在也只 ...

  4. NodeJS,MongoDB,Vue,VSCode 集成学习

    NodeJS,MongoDB,Vue,VSCode 集成学习 开源项目地址:http://www.mangdot.com

  5. Vue源码学习三 ———— Vue构造函数包装

    Vue源码学习二 是对Vue的原型对象的包装,最后从Vue的出生文件导出了 Vue这个构造函数 来到 src/core/index.js 代码是: import Vue from './instanc ...

  6. Vue源码学习二 ———— Vue原型对象包装

    Vue原型对象的包装 在Vue官网直接通过 script 标签导入的 Vue包是 umd模块的形式.在使用前都通过 new Vue({}).记录一下 Vue构造函数的包装. 在 src/core/in ...

  7. legend2---开发日志12(vue如何进一步学习)

    legend2---开发日志12(vue如何进一步学习) 一.总结 一句话总结:还是得找教程(比如视频),自己摸索太浪费时间,也容易踩坑和抓不住重点 还是得找教程(比如视频),自己摸索太浪费时间,也容 ...

  8. vue.js 初步学习

    跟着b站上的视频来学 首先什么是vue.js? 跟着b站上视频来学:(o゚v゚)ノ <!DOCTYPE html> <html lang="en"> < ...

  9. 4.VUE前端框架学习记录四:Vue组件化编码2

    VUE前端框架学习记录四:Vue组件化编码2文字信息没办法描述清楚,主要看编码Demo里面,有附带完整的代码下载地址,有需要的同学到脑图里面自取.脑图地址http://naotu.baidu.com/ ...

随机推荐

  1. LVS跨网段DR模型

    客户端IP地址:172.16.8.147 路由器服务器IP地址:172.16.8.167内网IP地址:192.168.1.3 一.将客户端的网关修改为路由服务器IP地址 vim ifcfg-ens33 ...

  2. 前端Node的实用方法

    Node 一.什么是Node Node是以基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动.非阻塞式I/O模型(I/O是 input/output的缩写,即输入输出端口,在 ...

  3. 032.Python魔术方法__new__和单态模式

    一 __new__ 魔术方法 1.1 介绍 触发时机:实例化类生成对象的时候触发(触发时机在__init__之前) 功能:控制对象的创建过程 参数:至少一个cls接受当前的类,其他根据情况决定 返回值 ...

  4. Hutool :一个小而全的 Java 工具类库

    Hutool 简介 Hutool 是一个小而全的 Java 工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以"甜甜的 ...

  5. Python 递归函数详解

    递归函数的概念: 直接或间接的调用自身的函数,称为递归函数. 每调用一次自身,相当于复制一份该函数,只不过参数有变化,参数的变化,就是重要的结束条件 下面是一个递归函数的实例: #coding=utf ...

  6. Java核心技术卷阅读随笔--第3章【Java 的基本程序设计结构】

    Java 的基本程序设计结构 现在, 假定已经成功地安装了 JDK,并且能够运行第 2 章中给出的示例程序.我们从现在开始将介绍 Java 应用程序设计.本章主要介绍程序设计的基本概念(如数据类型.分 ...

  7. 浅谈:Redis持久化机制(一)RDB篇

    浅谈:Redis持久化机制(一)RDB篇 ​ 众所周知,redis是一款性能极高,基于内存的键值对NoSql数据库,官方显示,它的读效率可达到11万次每秒,写效率能达到8万次每秒,因为它基于内存以及存 ...

  8. CAS(Compare and Swap)无锁算法-学习笔记

    非阻塞同步算法与CAS(Compare and Swap)无锁算法 这篇问题对java的CAS讲的非常透彻! 锁的代价 1. 内核态的锁的时候需要操作系统进行一次上下文切换,加锁.释放锁会导致比较多的 ...

  9. logstash收集时filebeat区分日志

    logstash收集时filebeat区分日志     1.场景 filebeat在服务器中同时收集nginx和web项目日志,需要对两个日志在logstash中分别处理 2.版本区别 ==6.x之前 ...

  10. VBScript学习第一天

    编码工具:VbsEdit 1.MsgBox() 毫无例外,第一个要学的就是"Hello, World!" 直接输入: MsgBox ("Hello, World!&quo ...