vue几种编译_Vue 是如何进行模板编译的?(二)

前言

上一节笼统介绍了 Vue 进行模板编译的过程,但其实其背后的复杂度绝对是超呼想象,要当其为一个庞大的工程来处理。众所周知,归根结底,Vue 项目也是一 html5 页面,要对其进行模板编译,也就时相当于去把这个页面所有的内容都给爬下来对其中的标签、属性等等进行一步步取值处理。下面,就一步步来欣赏其中的精彩:

首先,我们先来欣赏一下 html-parser

先来对其用到的一些正则进行简单的理解吧

1、属性值的匹配

const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/

属性值的匹配:1)以0个或者多个空格开始。2)[分组]然后至少一个不能包含空格、双引号、单引号、左尖括号、右尖括号、斜杠和等于号的字符。3)0或者一组([非捕获分组]0或者多个空格、等于号、0或者多个空格、[非捕获分组]双引号、[分组]0或者多个非双引号的字符、至少一个双引号;[分组]或者单引号、0或者多个非单引号的字符、至少一个单引号;[分组]或者然后至少一个不能包含空格、双引号、单引号、左尖括号、右尖括号、等于号和反单引号)。

()表示捕获分组,()会把每个分组里的匹配的值保存起来,使用 $n(n 是一个数字,表示第 n 个捕获组的内容)

(?:)表示非捕获分组,和捕获分组唯一的区别在于,非捕获分组匹配的值不会保存起来

具体可以匹配到类似下面三种情况如下:

'@click="tipShow=!tipShow""dfdfdfdfdfdf"""'.match(attribute)

// => ["@click="tipShow=!tipShow""", "@click", "=", "tipShow=!tipShow", undefined, undefined, index: 0, input: "@click="tipShow=!tipShow""dfdfdfdfdfdf"""", groups: undefined]

" @click = 'tipShow=!tipShow''dfdfdfdfdfdf'''".match(attribute)

// => [" @click = 'tipShow=!tipShow''", "@click", "=", undefined, "tipShow=!tipShow", undefined, index: 0, input: " @click = 'tipShow=!tipShow''dfdfdfdfdfdf'''", groups: undefined]

'@click=tipShow=!tipShowdfdfdfdfdfdf"""""'.match(attribute)

// => ["@click=tipShow", "@click", "=", undefined, undefined, "tipShow", index: 0, input: "@click=tipShow=!tipShowdfdfdfdfdfdf"""""", groups: undefined]

2、动态属性值(如包今 @、:、v- 等等 Vue 专有的)的匹配

const dynamicArgAttribute = /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/

动态属性值的匹配:1)以 0 个或者多个空格开始。2)[分组][非捕获分组]以 v-、加上至少一个单词字符或者 -、加上 :;或者以 @;或者以 : 或者以 # 开始。接着是 [、然后是一个或者多个非等号字符、接着是],然后是至少一个不能包含空格、双引号、单引号、左尖括号、右尖括号、等于号和斜杠的字符。3)[非捕获分组]以 0 个或者多个空格开始,接着是 =,接着是 0 个或者多个空格,然后[非捕获分组]双引号、[分组] 0 或者多个非双引号的字符、至少一个双引号;[分组]或者单引号、0 或者多个非单引号的字符、至少一个单引号;[分组]或者然后至少一个不能包含空格、双引号、单引号、左尖括号、右尖括号、等于号和反单引号)。

具体可以匹配到类似下面的情况如下:

' v-click:[kk] = "tipShow=!tipShow"dfdfdfdf'.match(dynamicArgAttribute)

//  => [" v-click:[kk] = "tipShow=!tipShow"", "v-click:[kk]", "=", "tipShow=!tipShow", undefined, undefined, index: 0, input: " v-click:[kk] = "tipShow=!tipShow"dfdfdfdf", groups: undefined]

' @[kk] = "tipShow=!tipShow"dfdfdfdf'.match(dynamicArgAttribute)

