Skip to content

Commit df8caf9

Browse files
committed
chore(ci): blog sync
1 parent 027229c commit df8caf9

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
---
2+
title: Vue源码——模版编译(八)
3+
date: 2023-12-23T12:39:51Z
4+
summary:
5+
tags: []
6+
---
7+
8+
## 前言
9+
在前篇文章我们了解到了在 vue 模板编译流程中解析到文本时调用 `chars` 钩子,在里面处理相关的内容;我们知道,Vue 可以使用插值符号在文本中随意的穿插变量,那么它是怎么解析插值语法的呢?这就用到了我们的 parseText 函数。
10+
```
11+
if (!inVPre && text !== " " && (res = parseText(text, delimiters))) {
12+
child = {
13+
type: 2,
14+
expression: res.expression,
15+
tokens: res.tokens,
16+
text,
17+
};
18+
} else if (
19+
text !== " " ||
20+
!children.length ||
21+
children[children.length - 1].text !== " "
22+
) {
23+
child = {
24+
type: 3,
25+
text,
26+
};
27+
}
28+
```
29+
## 正文
30+
31+
### 自定义插值符号
32+
在编译器 options 中有属性 delimiters 允许用户传入自定义的插值符号(默认的插值符号是{{ }}),此处 buildRegex 函数根据用户传入插值符号生成用于匹配的正则表达式
33+
```
34+
const buildRegex = cached(delimiters => {
35+
// 前面添加反斜杠在正则中用作转义
36+
const open = delimiters[0].replace(regexEscapeRE, '\\$&')
37+
const close = delimiters[1].replace(regexEscapeRE, '\\$&')
38+
// 生成正则表达式
39+
return new RegExp(open + '((?:.|\\n)+?)' + close, 'g')
40+
})
41+
```
42+
43+
函数接收两个参数,1、文本字符串;2、用户自定义的插值符号
44+
```
45+
// 默认情况下匹配文本中插值符号的正则
46+
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g
47+
// 匹配 delimiters 正则
48+
const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
49+
// flow 类型
50+
type TextParseResult = {
51+
expression: string,
52+
tokens: Array<string | { '@binding': string }>
53+
}
54+
55+
export function parseText (
56+
text: string,
57+
delimiters?: [string, string]
58+
): TextParseResult | void {
59+
// 匹配插值符号的正则
60+
const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE
61+
62+
if (!tagRE.test(text)) {
63+
return
64+
}
65+
66+
const tokens = []
67+
const rawTokens = []
68+
// 下一次开始匹配的位置
69+
let lastIndex = tagRE.lastIndex = 0
70+
71+
let match, index, tokenValue
72+
// 以首个插值匹配结果为条件
73+
while ((match = tagRE.exec(text))) {
74+
// 开头的位置
75+
index = match.index
76+
if (index > lastIndex) {
77+
// 插值符号前的文本
78+
rawTokens.push(tokenValue = text.slice(lastIndex, index))
79+
// 插值符号前的文本(json)
80+
tokens.push(JSON.stringify(tokenValue))
81+
}
82+
// 过滤非法符号
83+
const exp = parseFilters(match[1].trim())
84+
// 加工后的变量名
85+
tokens.push(`_s(${exp})`)
86+
// 加工后的变量名
87+
rawTokens.push({ '@binding': exp })
88+
89+
lastIndex = index + match[0].length
90+
}
91+
if (lastIndex < text.length) {
92+
rawTokens.push(tokenValue = text.slice(lastIndex))
93+
tokens.push(JSON.stringify(tokenValue))
94+
}
95+
return {
96+
// 以 +号连接成字符串
97+
expression: tokens.join('+'),
98+
tokens: rawTokens
99+
}
100+
}
101+
```
102+
模板文本为:`<div>姓名:[name];年龄:[age]</div>`
103+
解析后的返回值形如:
104+
```
105+
{
106+
expression: "\"姓名:\"+_s(name)+\";年龄:\"+_s(age)",
107+
tokens:["姓名",{@binding: "name"},";年龄:",{@binding: "age"}]
108+
}
109+
```
110+
## 总结
111+
本篇文章介绍了 parseText 的作用,这里对我们 Vue 的插值语法文本进行加工,为后续 render 函数渲染 dom 做准备
112+
113+

0 commit comments

Comments
 (0)