Appearance
创建生成上下文
生成代码时我们需要将生成的代码拼接成字符串,同时添加换行缩进等。
function createCodegeContext(){
const context = {
code:``,
indentLevel:0,
helper(key){ return `_${helperNameMap[key]}`},
push(code){context.code += code;},
indent(){ // 前进
newline(++context.indentLevel)
},
deindent(withoutnewline = false){ // 缩进
if (withoutnewline) {
--context.indentLevel
} else {
newline(--context.indentLevel)
}
},
newline(){ newline(context.indentLevel) } // 换行
}
function newline(n){ context.push('\n'+` `.repeat(n))}
return context
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function generate(ast){
const context = createCodegeContext();
}
1
2
3
2
3
export function compile(template){
// 1.将模板转化成ast语法树
const ast = baseParse(template);
// 2.对ast语法树进行转化
transform(ast);
// // 3.生成代码
return generate(ast)
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
生成文本代码
let r = compile('hello, jw')
1
如果仅仅是文本直接返回即可~
function genText(node,context){
context.push(JSON.stringify(node.content)) // 添加文本代码
}
function genFunctionPreamble(ast,context){ // 生成函数
const {push,newline} = context
if(ast.helpers.length > 0){ // 生成导入语句
push(`const {${ast.helpers.map((s)=>`${helperNameMap[s]}:_${helperNameMap[s]}`).join(', ')}} = Vue`)
}
newline()
push(`return `)
}
function genNode(node,context){
switch(node.type){
case NodeTypes.TEXT:
genText(node,context)
break;
}
}
function generate(ast){
const context = createCodegeContext();
const {push,indent} = context
genFunctionPreamble(ast,context);
const functionName = 'render';
const args = ['_ctx','$props'];
push(`function ${functionName}(${args.join(', ')}){`)
indent();
push(`return `)
if (ast.codegenNode) {
genNode(ast.codegenNode, context)
} else {
push(`null`)
}
return context.code
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
生成表达式代码
let r = compile('{{age}}')
1
function genExpression(node,context){
const { content } = node
context.push(content)
}
function genInterpolation(node,context){
const { push, helper } = context
push(`${helper(TO_DISPLAY_STRING)}(`)
genNode(node.content, context)
push(`)`)
}
function genNode(node,context){
switch(node.type){
case NodeTypes.TEXT:
genText(node,context)
break;
case NodeTypes.INTERPOLATION: // 生成表达式
genInterpolation(node,context)
break;
case NodeTypes.SIMPLE_EXPRESSION: // 简单表达式的处理
genExpression(node, context)
break
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
生成元素表达式
let r = compile(`<div a='1' b='2'>123</div>`)
1
function genNodeList(nodes,context){ // 生成节点列表,用","分割
const { push } = context;
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (isString(node)) {
push(`${node}`); // 如果是字符串直接放入
}else if(Array.isArray(node)){
genNodeList(node, context)
} else {
genNode(node, context);
}
if (i < nodes.length - 1) {
push(", ");
}
}
}
function genVNodeCall(node,context){
const {push,helper} = context;
const {tag,props,children,isBlock} = node
if(isBlock){
push(`(${helper(OPEN_BLOCK)}(),`)
}
// 生成createElementBlock或者createElementVnode
const callHelper = isBlock ? CREATE_ELEMENT_BLOCK: CREATE_ELEMENT_VNODE;
push(helper(callHelper));
push('(');
genNodeList([tag, props, children].map(item=>item || 'null'), context);
push(`)`)
if(isBlock){
push(`)`)
}
}
function genNode(node,context){
switch(node.type){
case NodeTypes.VNODE_CALL: // 元素调用
genVNodeCall(node,context);
break;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
生成元素属性
function genObjectExpression(node, context) {
const { push, newline } = context
const { properties } = node
if (!properties.length) {
push(`{}`)
return
}
push('{')
for (let i = 0; i < properties.length; i++) {
const { key, value } = properties[i]
// key
push(key);
push(`: `)
push(JSON.stringify(value));
// value
if (i < properties.length - 1) {
push(`,`)
}
}
push('}')
}
function genNode(node,context){
switch(node.type){
case NodeTypes.JS_OBJECT_EXPRESSION: // 元素属性
genObjectExpression(node, context)
break
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
处理Fragment情况
如果是Fragment,则将字符串和symbol直接放入代码中,对元素再次进行递归处理
let r = compile(`<div>123</div><div>123</div>`)
1
function genNode(node,context){
if (isString(node)) {
context.push(node)
return
}
if (isSymbol(node)) {
context.push(context.helper(node))
return
}
switch(node.type){
// ...
case NodeTypes.ELEMENT:
genNode(node.codegenNode,context)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
处理复合表达式
let r = compile(`<div>123 {{abc}}</div>`)
1
function genCompoundExpression(node,context) {
for (let i = 0; i < node.children!.length; i++) {
const child = node.children![i]
if (isString(child)) {
context.push(child)
} else {
genNode(child, context)
}
}
}
function genNode(node,context){
switch(node.type){
// ...
case NodeTypes.COMPOUND_EXPRESSION:
genCompoundExpression(node, context)
break
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
复合表达式+元素处理
let r = compile(`<div>123 {{name}} <span>{{name}}</span></div>`)
1
function genCallExpression(node, context) {
const { push, helper } = context
const callee = helper(node.callee)
push(callee + `(`, node)
genNodeList(node.arguments, context)
push(`)`)
}
function genNode(node,context){
switch(node.type){
// ...
case NodeTypes.TEXT_CALL: // 对文本处理
genNode(node.codegenNode, context)
break
case NodeTypes.JS_CALL_EXPRESSION: // 表达式处理
genCallExpression(node, context)
break
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19