Skip to content

Commit 1026e53

Browse files
committed
feat: 简易编译模板 & 手写红绿灯
1 parent 80dc7bd commit 1026e53

File tree

3 files changed

+188
-0
lines changed

3 files changed

+188
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
const template = '<div><p>hello </p>{{message}}</div>'
2+
3+
AstType = {
4+
rootType: 1, //根节点
5+
ElementType: 2, //标签节点
6+
smiple: 3, //插值语法
7+
textType: 4, //普通字符串
8+
}
9+
// 编译原理
10+
// 有限状态机
11+
12+
const ast = {
13+
type: AstType.rootType,
14+
children: [
15+
{
16+
type: AstType.ElementType,
17+
tag: 'div',
18+
children: [
19+
{
20+
type: AstType.textType,
21+
content: 'hello ',
22+
},
23+
{
24+
type: AstType.smiple,
25+
content: 'message',
26+
},
27+
],
28+
},
29+
],
30+
}
31+
32+
const startToken = '<'
33+
const endToken = '>'
34+
35+
function createContext(context) {
36+
return {
37+
source: context,
38+
}
39+
}
40+
41+
function parseTag(context, type) {
42+
const match = /^<\/?([a-z]*)/i.exec(context.source)
43+
console.log(match)
44+
context.source = context.source.slice(match[0].length)
45+
context.source = context.source.slice(1)
46+
if (type === 'TagEnd') return
47+
return {
48+
type: AstType.ElementType,
49+
tag: match[1],
50+
}
51+
}
52+
function parseElemet(context, TagStack) {
53+
// const element = {
54+
// type: AstType.ElementType,
55+
// tag: match[1],
56+
// }
57+
const element = parseTag(context, 'startTag')
58+
parseTagChildren(TagStack, element, context)
59+
// console.log(context.source.slice(2, 2 + element.tag.length))
60+
// console.log(context)
61+
if (context.source.slice(2, 2 + element.tag.length) == element.tag) {
62+
// 结束标签 推进一下
63+
parseTag(context, 'TagEnd')
64+
}
65+
return element
66+
}
67+
68+
function parseTagChildren(TagStack, element, context) {
69+
TagStack.push(element.tag)
70+
element.children = parseChildren(context, TagStack)
71+
TagStack.pop()
72+
}
73+
74+
function parseText(context) {
75+
let tokens = ['{{', '<']
76+
let endIndex = context.source.length
77+
const s = context.source
78+
for (let i = 0; i < tokens.length; i++) {
79+
const index = s.indexOf(tokens[i])
80+
if (index !== -1 && index < endIndex) {
81+
endIndex = index
82+
}
83+
}
84+
const content = s.slice(0, endIndex)
85+
context.source = context.source.slice(endIndex)
86+
return {
87+
type: AstType.textType,
88+
content,
89+
}
90+
}
91+
92+
function parseInterpolation(context) {
93+
// 走到这里说明是一个插值语法 {{xxx}}的形式 不管三七二十一 先推进2个字符长度
94+
context.source = context.source.slice(2)
95+
const index = context.source.indexOf('}}')
96+
97+
const content = context.source.slice(0, index)
98+
context.source = context.source.slice(index + 2)
99+
return {
100+
type: AstType.smiple,
101+
content,
102+
}
103+
}
104+
105+
function isEnd(context, TagStack) {
106+
console.log(context, TagStack)
107+
// 两种情况结束
108+
// 1. 遇到结束标签 2. context.source.length ==0
109+
if (context.source.startsWith('</')) {
110+
for (let i = 0; i < TagStack.length; i++) {
111+
const tag = TagStack[i]
112+
// 'div' '</div>'
113+
if (context.source.slice(2, tag.length + 2) === tag) {
114+
return true
115+
}
116+
}
117+
}
118+
119+
return !context.source
120+
}
121+
function parseChildren(context, TagStack) {
122+
const nodes = []
123+
while (!isEnd(context, TagStack)) {
124+
console.log(context.source, 'parseCHildren')
125+
let node
126+
if (context.source.startsWith(startToken)) {
127+
// 走进来说明是以<开头的,那就说明是标签内容
128+
if (/[a-z]/i.test(context.source[1])) {
129+
node = parseElemet(context, TagStack)
130+
}
131+
} else if (context.source.startsWith('{{')) {
132+
node = parseInterpolation(context, TagStack)
133+
}
134+
if (!node) {
135+
node = parseText(context, TagStack)
136+
}
137+
nodes.push(node)
138+
}
139+
140+
return nodes
141+
}
142+
143+
function parse(template) {
144+
const context = createContext(template)
145+
146+
const res = parseChildren(context, [])
147+
148+
return {
149+
children: res,
150+
type: AstType.rootType,
151+
}
152+
}
153+
const res = parse(template)
154+
155+
console.log(res)
156+
const str = `const {name,age} = Vue
157+
console.log(name,age)`
158+
const render = new Function("Vue={name: 'coderwei1', age: 181}", str)({
159+
name: 'coderwei',
160+
age: 18,
161+
})
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
async function foo() {
2+
console.log('红')
3+
await sleep(1000)
4+
console.log('绿')
5+
await sleep(2000)
6+
console.log('黄')
7+
}
8+
9+
function sleep(time) {
10+
return new Promise((resolve) => {
11+
setTimeout(resolve, time)
12+
})
13+
}
14+
15+
foo()

面试常见的手写题/index.html

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Document</title>
8+
</head>
9+
<body>
10+
<script src="./19-简易编译模板.js"></script>
11+
</body>
12+
</html>

0 commit comments

Comments
 (0)