create-functional-component.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /* @flow */
  2. import VNode from './vnode'
  3. import { createElement } from './create-element'
  4. import { resolveInject } from '../instance/inject'
  5. import { resolveSlots } from '../instance/render-helpers/resolve-slots'
  6. import { installRenderHelpers } from '../instance/render-helpers/index'
  7. import {
  8. isDef,
  9. isTrue,
  10. camelize,
  11. emptyObject,
  12. validateProp
  13. } from '../util/index'
  14. function FunctionalRenderContext (
  15. data,
  16. props,
  17. children,
  18. parent,
  19. Ctor
  20. ) {
  21. const options = Ctor.options
  22. this.data = data
  23. this.props = props
  24. this.children = children
  25. this.parent = parent
  26. this.listeners = data.on || emptyObject
  27. this.injections = resolveInject(options.inject, parent)
  28. this.slots = () => resolveSlots(children, parent)
  29. // ensure the createElement function in functional components
  30. // gets a unique context - this is necessary for correct named slot check
  31. const contextVm = Object.create(parent)
  32. const isCompiled = isTrue(options._compiled)
  33. const needNormalization = !isCompiled
  34. // support for compiled functional template
  35. if (isCompiled) {
  36. // exposing $options for renderStatic()
  37. this.$options = options
  38. // pre-resolve slots for renderSlot()
  39. this.$slots = this.slots()
  40. this.$scopedSlots = data.scopedSlots || emptyObject
  41. }
  42. if (options._scopeId) {
  43. this._c = (a, b, c, d) => {
  44. const vnode: ?VNode = createElement(contextVm, a, b, c, d, needNormalization)
  45. if (vnode) {
  46. vnode.functionalScopeId = options._scopeId
  47. vnode.functionalContext = parent
  48. }
  49. return vnode
  50. }
  51. } else {
  52. this._c = (a, b, c, d) => createElement(contextVm, a, b, c, d, needNormalization)
  53. }
  54. }
  55. installRenderHelpers(FunctionalRenderContext.prototype)
  56. export function createFunctionalComponent (
  57. Ctor: Class<Component>,
  58. propsData: ?Object,
  59. data: VNodeData,
  60. contextVm: Component,
  61. children: ?Array<VNode>
  62. ): VNode | void {
  63. const options = Ctor.options
  64. const props = {}
  65. const propOptions = options.props
  66. if (isDef(propOptions)) {
  67. for (const key in propOptions) {
  68. props[key] = validateProp(key, propOptions, propsData || emptyObject)
  69. }
  70. } else {
  71. if (isDef(data.attrs)) mergeProps(props, data.attrs)
  72. if (isDef(data.props)) mergeProps(props, data.props)
  73. }
  74. const renderContext = new FunctionalRenderContext(
  75. data,
  76. props,
  77. children,
  78. contextVm,
  79. Ctor
  80. )
  81. const vnode = options.render.call(null, renderContext._c, renderContext)
  82. if (vnode instanceof VNode) {
  83. vnode.functionalContext = contextVm
  84. vnode.functionalOptions = options
  85. if (data.slot) {
  86. (vnode.data || (vnode.data = {})).slot = data.slot
  87. }
  88. }
  89. return vnode
  90. }
  91. function mergeProps (to, from) {
  92. for (const key in from) {
  93. to[camelize(key)] = from[key]
  94. }
  95. }