• createElement 参数
    • 深入数据对象
    • 完整示例
    • 约束
      • VNode 必须唯一

    createElement 参数

    接下来你需要熟悉的是如何在 createElement 函数中使用模板中的那些功能。这里是 createElement 接受的参数:

    1. // @returns {VNode}
    2. createElement(
    3. // {String | Object | Function}
    4. // 一个 HTML 标签名、组件选项对象,或者
    5. // resolve 了上述任何一种的一个 async 函数。必填项。
    6. 'div',
    7. // {Object}
    8. // 一个与模板中属性对应的数据对象。可选。
    9. {
    10. // (详情见下一节)
    11. },
    12. // {String | Array}
    13. // 子级虚拟节点 (VNodes),由 `createElement()` 构建而成,
    14. // 也可以使用字符串来生成“文本虚拟节点”。可选。
    15. [
    16. '先写一些文字',
    17. createElement('h1', '一则头条'),
    18. createElement(MyComponent, {
    19. props: {
    20. someProp: 'foobar'
    21. }
    22. })
    23. ]
    24. )

    深入数据对象

    有一点要注意:正如 v-bind:classv-bind:style 在模板语法中会被特别对待一样,它们在 VNode 数据对象中也有对应的顶层字段。该对象也允许你绑定普通的 HTML 特性,也允许绑定如 innerHTML 这样的 DOM 属性 (这会覆盖 v-html 指令)。

    1. {
    2. // 与 `v-bind:class` 的 API 相同,
    3. // 接受一个字符串、对象或字符串和对象组成的数组
    4. 'class': {
    5. foo: true,
    6. bar: false
    7. },
    8. // 与 `v-bind:style` 的 API 相同,
    9. // 接受一个字符串、对象,或对象组成的数组
    10. style: {
    11. color: 'red',
    12. fontSize: '14px'
    13. },
    14. // 普通的 HTML 特性
    15. attrs: {
    16. id: 'foo'
    17. },
    18. // 组件 prop
    19. props: {
    20. myProp: 'bar'
    21. },
    22. // DOM 属性
    23. domProps: {
    24. innerHTML: 'baz'
    25. },
    26. // 事件监听器在 `on` 属性内,
    27. // 但不再支持如 `v-on:keyup.enter` 这样的修饰器。
    28. // 需要在处理函数中手动检查 keyCode。
    29. on: {
    30. click: this.clickHandler
    31. },
    32. // 仅用于组件,用于监听原生事件,而不是组件内部使用
    33. // `vm.$emit` 触发的事件。
    34. nativeOn: {
    35. click: this.nativeClickHandler
    36. },
    37. // 自定义指令。注意,你无法对 `binding` 中的 `oldValue`
    38. // 赋值,因为 Vue 已经自动为你进行了同步。
    39. directives: [
    40. {
    41. name: 'my-custom-directive',
    42. value: '2',
    43. expression: '1 + 1',
    44. arg: 'foo',
    45. modifiers: {
    46. bar: true
    47. }
    48. }
    49. ],
    50. // 作用域插槽的格式为
    51. // { name: props => VNode | Array<VNode> }
    52. scopedSlots: {
    53. default: props => createElement('span', props.text)
    54. },
    55. // 如果组件是其它组件的子组件,需为插槽指定名称
    56. slot: 'name-of-slot',
    57. // 其它特殊顶层属性
    58. key: 'myKey',
    59. ref: 'myRef',
    60. // 如果你在渲染函数中给多个元素都应用了相同的 ref 名,
    61. // 那么 `$refs.myRef` 会变成一个数组。
    62. refInFor: true
    63. }

    完整示例

    有了这些知识,我们现在可以完成我们最开始想实现的组件:

    1. var getChildrenTextContent = function (children) {
    2. return children.map(function (node) {
    3. return node.children
    4. ? getChildrenTextContent(node.children)
    5. : node.text
    6. }).join('')
    7. }
    8. Vue.component('anchored-heading', {
    9. render: function (createElement) {
    10. // 创建 kebab-case 风格的 ID
    11. var headingId = getChildrenTextContent(this.$slots.default)
    12. .toLowerCase()
    13. .replace(/\W+/g, '-')
    14. .replace(/(^-|-$)/g, '')
    15. return createElement(
    16. 'h' + this.level,
    17. [
    18. createElement('a', {
    19. attrs: {
    20. name: headingId,
    21. href: '#' + headingId
    22. }
    23. }, this.$slots.default)
    24. ]
    25. )
    26. },
    27. props: {
    28. level: {
    29. type: Number,
    30. required: true
    31. }
    32. }
    33. })

    约束

    VNode 必须唯一

    组件树中的所有 VNode 必须是唯一的。这意味着,下面的渲染函数是不合法的:

    1. render: function (createElement) {
    2. var myParagraphVNode = createElement('p', 'hi')
    3. return createElement('div', [
    4. // 错误 - 重复的 VNode
    5. myParagraphVNode, myParagraphVNode
    6. ])
    7. }

    如果你真的需要重复很多次的元素/组件,你可以使用工厂函数来实现。例如,下面这渲染函数用完全合法的方式渲染了 20 个相同的段落:

    1. render: function (createElement) {
    2. return createElement('div',
    3. Array.apply(null, { length: 20 }).map(function () {
    4. return createElement('p', 'hi')
    5. })
    6. )
    7. }