+149
-20
lines changedFilter options
+149
-20
lines changed Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ declare interface Component {
20
20
// public properties
21
21
$el: any; // so that we can attach __vue__ to it
22
22
$data: Object;
23
+
$props: Object;
23
24
$options: ComponentOptions;
24
25
$parent: Component | void;
25
26
$root: Component;
@@ -28,8 +29,9 @@ declare interface Component {
28
29
$slots: { [key: string]: Array<VNode> };
29
30
$scopedSlots: { [key: string]: () => VNodeChildren };
30
31
$vnode: VNode; // the placeholder node for the component in parent's render tree
32
+
$attrs: ?{ [key: string] : string };
33
+
$listeners: ?{ [key: string]: Function | Array<Function> };
31
34
$isServer: boolean;
32
-
$props: Object;
33
35
34
36
// public methods
35
37
$mount: (el?: Element | string, hydrating?: boolean) => Component;
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ import {
18
18
} from '../util/index'
19
19
20
20
export let activeInstance: any = null
21
+
export let isUpdatingChildComponent: boolean = false
21
22
22
23
export function initLifecycle (vm: Component) {
23
24
const options = vm.$options
@@ -207,6 +208,10 @@ export function updateChildComponent (
207
208
parentVnode: VNode,
208
209
renderChildren: ?Array<VNode>
209
210
) {
211
+
if (process.env.NODE_ENV !== 'production') {
212
+
isUpdatingChildComponent = true
213
+
}
214
+
210
215
// determine whether component has slot children
211
216
// we need to do this before overwriting $options._renderChildren
212
217
const hasChildren = !!(
@@ -218,30 +223,32 @@ export function updateChildComponent (
218
223
219
224
vm.$options._parentVnode = parentVnode
220
225
vm.$vnode = parentVnode // update vm's placeholder node without re-render
226
+
221
227
if (vm._vnode) { // update child tree's parent
222
228
vm._vnode.parent = parentVnode
223
229
}
224
230
vm.$options._renderChildren = renderChildren
225
231
232
+
// update $attrs and $listensers hash
233
+
// these are also reactive so they may trigger child update if the child
234
+
// used them during render
235
+
vm.$attrs = parentVnode.data && parentVnode.data.attrs
236
+
vm.$listeners = listeners
237
+
226
238
// update props
227
239
if (propsData && vm.$options.props) {
228
240
observerState.shouldConvert = false
229
-
if (process.env.NODE_ENV !== 'production') {
230
-
observerState.isSettingProps = true
231
-
}
232
241
const props = vm._props
233
242
const propKeys = vm.$options._propKeys || []
234
243
for (let i = 0; i < propKeys.length; i++) {
235
244
const key = propKeys[i]
236
245
props[key] = validateProp(key, vm.$options.props, propsData, vm)
237
246
}
238
247
observerState.shouldConvert = true
239
-
if (process.env.NODE_ENV !== 'production') {
240
-
observerState.isSettingProps = false
241
-
}
242
248
// keep a copy of raw propsData
243
249
vm.$options.propsData = propsData
244
250
}
251
+
245
252
// update listeners
246
253
if (listeners) {
247
254
const oldListeners = vm.$options._parentListeners
@@ -253,6 +260,10 @@ export function updateChildComponent (
253
260
vm.$slots = resolveSlots(renderChildren, parentVnode.context)
254
261
vm.$forceUpdate()
255
262
}
263
+
264
+
if (process.env.NODE_ENV !== 'production') {
265
+
isUpdatingChildComponent = false
266
+
}
256
267
}
257
268
258
269
function isInInactiveTree (vm) {
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@ import {
8
8
looseEqual,
9
9
emptyObject,
10
10
handleError,
11
-
looseIndexOf
11
+
looseIndexOf,
12
+
defineReactive
12
13
} from '../util/index'
13
14
14
15
import VNode, {
@@ -17,6 +18,8 @@ import VNode, {
17
18
createEmptyVNode
18
19
} from '../vdom/vnode'
19
20
21
+
import { isUpdatingChildComponent } from './lifecycle'
22
+
20
23
import { createElement } from '../vdom/create-element'
21
24
import { renderList } from './render-helpers/render-list'
22
25
import { renderSlot } from './render-helpers/render-slot'
@@ -42,6 +45,21 @@ export function initRender (vm: Component) {
42
45
// normalization is always applied for the public version, used in
43
46
// user-written render functions.
44
47
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
48
+
49
+
// $attrs & $listeners are exposed for easier HOC creation.
50
+
// they need to be reactive so that HOCs using them are always updated
51
+
const parentData = parentVnode && parentVnode.data
52
+
if (process.env.NODE_ENV !== 'production') {
53
+
defineReactive(vm, '$attrs', parentData && parentData.attrs, () => {
54
+
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
55
+
}, true)
56
+
defineReactive(vm, '$listeners', parentData && parentData.on, () => {
57
+
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
58
+
}, true)
59
+
} else {
60
+
defineReactive(vm, '$attrs', parentData && parentData.attrs, null, true)
61
+
defineReactive(vm, '$listeners', parentData && parentData.on, null, true)
62
+
}
45
63
}
46
64
47
65
export function renderMixin (Vue: Class<Component>) {
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
3
3
import config from '../config'
4
4
import Dep from '../observer/dep'
5
5
import Watcher from '../observer/watcher'
6
+
import { isUpdatingChildComponent } from './lifecycle'
6
7
7
8
import {
8
9
set,
@@ -86,7 +87,7 @@ function initProps (vm: Component, propsOptions: Object) {
86
87
)
87
88
}
88
89
defineReactive(props, key, value, () => {
89
-
if (vm.$parent && !observerState.isSettingProps) {
90
+
if (vm.$parent && !isUpdatingChildComponent) {
90
91
warn(
91
92
`Avoid mutating a prop directly since the value will be ` +
92
93
`overwritten whenever the parent component re-renders. ` +
Original file line number Diff line number Diff line change
@@ -22,8 +22,7 @@ const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
22
22
* under a frozen data structure. Converting it would defeat the optimization.
23
23
*/
24
24
export const observerState = {
25
-
shouldConvert: true,
26
-
isSettingProps: false
25
+
shouldConvert: true
27
26
}
28
27
29
28
/**
@@ -133,7 +132,8 @@ export function defineReactive (
133
132
obj: Object,
134
133
key: string,
135
134
val: any,
136
-
customSetter?: Function
135
+
customSetter?: ?Function,
136
+
shallow?: boolean
137
137
) {
138
138
const dep = new Dep()
139
139
@@ -146,7 +146,7 @@ export function defineReactive (
146
146
const getter = property && property.get
147
147
const setter = property && property.set
148
148
149
-
let childOb = observe(val)
149
+
let childOb = !shallow && observe(val)
150
150
Object.defineProperty(obj, key, {
151
151
enumerable: true,
152
152
configurable: true,
@@ -178,7 +178,7 @@ export function defineReactive (
178
178
} else {
179
179
val = newVal
180
180
}
181
-
childOb = observe(newVal)
181
+
childOb = !shallow && observe(newVal)
182
182
dep.notify()
183
183
}
184
184
})
Original file line number Diff line number Diff line change
@@ -18,6 +18,10 @@ import {
18
18
} from 'web/util/index'
19
19
20
20
function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) {
21
+
const opts = vnode.componentOptions
22
+
if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {
23
+
return
24
+
}
21
25
if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {
22
26
return
23
27
}
Original file line number Diff line number Diff line change
@@ -664,7 +664,6 @@ describe('Directive v-on', () => {
664
664
@click="click"
665
665
@mousedown="mousedown"
666
666
@mouseup.native="mouseup">
667
-
hello
668
667
</foo-button>
669
668
`,
670
669
methods: {
@@ -675,11 +674,7 @@ describe('Directive v-on', () => {
675
674
components: {
676
675
fooButton: {
677
676
template: `
678
-
<button
679
-
v-bind="$vnode.data.attrs"
680
-
v-on="$vnode.data.on">
681
-
<slot/>
682
-
</button>
677
+
<button v-on="$listeners"></button>
683
678
`
684
679
}
685
680
}
Original file line number Diff line number Diff line change
@@ -125,4 +125,61 @@ describe('Instance properties', () => {
125
125
}).$mount()
126
126
expect(`Avoid mutating a prop`).toHaveBeenWarned()
127
127
})
128
+
129
+
it('$attrs', done => {
130
+
const vm = new Vue({
131
+
template: `<foo :id="foo" bar="1"/>`,
132
+
data: { foo: 'foo' },
133
+
components: {
134
+
foo: {
135
+
props: ['bar'],
136
+
template: `<div><div v-bind="$attrs"></div></div>`
137
+
}
138
+
}
139
+
}).$mount()
140
+
expect(vm.$el.children[0].id).toBe('foo')
141
+
expect(vm.$el.children[0].hasAttribute('bar')).toBe(false)
142
+
vm.foo = 'bar'
143
+
waitForUpdate(() => {
144
+
expect(vm.$el.children[0].id).toBe('bar')
145
+
expect(vm.$el.children[0].hasAttribute('bar')).toBe(false)
146
+
}).then(done)
147
+
})
148
+
149
+
it('warn mutating $attrs', () => {
150
+
const vm = new Vue()
151
+
vm.$attrs = {}
152
+
expect(`$attrs is readonly`).toHaveBeenWarned()
153
+
})
154
+
155
+
it('$listeners', done => {
156
+
const spyA = jasmine.createSpy('A')
157
+
const spyB = jasmine.createSpy('B')
158
+
const vm = new Vue({
159
+
template: `<foo @click="foo"/>`,
160
+
data: { foo: spyA },
161
+
components: {
162
+
foo: {
163
+
template: `<div v-on="$listeners"></div>`
164
+
}
165
+
}
166
+
}).$mount()
167
+
168
+
triggerEvent(vm.$el, 'click')
169
+
expect(spyA.calls.count()).toBe(1)
170
+
expect(spyB.calls.count()).toBe(0)
171
+
172
+
vm.foo = spyB
173
+
waitForUpdate(() => {
174
+
triggerEvent(vm.$el, 'click')
175
+
expect(spyA.calls.count()).toBe(1)
176
+
expect(spyB.calls.count()).toBe(1)
177
+
}).then(done)
178
+
})
179
+
180
+
it('warn mutating $listeners', () => {
181
+
const vm = new Vue()
182
+
vm.$listeners = {}
183
+
expect(`$listeners is readonly`).toHaveBeenWarned()
184
+
})
128
185
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
1
+
import Vue from 'vue'
2
+
3
+
describe('Options inheritAttrs', () => {
4
+
it('should work', done => {
5
+
const vm = new Vue({
6
+
template: `<foo :id="foo"/>`,
7
+
data: { foo: 'foo' },
8
+
components: {
9
+
foo: {
10
+
inheritAttrs: false,
11
+
template: `<div>foo</div>`
12
+
}
13
+
}
14
+
}).$mount()
15
+
expect(vm.$el.id).toBe('')
16
+
vm.foo = 'bar'
17
+
waitForUpdate(() => {
18
+
expect(vm.$el.id).toBe('')
19
+
}).then(done)
20
+
})
21
+
22
+
it('with inner v-bind', done => {
23
+
const vm = new Vue({
24
+
template: `<foo :id="foo"/>`,
25
+
data: { foo: 'foo' },
26
+
components: {
27
+
foo: {
28
+
inheritAttrs: false,
29
+
template: `<div><div v-bind="$attrs"></div></div>`
30
+
}
31
+
}
32
+
}).$mount()
33
+
expect(vm.$el.children[0].id).toBe('foo')
34
+
vm.foo = 'bar'
35
+
waitForUpdate(() => {
36
+
expect(vm.$el.children[0].id).toBe('bar')
37
+
}).then(done)
38
+
})
39
+
})
Original file line number Diff line number Diff line change
@@ -45,6 +45,8 @@ export declare class Vue {
45
45
readonly $ssrContext: any;
46
46
readonly $props: any;
47
47
readonly $vnode: VNode;
48
+
readonly $attrs: { [key: string] : string } | void;
49
+
readonly $listeners: { [key: string]: Function | Array<Function> } | void;
48
50
49
51
$mount(elementOrSelector?: Element | String, hydrating?: boolean): this;
50
52
$forceUpdate(): void;
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4