# Vue 模板 AST 详解 ## type 节点元素描述对象的 `type` 属性用来标识一个节点的类型。 * 示例: ```js ast = { type: 1 } ``` 它有三个可取值,分别是 `1`、`2`、`3`,分别代表的含义是: * `1`:代表当前节点类型为标签 * `2`:包含字面量表达式的文本节点 * `3`:普通文本节点或注释节点 ## expression 当节点类型为 `2` 时,该节点的元素描述对象会包含 `expression` 属性。 * 示例: ```js ast = { type: 2, expression: "'abc'+_s(name)+'def'" } ``` ## tokens 与 `expression` 类似,当节点类型为 `2` 时,该节点的元素描述对象会包含 `tokens` 属性。 * 示例: ```js ast = { type: 2, expression: "'abc'+_s(name)+'def'", tokens: [ 'abc', { '@binding': '_s(name)' }, 'def' ] } ``` 节点元素描述对象的 `tokens` 属性是用来给 `weex` 使用的,这里不做过多解释。 ## tag 只有当节点类型为 `1`,即该节点为标签时其元素描述对象才会有 `tag` 属性,该属性的值代表标签的名字。 * 示例: ```js ast = { type: 1, tag: 'div' } ``` ## attrsList 只有当节点类型为 `1`,即该节点为标签时其元素描述对象才会有 `attrsList` 属性,它是一个对象数组,存储着原始的 `html` 属性名和值。 * 示例: ```js ast = { type: 1, attrsList: [ { name: 'v-for', value: 'obj of list' }, { name: 'class', value: 'box' } ] } ``` ## attrsMap 节点元素描述对象的 `attrsMap` 属性与 `attrsList` 属性一样,不同点在于 `attrsMap` 是以键值对的方式保存 `html` 属性名和值的。 * 示例: ```js ast = { type: 1, attrsMap: { 'v-for': 'obj of list', 'class': 'box' } } ``` ## attrs 节点元素描述对象的 `attrs` 属性也是一个数组,并且也只有当节点类型为 `1`,即节点为标签的时候,其元素描述对象才会包含这个属性。`attrs` 属性不同于 `attrsList` 属性,具体表现在: * 1、`attrsList` 属性仅用于解析阶段,而 `attrs` 属性则用于代码生成阶段,甚至运行时阶段。 * 2、`attrsList` 属性所包含的内容作为元素材料被解析器使用,而 `attrs` 属性所包含的内容在运行时阶段会使用原生 `DOM` 操作方法 `setAttribute` 真正将属性设置给 `DOM` 元素 简单来说 `attrs` 属性会包含以下内容: * 1、大部分使用 `v-bind`(或其缩写`:`) 指令绑定的属性会被添加到 `attrs` 数组中。 为什么说大部分而不是全部呢?因为在 `Vue` 中有个 `Must Use Prop` 的概念,对于一个属性如果它是 `Must Use Prop` 的,则该属性不会被添加到 `attrs` 数组中,而是会被添加到元素描述对象的 `props` 数组中。 如下 `html` 模板所示: ```html
``` 最终 `attrs` 数组将为: ```js ast = { attrs: [ { name: 'some-attr', value: 'val' } ] } ``` * 2、普通的非绑定属性会被添加到 `attrs` 数组中。 如下 `html` 模板所示: ```html ``` 最终 `attrs` 数组将为: ```js ast = { attrs: [ { name: 'no-binding-attr', value: '"val"' } ] } ``` 大家观察绑定属性和非绑定属性在 `attrs` 数组中的却别?很容易能够发现,非绑定属性的属性值是经过 `JSON.stringify` 的,我们已经不止一次的提到过这么做的目的。 * 3、`slot` 特性会被添加到 `attrs` 数组中。 如下 `html` 模板所示: ```html ``` 最终 `attrs` 数组将为: ```js ast = { attrs: [ { name: 'slot', value: '"header"' } ] } ``` 当然了由于 `slot` 本身是可绑定的属性,所以如果 `html` 模板如下: ```html ``` 最终 `attrs` 数组将为: ```js ast = { attrs: [ { name: 'slot', value: 'header' } ] } ``` 区别在于 `value` 值是非 `JSON.stringify` 化的。 实际上,并不是出现在 `attrs` 数组中的属性就一定会使用 `setAttribute` 函数将其添加到 `DOM` 上,例如在运行时阶段,组件会根据该组件自身的 `props` 定义,从 `attrs` 中抽离出那些作为组件 `props` 的属性元素。 ## props 节点元素描述对象的 `props` 属性也是一个数组,它的格式与 `attrs` 数组类似。就像 `attrs` 数组中的属性在运行时阶段会使用 `setAttribute` 函数将其添加到 `DOM` 上一样,`props` 数组中的属性则会直接通过 `DOM` 元素对象访问并添加,举个例子,假设 `props` 数组如下: ```js ast = { props: [ { name: 'innerHTML', value: '"some text"' } ] } ``` 则在运行时阶段,会使用如下代码操作 `DOM`: ```js elm.innerHTML = 'some text' ``` 其中 `elm` 为 `DOM` 节点对象。 那么那些属性会被当做 `props` 呢?有两种,第一种是在绑定属性时使用了 `prop` 修饰符,例如: ```html ``` 由于绑定 `some` 属性的时候使用了 `prop` 修饰符,所以 `some` 属性不会出现在元素描述对象的 `attrs` 数组中,而是会出现在元素描述对象的 `props` 数组中。 第二种是那些比较特殊的属性,在绑定这些属性时,即使没有指定 `prop` 修饰符,但是由于它属于 `Must Use Prop` 的,所以这些属性会被强制添加到元素描述对象的 `props` 数组中,只有那些属性是 `Must Use Prop`,可以查看附录:[mustuseprop](../appendix/web-util.html#mustuseprop) ## pre 节点元素描述对象的 `pre` 属性是一个布尔值,它的真假代表着标签是否使用了 `v-pre` 指令,既然是标签,所以只有当节点的类型为 `1` 的时候其元素描述对象才会拥有 `pre` 属性。 * 示例: ```js ast = { type: 1, pre: true } ``` ## ns 标签的 `Namespace`,如果一个标签是 `SVG` 标签,则该标签的元素描述对象将会拥有 `ns` 属性,其值为 `'svg'`,如果一个标签是 `