// => [" @[kk] = "tipShow=!tipShow"", "@[kk]", "=", "tipShow=!tipShow", undefined, undefined, index: 0, input: " @[kk] = "tipShow=!tipShow"dfdfdfdf", groups: undefined]

' :[kk] = "tipShow=!tipShow"dfdfdfdf'.match(dynamicArgAttribute)

// => [" :[kk] = "tipShow=!tipShow"", ":[kk]", "=", "tipShow=!tipShow", undefined, undefined, index: 0, input: " :[kk] = "tipShow=!tipShow"dfdfdfdf", groups: undefined]

' #[dfdfdfdfdfkk] = "tipShow=!tipShow"dfdfdfdf'.match(dynamicArgAttribute)

// => [" #[dfdfdfdfdfkk] = "tipShow=!tipShow"", "#[dfdfdfdfdfkk]", "=", "tipShow=!tipShow", undefined, undefined, index: 0, input: " #[dfdfdfdfdfkk] = "tipShow=!tipShow"dfdfdfdf", groups: undefined]

3、其它正则

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

const qnameCapture = `((?:${ncname}\\:)?${ncname})`

const startTagOpen = new RegExp(`^

const startTagClose = /^\s*(\/?)>/

const endTag = new RegExp(`^]*>`)

const doctype = /^^>]+>/i

// #7298: escape - to avoid being pased as HTML comment when inlined in page

const comment = /^

const conditionalComment = /^

首先,ncname 是一个必须以字母开头然后跟着一个或者多个下横线、点、数字、字母或者其他可支持的字符。

startTagOpen 是开标签的识别,它是由 < 开头,然后跟着[分组][零个或一个非捕获分组]的 ncname 和接着的冒号,然后是 ncname

具体可以匹配到类似下面的情况如下:

'dfdfdfd'.match(startTagOpen)

// => ["dfdfdfd", groups: undefined]

'dfdfdfd'.match(startTagOpen)

// => ["dfdfdfd", groups: undefined]

'dfdfdfd'.match(startTagOpen)

// => ["dfdfdfd", groups: undefined]

startTagClose就是对开始标签的结束进行匹配,以0个或者多个空格开始然后是 0 或者 1 个[分组]反斜杠 / 接着是右尖括号 >

具体可以匹配到类似下面的情况如下:

' />fdfdfdfdfd'.match(startTagClose)

// => [" />", "/", index: 0, input: " />fdfdfdfdfd", groups: undefined]

' >fdfdfdfdfd'.match(startTagClose)

[" >", "", index: 0, input: " >fdfdfdfdfd", groups: undefined]

endTag 就是匹配整个结束标签,能左尖括号 < 开始跟着是斜杠 /,然后是跟上面 startTagOpen 的 qnameCapture 一样,然后是 0 或者多个非右尖括号 >然后最后是结束符号右尖括号 >

具体可以匹配到类似下面的情况如下:

'dfdfd'.match(endTag)

// => ["", "kjdkfjdfkdj", index: 0, input: "dfdfd", groups: undefined]

'dfdfd'.match(endTag)

// => ["", "kjdkf:jdfkdj", index: 0, input: "dfdfd", groups: undefined]

doctype 就比较简单,直接匹配 html5 页面开始的 doctype 声明标签

comment 也是非常简单,直接就匹配注释部分

conditionalComment 就是条件注释部分,这经常会出现在要对 ie 浏览器进行版本渲染的情况。

总结

通过这些正则,我们大概知道接下来准备要进行什么操作了,就是要把整个 html 源码由头到尾吃掉,然后把所有 tag,text,样式,JS 等内容全部爬下来,在整个爬的过程以回调(start,end, chars,comment共 4 个回调函数并带参数)的方式作为工具给 index.js 中的 parse 函数使用。下一节就来详细介绍一下 parseHTML 函数。

阅读:

1,669

作者: 博主

Talk is cheap, show me the code!查看博主的所有文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值