Skip to content

Commit b5b963f

Browse files
committed
wip: refactor children resolution (remove thunk mechanism)
1 parent 2289481 commit b5b963f

File tree

12 files changed

+31
-84
lines changed

12 files changed

+31
-84
lines changed

flow/vnode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
declare type VNodeChildren = Array<any> | () => Array<any> | string
1+
declare type VNodeChildren = Array<any> | string
22

33
declare type VNodeComponentOptions = {
44
Ctor: Class<Component>;

src/compiler/codegen/index.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
import { genHandlers } from './events'
44
import { baseWarn, pluckModuleFunction } from '../helpers'
55
import baseDirectives from '../directives/index'
6-
import { no } from 'shared/util'
76

87
// configurable state
98
let warn
109
let transforms
1110
let dataGenFns
1211
let platformDirectives
13-
let isPlatformReservedTag
1412
let staticRenderFns
1513
let currentOptions
1614

@@ -29,7 +27,6 @@ export function generate (
2927
transforms = pluckModuleFunction(options.modules, 'transformCode')
3028
dataGenFns = pluckModuleFunction(options.modules, 'genData')
3129
platformDirectives = options.directives || {}
32-
isPlatformReservedTag = options.isReservedTag || no
3330
const code = ast ? genElement(ast) : '_h("div")'
3431
// console.log(code)
3532
staticRenderFns = prevStaticRenderFns
@@ -60,11 +57,7 @@ function genElement (el: ASTElement): string {
6057
code = genComponent(el)
6158
} else {
6259
const data = genData(el)
63-
// if the element is potentially a component,
64-
// wrap its children as a thunk.
65-
const children = !el.inlineTemplate
66-
? genChildren(el, !isPlatformReservedTag(el.tag) /* asThunk */)
67-
: null
60+
const children = el.inlineTemplate ? null : genChildren(el)
6861
code = `_h('${el.tag}'${
6962
data ? `,${data}` : '' // data
7063
}${

src/core/instance/lifecycle.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { emptyVNode } from '../vdom/vnode'
55
import { observerState } from '../observer/index'
66
import { warn, validateProp, remove, noop } from '../util/index'
77

8+
export let activeInstance: any = null
9+
810
export function initLifecycle (vm: Component) {
911
const options = vm.$options
1012

@@ -76,13 +78,16 @@ export function lifecycleMixin (Vue: Class<Component>) {
7678
callHook(vm, 'beforeUpdate')
7779
}
7880
const prevEl = vm.$el
81+
const prevActiveInstance = activeInstance
82+
activeInstance = vm
7983
if (!vm._vnode) {
8084
// Vue.prototype.__patch__ is injected in entry points
8185
// based on the rendering backend used.
8286
vm.$el = vm.__patch__(vm.$el, vnode, hydrating)
8387
} else {
8488
vm.$el = vm.__patch__(vm._vnode, vnode)
8589
}
90+
activeInstance = prevActiveInstance
8691
vm._vnode = vnode
8792
// update __vue__ reference
8893
if (prevEl) {
@@ -107,6 +112,7 @@ export function lifecycleMixin (Vue: Class<Component>) {
107112
renderChildren: ?VNodeChildren
108113
) {
109114
const vm: Component = this
115+
const hasChildren = !!(vm.$options._renderChildren || renderChildren)
110116
vm.$options._parentVnode = parentVnode
111117
vm.$options._renderChildren = renderChildren
112118
// update props
@@ -131,6 +137,10 @@ export function lifecycleMixin (Vue: Class<Component>) {
131137
vm.$options._parentListeners = listeners
132138
vm._updateListeners(listeners, oldListeners)
133139
}
140+
// force udpate if has children
141+
if (hasChildren) {
142+
vm.$forceUpdate()
143+
}
134144
}
135145

136146
Vue.prototype.$forceUpdate = function () {

src/core/instance/render.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export function renderMixin (Vue: Class<Component>) {
184184
}
185185
}
186186

187-
export function resolveSlots (renderChildren: any): Object {
187+
export function resolveSlots (renderChildren: ?VNodeChildren): Object {
188188
const slots = {}
189189
if (!renderChildren) {
190190
return slots

src/core/vdom/create-component.js

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,6 @@ export function createComponent (
1919
children?: VNodeChildren,
2020
tag?: string
2121
): VNode | void {
22-
// ensure children is a thunk
23-
if (process.env.NODE_ENV !== 'production' &&
24-
children && typeof children !== 'function') {
25-
warn(
26-
'A component\'s children should be a function that returns the ' +
27-
'children array. This allows the component to track the children ' +
28-
'dependencies and optimizes re-rendering.'
29-
)
30-
}
31-
3222
if (!Ctor) {
3323
return
3424
}
@@ -84,7 +74,7 @@ export function createComponent (
8474
props,
8575
parent,
8676
data,
87-
children: () => normalizeChildren(children),
77+
children: normalizeChildren(children),
8878
slots: () => resolveSlots(children)
8979
}
9080
)
@@ -156,10 +146,6 @@ function prepatch (
156146
vnode, // new parent vnode
157147
options.children // new children
158148
)
159-
// always update abstract components.
160-
if (child.$options.abstract) {
161-
child.$forceUpdate()
162-
}
163149
}
164150

165151
function insert (vnode: MountedComponentVNode) {

src/core/vdom/helpers.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,6 @@ export function normalizeChildren (
77
children: any,
88
ns: string | void
99
): Array<VNode> | void {
10-
// Invoke children thunks. Components always receive their children
11-
// as thunks so that they can perform the actual render inside their
12-
// own dependency collection cycle. Also, since JSX automatically
13-
// wraps component children in a thunk, we handle nested thunks to
14-
// prevent situations such as <MyComponent>{ children }</MyComponent>
15-
// from failing when it produces a double thunk.
16-
while (typeof children === 'function') {
17-
children = children()
18-
}
19-
2010
if (isPrimitive(children)) {
2111
return [createTextVNode(children)]
2212
}

src/core/vdom/patch.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import config from '../config'
1111
import VNode from './vnode'
1212
import { isPrimitive, _toString, warn } from '../util/index'
13+
import { activeInstance } from '../instance/lifecycle'
1314

1415
const emptyData = {}
1516
const emptyNode = new VNode('', emptyData, [])
@@ -156,10 +157,10 @@ export function createPatchFunction (backend) {
156157
// of going through the normal attribute patching process.
157158
function setScope (vnode) {
158159
let i
159-
if (isDef(i = vnode.host) && isDef(i = i.$options._scopeId)) {
160+
if (isDef(i = vnode.context) && isDef(i = i.$options._scopeId)) {
160161
nodeOps.setAttribute(vnode.elm, i, '')
161162
}
162-
if (isDef(i = vnode.context) && isDef(i = i.$options._scopeId)) {
163+
if (activeInstance !== vnode.context && isDef(i = activeInstance.$options._scopeId)) {
163164
nodeOps.setAttribute(vnode.elm, i, '')
164165
}
165166
}

src/server/render.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ export function createRenderFunction (
4040
const get = cache && normalizeAsync(cache, 'get')
4141
const has = cache && normalizeAsync(cache, 'has')
4242

43+
// used to track and apply scope ids
44+
let activeInstance: any
45+
4346
function renderNode (
4447
node: VNode,
4548
write: Function,
@@ -96,7 +99,10 @@ export function createRenderFunction (
9699
normalizeRender(child)
97100
const childNode = child._render()
98101
childNode.parent = node
102+
const prevActive = activeInstance
103+
activeInstance = child
99104
renderNode(childNode, write, next, isRoot)
105+
activeInstance = prevActive
100106
}
101107

102108
function renderComponentWithCache (node, write, next, isRoot, cache, key) {
@@ -179,7 +185,9 @@ export function createRenderFunction (
179185
}
180186
// attach scoped CSS ID
181187
let scopeId
182-
if (node.host && (scopeId = node.host.$options._scopeId)) {
188+
if (activeInstance &&
189+
activeInstance !== node.context &&
190+
(scopeId = activeInstance.$options._scopeId)) {
183191
markup += ` ${scopeId}`
184192
}
185193
while (node) {
@@ -196,6 +204,7 @@ export function createRenderFunction (
196204
write: (text: string, next: Function) => void,
197205
done: Function
198206
) {
207+
activeInstance = component
199208
normalizeRender(component)
200209
renderNode(component._render(), write, done, true)
201210
}

test/unit/features/options/functional.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ describe('Options functional', () => {
1010
functional: true,
1111
props: ['msg'],
1212
render (h, { props, children }) {
13-
return h('div', null, [props.msg, ' '].concat(children()))
13+
return h('div', null, [props.msg, ' '].concat(children))
1414
}
1515
}
1616
}
@@ -54,7 +54,7 @@ describe('Options functional', () => {
5454
functional: true,
5555
props: ['field'],
5656
render (h, { props, children, data: { on } }) {
57-
props.child = children()[0]
57+
props.child = children[0]
5858
return h('validate-control', { props, on })
5959
}
6060
},

test/unit/features/options/render.spec.js

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,4 @@ describe('Options render', () => {
3636
new Vue().$mount()
3737
expect('Failed to mount component: template or render function not defined.').toHaveBeenWarned()
3838
})
39-
40-
// Since JSX automatically thunkifies children, this will
41-
// prevent <MyComponent>{ children }</MyComponent> from
42-
// failing when it produces a double thunk.
43-
it('should support nested thunk children', () => {
44-
const vm = new Vue({
45-
render: h => h('div',
46-
() => () => () => ['hello ', h('strong', 'world')]
47-
)
48-
}).$mount()
49-
expect(vm.$el.innerHTML).toBe('hello <strong>world</strong>')
50-
})
5139
})

0 commit comments

Comments
 (0)