@@ -1496,7 +1496,202 @@ console.log(JSON.stringify(ast, null, 2))
1496
1496
1497
1497
---
1498
1498
1499
- ## transform
1499
+ ## transform 转换器
1500
+
1501
+ 前面已经实现了 解析器(parser) —— 将模板字符串解析为 AST 语法树,接下来需要实现 transform 转换器,进一步处理模板 AST,为后期的代码生成做准备。
1502
+
1503
+ 做了什么工作:
1504
+
1505
+ - 预处理插值
1506
+ - 生成 codegenNode 节点,用于后续的代码生成
1507
+ - 生成 patchFlag
1508
+ - 处理指令
1509
+ - ...
1510
+
1511
+ 接下来看看代码实现。
1512
+
1513
+ ---
1514
+
1515
+ 主入口函数 ` transform ` :
1516
+
1517
+ ``` js
1518
+ /**
1519
+ * AST 转换
1520
+ * @param {Object} root 根节点
1521
+ * @param {Object} options 配置项
1522
+ */
1523
+ export function transform (root , options = {}) {
1524
+ // 创建上下文
1525
+ const context = createTransformContext (root, options)
1526
+ // 遍历 ast
1527
+ traverseNode (root, context)
1528
+ // 处理根节点
1529
+ createRootCodegen (root)
1530
+ }
1531
+ ```
1532
+
1533
+ ---
1534
+
1535
+ 创建上下文
1536
+
1537
+ <div grid =" ~ cols-2 gap-2 " >
1538
+
1539
+ ``` js
1540
+ function createTransformContext (
1541
+ root ,
1542
+ { nodeTransforms = [] }
1543
+ ) {
1544
+ const context = {
1545
+ currentNode: null , // 当前转换的节点
1546
+ root, // 根节点
1547
+ parent: null , // 当前转换节点的父节点
1548
+ nodeTransforms, // 节点转换函数列表
1549
+ }
1550
+
1551
+ return context
1552
+ }
1553
+ ```
1554
+
1555
+ ``` js
1556
+ {
1557
+ nodeTransforms: [
1558
+ transformElement,
1559
+ transformExpression,
1560
+ transformText,
1561
+ ]
1562
+ }
1563
+ ```
1564
+
1565
+ </div >
1566
+
1567
+ ---
1568
+
1569
+ ``` js
1570
+ // 遍历 AST,执行 transforms
1571
+ function traverseNode (ast , context ) {
1572
+ context .currentNode = ast
1573
+ const exitFns = [] // 保存退出函数
1574
+ const transforms = context .nodeTransforms
1575
+ for (let i = 0 ; i < transforms .length ; i++ ) {
1576
+ // 执行转换操作,返回待执行的一个回调函数
1577
+ const onExit = transforms[i](context .currentNode , context)
1578
+ if (onExit) exitFns .push (onExit)
1579
+ // 由于转换函数可能移除当前节点,因此需要在转换函数执行之后检查当前节点是否存在,如果不存在,则直接返回
1580
+ if (! context .currentNode ) return
1581
+ }
1582
+ const children = context .currentNode .children
1583
+ if (children) {
1584
+ children .forEach ((child , index ) => {
1585
+ context .parent = context .currentNode
1586
+ traverseNode (child, context)
1587
+ })
1588
+ }
1589
+ let size = exitFns .length
1590
+ // 回调函数反序执行,从叶节点往根节点执行
1591
+ // 保证了 先处理子节点 再处理父节点
1592
+ while (size-- ) {
1593
+ exitFns[size]()
1594
+ }
1595
+ }
1596
+ ```
1597
+
1598
+ ---
1599
+
1600
+ 拿到子节点的 ` codegenNode ` ,将其挂载到 ` root ` 上
1601
+
1602
+ ``` js
1603
+ function createRootCodegen (root ) {
1604
+ const { children } = root
1605
+ if (children .length === 1 ) {
1606
+ // 单根节点
1607
+ const child = children[0 ]
1608
+ if (child .type === NodeTypes .ELEMENT && child .codegenNode ) {
1609
+ const codegenNode = child .codegenNode
1610
+ root .codegenNode = codegenNode
1611
+ } else {
1612
+ root .codegenNode = child
1613
+ }
1614
+ } else if (children .length > 1 ) {
1615
+ // 多根节点
1616
+ } else {
1617
+ // no children
1618
+ }
1619
+ }
1620
+ ```
1621
+
1622
+ ---
1623
+
1624
+ 生成 ` codegenNode ` 节点:
1625
+
1626
+ ``` js
1627
+ function transformElement (node , context ) {
1628
+ // 返回一个退出函数
1629
+ return () => {
1630
+ if (node .type !== NodeTypes .ELEMENT ) return
1631
+ const type = node .type
1632
+ const tag = node .tag
1633
+ const props = node .props
1634
+ const children = node .children
1635
+ // 简单处理下
1636
+ node .props .forEach (prop => {
1637
+ if (! prop .isStatic ) {
1638
+ prop .value = ` _ctx.${ prop .value } `
1639
+ }
1640
+ })
1641
+ node .codegenNode = {
1642
+ type,
1643
+ tag,
1644
+ props,
1645
+ children
1646
+ }
1647
+ // ... 在源码中这块其实是非常复杂的
1648
+ }
1649
+ }
1650
+ ```
1651
+
1652
+ ---
1653
+
1654
+ 处理插值表达式
1655
+
1656
+ <div grid =" ~ cols-2 gap-2 " >
1657
+
1658
+ ``` js
1659
+ function transformExpression (node ) {
1660
+ if (node .type === NodeTypes .INTERPOLATION ) {
1661
+ node .content = processExpression (node .content )
1662
+ }
1663
+ }
1664
+
1665
+ function processExpression (node ) {
1666
+ node .content = ` _ctx.${ node .content } `
1667
+ return node
1668
+ }
1669
+ ```
1670
+
1671
+ ``` js
1672
+
1673
+ // 模板: {{ msg }}
1674
+
1675
+ {
1676
+ type: NodeTypes .INTERPOLATION ,
1677
+ content: {
1678
+ type: NodeTypes .SIMPLE_EXPRESSION ,
1679
+ content: ' msg'
1680
+ }
1681
+ }
1682
+
1683
+ // 转换 =>
1684
+
1685
+ {
1686
+ type: NodeTypes .INTERPOLATION ,
1687
+ content: {
1688
+ type: NodeTypes .SIMPLE_EXPRESSION ,
1689
+ content: ' _ctx.msg'
1690
+ }
1691
+ }
1692
+ ```
1693
+
1694
+ </div >
1500
1695
1501
1696
---
1502
1697
0 commit comments