Skip to content

Commit 94890fc

Browse files
committed
update transform
1 parent b0393ae commit 94890fc

File tree

1 file changed

+196
-1
lines changed

1 file changed

+196
-1
lines changed

slides.md

Lines changed: 196 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,202 @@ console.log(JSON.stringify(ast, null, 2))
14961496

14971497
---
14981498

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>
15001695

15011696
---
15021697

0 commit comments

Comments
 (0)