Skip to content

Commit fe801b1

Browse files
committed
wip: fix $parent after children change
1 parent b5b963f commit fe801b1

File tree

11 files changed

+57
-82
lines changed

11 files changed

+57
-82
lines changed

flow/vnode.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ declare type VNodeComponentOptions = {
44
Ctor: Class<Component>;
55
propsData: ?Object;
66
listeners: ?Object;
7-
parent: Component;
87
children: ?VNodeChildren;
98
tag?: string;
109
}

src/core/instance/render.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ import {
1010

1111
import { createElement } from '../vdom/create-element'
1212

13-
export const renderState: {
14-
activeInstance: ?Component
15-
} = {
16-
activeInstance: null
17-
}
18-
1913
export function initRender (vm: Component) {
2014
vm.$vnode = null // the placeholder node in parent tree
2115
vm._vnode = null // the root of the child tree
@@ -36,11 +30,6 @@ export function renderMixin (Vue: Class<Component>) {
3630

3731
Vue.prototype._render = function (): VNode {
3832
const vm: Component = this
39-
40-
// set current active instance
41-
const prev = renderState.activeInstance
42-
renderState.activeInstance = vm
43-
4433
const {
4534
render,
4635
staticRenderFns,
@@ -91,8 +80,6 @@ export function renderMixin (Vue: Class<Component>) {
9180
}
9281
// set parent
9382
vnode.parent = _parentVnode
94-
// restore render state
95-
renderState.activeInstance = prev
9683
return vnode
9784
}
9885

src/core/vdom/create-component.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import Vue from '../instance/index'
44
import VNode from './vnode'
55
import { normalizeChildren } from './helpers'
6-
import { callHook } from '../instance/lifecycle'
6+
import { activeInstance, callHook } from '../instance/lifecycle'
77
import { resolveSlots } from '../instance/render'
88
import { warn, isObject, hasOwn, hyphenate, validateProp } from '../util/index'
99

@@ -13,9 +13,7 @@ const hooksToMerge = Object.keys(hooks)
1313
export function createComponent (
1414
Ctor: Class<Component> | Function | Object | void,
1515
data?: VNodeData,
16-
parent: Component,
1716
context: Component,
18-
host: ?Component,
1917
children?: VNodeChildren,
2018
tag?: string
2119
): VNode | void {
@@ -29,7 +27,7 @@ export function createComponent (
2927

3028
if (typeof Ctor !== 'function') {
3129
if (process.env.NODE_ENV !== 'production') {
32-
warn(`Invalid Component definition: ${Ctor}`, parent)
30+
warn(`Invalid Component definition: ${Ctor}`, context)
3331
}
3432
return
3533
}
@@ -41,9 +39,8 @@ export function createComponent (
4139
} else {
4240
Ctor = resolveAsyncComponent(Ctor, () => {
4341
// it's ok to queue this on every render because
44-
// $forceUpdate is buffered. this is only called
45-
// if the
46-
parent.$forceUpdate()
42+
// $forceUpdate is buffered by the scheduler.
43+
context.$forceUpdate()
4744
})
4845
if (!Ctor) {
4946
// return nothing if this is indeed an async component
@@ -69,10 +66,10 @@ export function createComponent (
6966
}
7067
return Ctor.options.render.call(
7168
null,
72-
parent.$createElement,
69+
context.$createElement,
7370
{
7471
props,
75-
parent,
72+
context,
7673
data,
7774
children: normalizeChildren(children),
7875
slots: () => resolveSlots(children)
@@ -99,19 +96,20 @@ export function createComponent (
9996
const name = Ctor.options.name || tag
10097
const vnode = new VNode(
10198
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
102-
data, undefined, undefined, undefined, undefined, context, host,
103-
{ Ctor, propsData, listeners, parent, tag, children }
99+
data, undefined, undefined, undefined, undefined, context,
100+
{ Ctor, propsData, listeners, tag, children }
104101
)
105102
return vnode
106103
}
107104

108105
export function createComponentInstanceForVnode (
109-
vnode: any // we know it's MountedComponentVNode but flow doesn't
106+
vnode: any, // we know it's MountedComponentVNode but flow doesn't
107+
parent: any // activeInstance in lifecycle state
110108
): Component {
111109
const vnodeComponentOptions = vnode.componentOptions
112110
const options: InternalComponentOptions = {
113111
_isComponent: true,
114-
parent: vnodeComponentOptions.parent,
112+
parent,
115113
propsData: vnodeComponentOptions.propsData,
116114
_componentTag: vnodeComponentOptions.tag,
117115
_parentVnode: vnode,
@@ -129,7 +127,7 @@ export function createComponentInstanceForVnode (
129127

130128
function init (vnode: VNodeWithData, hydrating: boolean) {
131129
if (!vnode.child) {
132-
const child = vnode.child = createComponentInstanceForVnode(vnode)
130+
const child = vnode.child = createComponentInstanceForVnode(vnode, activeInstance)
133131
child.$mount(hydrating ? vnode.elm : undefined, hydrating)
134132
}
135133
}

src/core/vdom/create-element.js

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import VNode, { emptyVNode } from './vnode'
44
import config from '../config'
55
import { createComponent } from './create-component'
66
import { normalizeChildren } from './helpers'
7-
import { renderState } from '../instance/render'
87
import { warn, resolveAsset } from '../util/index'
98

109
// wrapper function for providing a more flexible interface
@@ -28,15 +27,6 @@ function _createElement (
2827
data?: VNodeData,
2928
children?: VNodeChildren | void
3029
): VNode | Array<VNode> | void {
31-
const parent: ?Component = renderState.activeInstance
32-
const host = context !== parent ? parent : undefined
33-
if (!parent) {
34-
process.env.NODE_ENV !== 'production' && warn(
35-
'createElement cannot be called outside of component ' +
36-
'render functions.'
37-
)
38-
return
39-
}
4030
if (data && data.__ob__) {
4131
process.env.NODE_ENV !== 'production' && warn(
4232
`Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
@@ -56,22 +46,22 @@ function _createElement (
5646
// platform built-in elements
5747
return new VNode(
5848
tag, data, normalizeChildren(children, ns),
59-
undefined, undefined, ns, context, host
49+
undefined, undefined, ns, context
6050
)
6151
} else if ((Ctor = resolveAsset(context.$options, 'components', tag))) {
6252
// component
63-
return createComponent(Ctor, data, parent, context, host, children, tag)
53+
return createComponent(Ctor, data, context, children, tag)
6454
} else {
6555
// unknown or unlisted namespaced elements
6656
// check at runtime because it may get assigned a namespace when its
6757
// parent normalizes children
6858
return new VNode(
6959
tag, data, normalizeChildren(children, ns),
70-
undefined, undefined, ns, context, host
60+
undefined, undefined, ns, context
7161
)
7262
}
7363
} else {
7464
// direct component options / constructor
75-
return createComponent(tag, data, parent, context, host, children)
65+
return createComponent(tag, data, context, children)
7666
}
7767
}

src/core/vdom/patch.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,9 @@ export function createPatchFunction (backend) {
160160
if (isDef(i = vnode.context) && isDef(i = i.$options._scopeId)) {
161161
nodeOps.setAttribute(vnode.elm, i, '')
162162
}
163-
if (activeInstance !== vnode.context && isDef(i = activeInstance.$options._scopeId)) {
163+
if (isDef(i = activeInstance) &&
164+
i !== vnode.context &&
165+
isDef(i = i.$options._scopeId)) {
164166
nodeOps.setAttribute(vnode.elm, i, '')
165167
}
166168
}

src/core/vdom/vnode.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default class VNode {
88
elm: Node | void;
99
ns: string | void;
1010
context: Component | void; // rendered in this component's scope
11-
host: ?Component; // inserted into this component as children
1211
key: string | number | void;
1312
componentOptions: VNodeComponentOptions | void;
1413
child: Component | void; // component instance
@@ -26,7 +25,6 @@ export default class VNode {
2625
elm?: Node,
2726
ns?: string | void,
2827
context?: Component,
29-
host?: ?Component,
3028
componentOptions?: VNodeComponentOptions
3129
) {
3230
this.tag = tag
@@ -36,7 +34,6 @@ export default class VNode {
3634
this.elm = elm
3735
this.ns = ns
3836
this.context = context
39-
this.host = host
4037
this.key = data && data.key
4138
this.componentOptions = componentOptions
4239
this.child = undefined

src/server/render.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,15 @@ export function createRenderFunction (
9595
}
9696

9797
function renderComponent (node, write, next, isRoot) {
98-
const child = createComponentInstanceForVnode(node)
98+
const prevActive = activeInstance
99+
const child = activeInstance = createComponentInstanceForVnode(node, activeInstance)
99100
normalizeRender(child)
100101
const childNode = child._render()
101102
childNode.parent = node
102-
const prevActive = activeInstance
103-
activeInstance = child
104-
renderNode(childNode, write, next, isRoot)
105-
activeInstance = prevActive
103+
renderNode(childNode, write, () => {
104+
activeInstance = prevActive
105+
next()
106+
}, isRoot)
106107
}
107108

108109
function renderComponentWithCache (node, write, next, isRoot, cache, key) {

test/ssr/ssr-stream.spec.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,24 @@ describe('SSR: renderToStream', () => {
2323
components: {
2424
bComp (resolve) {
2525
return resolve({
26-
render () {
27-
return this.$createElement('test-async-2')
26+
render (h) {
27+
return h('test-async-2')
2828
},
2929
components: {
3030
testAsync2 (resolve) {
3131
return resolve({
3232
created () { this.$parent.$parent.testClass = 'b' },
33-
render () {
34-
return this.$createElement('div', { class: [this.$parent.$parent.testClass] }, 'test')
33+
render (h) {
34+
return h('div', { class: [this.$parent.$parent.testClass] }, 'test')
3535
}
3636
})
3737
}
3838
}
3939
})
4040
},
4141
cComp: {
42-
render () {
43-
return this.$createElement('div', { class: [this.$parent.testClass] }, 'test')
42+
render (h) {
43+
return h('div', { class: [this.$parent.testClass] }, 'test')
4444
}
4545
}
4646
}

test/unit/features/instance/properties.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,32 @@ describe('Instance properties', () => {
5454
}).then(done)
5555
})
5656

57+
it('$parent', () => {
58+
const calls = []
59+
const makeOption = name => ({
60+
name,
61+
template: `<div><slot></slot></div>`,
62+
created () {
63+
calls.push(`${name}:${this.$parent.$options.name}`)
64+
}
65+
})
66+
new Vue({
67+
template: `
68+
<div>
69+
<outer><middle><inner></inner></middle></outer>
70+
<next></next>
71+
</div>
72+
`,
73+
components: {
74+
outer: makeOption('outer'),
75+
middle: makeOption('middle'),
76+
inner: makeOption('inner'),
77+
next: makeOption('next')
78+
}
79+
}).$mount()
80+
expect(calls).toEqual(['outer:undefined', 'middle:outer', 'inner:middle', 'next:undefined'])
81+
})
82+
5783
it('$isServer', () => {
5884
const vm = new Vue()
5985
expect(vm.$isServer).toBe(false)

test/unit/modules/vdom/create-element.spec.js

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
import Vue from 'vue'
2-
import { renderState } from 'core/instance/render'
32
import { createElement } from 'core/vdom/create-element'
43
import { emptyVNode } from 'core/vdom/vnode'
54
import { bind } from 'shared/util'
65

76
describe('create-element', () => {
8-
afterEach(() => {
9-
renderState.activeInstance = null
10-
})
11-
127
it('render vnode with basic reserved tag using createElement', () => {
138
const vm = new Vue({
149
data: { msg: 'hello world' }
1510
})
1611
const h = bind(createElement, vm)
17-
renderState.activeInstance = vm
1812
const vnode = h('p', {})
1913
expect(vnode.tag).toBe('p')
2014
expect(vnode.data).toEqual({})
@@ -35,7 +29,6 @@ describe('create-element', () => {
3529
}
3630
})
3731
const h = bind(createElement, vm)
38-
renderState.activeInstance = vm
3932
const vnode = h('my-component', { props: { msg: vm.message }})
4033
expect(vnode.tag).toMatch(/vue-component-[0-9]+/)
4134
expect(vnode.componentOptions.propsData).toEqual({ msg: vm.message })
@@ -52,7 +45,6 @@ describe('create-element', () => {
5245
})
5346
const h = bind(createElement, vm)
5447
const tag = 'custom-tag'
55-
renderState.activeInstance = vm
5648
const vnode = h(tag, {})
5749
expect(vnode.tag).toBe('custom-tag')
5850
expect(vnode.data).toEqual({})
@@ -69,7 +61,6 @@ describe('create-element', () => {
6961
data: { msg: 'hello world' }
7062
})
7163
const h = bind(createElement, vm)
72-
renderState.activeInstance = vm
7364
const vnode = h(null, {})
7465
expect(vnode).toEqual(emptyVNode())
7566
})
@@ -79,7 +70,6 @@ describe('create-element', () => {
7970
data: { msg: 'hello world' }
8071
})
8172
const h = bind(createElement, vm)
82-
renderState.activeInstance = vm
8373
const vnode = h(Vue.extend({ // Component class
8474
props: ['msg']
8575
}), { props: { msg: vm.message }})
@@ -92,19 +82,9 @@ describe('create-element', () => {
9282
expect(vnode.context).toEqual(vm)
9383
})
9484

95-
it('warn message that createElement cannot called when using createElement', () => {
96-
const vm = new Vue({
97-
data: { msg: 'hello world' }
98-
})
99-
const h = bind(createElement, vm)
100-
h('p', {})
101-
expect('createElement cannot be called outside of component').toHaveBeenWarned()
102-
})
103-
10485
it('render vnode with createElement with children', () => {
10586
const vm = new Vue({})
10687
const h = bind(createElement, vm)
107-
renderState.activeInstance = vm
10888
const vnode = h('p', void 0, [h('br'), 'hello world', h('br')])
10989
expect(vnode.children[0].tag).toBe('br')
11090
expect(vnode.children[1].text).toBe('hello world')
@@ -114,7 +94,6 @@ describe('create-element', () => {
11494
it('render vnode with children, omitting data', () => {
11595
const vm = new Vue({})
11696
const h = bind(createElement, vm)
117-
renderState.activeInstance = vm
11897
const vnode = h('p', [h('br'), 'hello world', h('br')])
11998
expect(vnode.children[0].tag).toBe('br')
12099
expect(vnode.children[1].text).toBe('hello world')
@@ -124,7 +103,6 @@ describe('create-element', () => {
124103
it('render svg elements with correct namespace', () => {
125104
const vm = new Vue({})
126105
const h = bind(createElement, vm)
127-
renderState.activeInstance = vm
128106
const vnode = h('svg', [h('a', [h('foo', [h('bar')])])])
129107
expect(vnode.ns).toBe('svg')
130108
// should apply ns to children recursively
@@ -136,7 +114,6 @@ describe('create-element', () => {
136114
it('render MathML elements with correct namespace', () => {
137115
const vm = new Vue({})
138116
const h = bind(createElement, vm)
139-
renderState.activeInstance = vm
140117
const vnode = h('math', [h('matrix')])
141118
expect(vnode.ns).toBe('math')
142119
// should apply ns to children

0 commit comments

Comments
 (0)