vue 2.7.5 → 2.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@ import {
13
13
  ASTText,
14
14
  CompilerOptions
15
15
  } from 'types/compiler'
16
- import { BindingMetadata } from 'sfc/types'
16
+ import { BindingMetadata, BindingTypes } from 'sfc/types'
17
17
 
18
18
  type TransformFunction = (el: ASTElement, code: string) => string
19
19
  type DataGenFunction = (el: ASTElement) => string
@@ -95,18 +95,16 @@ export function genElement(el: ASTElement, state: CodegenState): string {
95
95
  code = genComponent(el.component, el, state)
96
96
  } else {
97
97
  let data
98
- if (!el.plain || (el.pre && state.maybeComponent(el))) {
98
+ const maybeComponent = state.maybeComponent(el)
99
+ if (!el.plain || (el.pre && maybeComponent)) {
99
100
  data = genData(el, state)
100
101
  }
101
102
 
102
103
  let tag: string | undefined
103
104
  // check if this is a component in <script setup>
104
105
  const bindings = state.options.bindings
105
- if (bindings && bindings.__isScriptSetup !== false) {
106
- tag =
107
- checkBindingType(bindings, el.tag) ||
108
- checkBindingType(bindings, camelize(el.tag)) ||
109
- checkBindingType(bindings, capitalize(camelize(el.tag)))
106
+ if (maybeComponent && bindings && bindings.__isScriptSetup !== false) {
107
+ tag = checkBindingType(bindings, el.tag)
110
108
  }
111
109
  if (!tag) tag = `'${el.tag}'`
112
110
 
@@ -126,9 +124,32 @@ export function genElement(el: ASTElement, state: CodegenState): string {
126
124
  }
127
125
 
128
126
  function checkBindingType(bindings: BindingMetadata, key: string) {
129
- const type = bindings[key]
130
- if (type && type.startsWith('setup')) {
131
- return key
127
+ const camelName = camelize(key)
128
+ const PascalName = capitalize(camelName)
129
+ const checkType = (type) => {
130
+ if (bindings[key] === type) {
131
+ return key
132
+ }
133
+ if (bindings[camelName] === type) {
134
+ return camelName
135
+ }
136
+ if (bindings[PascalName] === type) {
137
+ return PascalName
138
+ }
139
+ }
140
+ const fromConst =
141
+ checkType(BindingTypes.SETUP_CONST) ||
142
+ checkType(BindingTypes.SETUP_REACTIVE_CONST)
143
+ if (fromConst) {
144
+ return fromConst
145
+ }
146
+
147
+ const fromMaybeRef =
148
+ checkType(BindingTypes.SETUP_LET) ||
149
+ checkType(BindingTypes.SETUP_REF) ||
150
+ checkType(BindingTypes.SETUP_MAYBE_REF)
151
+ if (fromMaybeRef) {
152
+ return fromMaybeRef
132
153
  }
133
154
  }
134
155
 
@@ -1,8 +1,7 @@
1
1
  import { warn, hasSymbol, isFunction, isObject } from '../util/index'
2
2
  import { defineReactive, toggleObserving } from '../observer/index'
3
3
  import type { Component } from 'types/component'
4
- import { provide } from 'v3/apiInject'
5
- import { setCurrentInstance } from '../../v3/currentInstance'
4
+ import { resolveProvided } from 'v3/apiInject'
6
5
 
7
6
  export function initProvide(vm: Component) {
8
7
  const provideOption = vm.$options.provide
@@ -13,12 +12,18 @@ export function initProvide(vm: Component) {
13
12
  if (!isObject(provided)) {
14
13
  return
15
14
  }
15
+ const source = resolveProvided(vm)
16
+ // IE9 doesn't support Object.getOwnPropertyDescriptors so we have to
17
+ // iterate the keys ourselves.
16
18
  const keys = hasSymbol ? Reflect.ownKeys(provided) : Object.keys(provided)
17
- setCurrentInstance(vm)
18
19
  for (let i = 0; i < keys.length; i++) {
19
- provide(keys[i], provided[keys[i]])
20
+ const key = keys[i]
21
+ Object.defineProperty(
22
+ source,
23
+ key,
24
+ Object.getOwnPropertyDescriptor(provided, key)!
25
+ )
20
26
  }
21
- setCurrentInstance()
22
27
  }
23
28
  }
24
29
 
@@ -18,7 +18,7 @@ import {
18
18
  invokeWithErrorHandling
19
19
  } from '../util/index'
20
20
  import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
21
- import { syncSetupAttrs } from 'v3/apiSetup'
21
+ import { syncSetupProxy } from 'v3/apiSetup'
22
22
 
23
23
  export let activeInstance: any = null
24
24
  export let isUpdatingChildComponent: boolean = false
@@ -288,11 +288,12 @@ export function updateChildComponent(
288
288
  // force update if attrs are accessed and has changed since it may be
289
289
  // passed to a child component.
290
290
  if (
291
- syncSetupAttrs(
291
+ syncSetupProxy(
292
292
  vm._attrsProxy,
293
293
  attrs,
294
294
  (prevVNode.data && prevVNode.data.attrs) || emptyObject,
295
- vm
295
+ vm,
296
+ '$attrs'
296
297
  )
297
298
  ) {
298
299
  needsForceUpdate = true
@@ -300,7 +301,20 @@ export function updateChildComponent(
300
301
  }
301
302
  vm.$attrs = attrs
302
303
 
303
- vm.$listeners = listeners || emptyObject
304
+ // update listeners
305
+ listeners = listeners || emptyObject
306
+ const prevListeners = vm.$options._parentListeners
307
+ if (vm._listenersProxy) {
308
+ syncSetupProxy(
309
+ vm._listenersProxy,
310
+ listeners,
311
+ prevListeners || emptyObject,
312
+ vm,
313
+ '$listeners'
314
+ )
315
+ }
316
+ vm.$listeners = vm.$options._parentListeners = listeners
317
+ updateComponentListeners(vm, listeners, prevListeners)
304
318
 
305
319
  // update props
306
320
  if (propsData && vm.$options.props) {
@@ -317,12 +331,6 @@ export function updateChildComponent(
317
331
  vm.$options.propsData = propsData
318
332
  }
319
333
 
320
- // update listeners
321
- listeners = listeners || emptyObject
322
- const oldListeners = vm.$options._parentListeners
323
- vm.$options._parentListeners = listeners
324
- updateComponentListeners(vm, listeners, oldListeners)
325
-
326
334
  // resolve slots + force update if has children
327
335
  if (needsForceUpdate) {
328
336
  vm.$slots = resolveSlots(renderChildren, parentVnode.context)
@@ -25,7 +25,13 @@ export function initRender(vm: Component) {
25
25
  const parentVnode = (vm.$vnode = options._parentVnode!) // the placeholder node in parent tree
26
26
  const renderContext = parentVnode && (parentVnode.context as Component)
27
27
  vm.$slots = resolveSlots(options._renderChildren, renderContext)
28
- vm.$scopedSlots = emptyObject
28
+ vm.$scopedSlots = parentVnode
29
+ ? normalizeScopedSlots(
30
+ vm.$parent!,
31
+ parentVnode.data!.scopedSlots,
32
+ vm.$slots
33
+ )
34
+ : emptyObject
29
35
  // bind the createElement fn to this instance
30
36
  // so that we get proper render context inside it.
31
37
  // args order: tag, data, children, normalizationType, alwaysNormalize
@@ -98,7 +104,7 @@ export function renderMixin(Vue: typeof Component) {
98
104
  const vm: Component = this
99
105
  const { render, _parentVnode } = vm.$options
100
106
 
101
- if (_parentVnode) {
107
+ if (_parentVnode && vm._isMounted) {
102
108
  vm.$scopedSlots = normalizeScopedSlots(
103
109
  vm.$parent!,
104
110
  _parentVnode.data!.scopedSlots,
@@ -191,7 +191,7 @@ export function defineReactive(
191
191
  } else if (getter) {
192
192
  // #7981: for accessor properties without setter
193
193
  return
194
- } else if (isRef(value) && !isRef(newVal)) {
194
+ } else if (!shallow && isRef(value) && !isRef(newVal)) {
195
195
  value.value = newVal
196
196
  return
197
197
  } else {
@@ -59,6 +59,15 @@ if (inBrowser && !isIE) {
59
59
  }
60
60
  }
61
61
 
62
+ const sortCompareFn = (a: Watcher, b: Watcher): number => {
63
+ if (a.post) {
64
+ if (!b.post) return 1
65
+ } else if (b.post) {
66
+ return -1
67
+ }
68
+ return a.id - b.id
69
+ }
70
+
62
71
  /**
63
72
  * Flush both queues and run the watchers.
64
73
  */
@@ -75,7 +84,7 @@ function flushSchedulerQueue() {
75
84
  // user watchers are created before the render watcher)
76
85
  // 3. If a component is destroyed during a parent component's watcher run,
77
86
  // its watchers can be skipped.
78
- queue.sort((a, b) => a.id - b.id)
87
+ queue.sort(sortCompareFn)
79
88
 
80
89
  // do not cache length because more watchers might be pushed
81
90
  // as we run existing watchers
@@ -58,6 +58,7 @@ export default class Watcher implements DepTarget {
58
58
  noRecurse?: boolean
59
59
  getter: Function
60
60
  value: any
61
+ post: boolean
61
62
 
62
63
  // dev only
63
64
  onTrack?: ((event: DebuggerEvent) => void) | undefined
@@ -93,6 +94,7 @@ export default class Watcher implements DepTarget {
93
94
  this.cb = cb
94
95
  this.id = ++uid // uid for batching
95
96
  this.active = true
97
+ this.post = false
96
98
  this.dirty = this.lazy // for lazy watchers
97
99
  this.deps = []
98
100
  this.newDeps = []
@@ -111,6 +111,7 @@ export declare class Component {
111
111
  _setupProxy?: Record<string, any>
112
112
  _setupContext?: SetupContext
113
113
  _attrsProxy?: Record<string, any>
114
+ _listenersProxy?: Record<string, Function | Function[]>
114
115
  _slotsProxy?: Record<string, () => VNode[]>
115
116
  _preWatchers?: Watcher[]
116
117
 
@@ -1,5 +1,6 @@
1
1
  import { isFunction, warn } from 'core/util'
2
2
  import { currentInstance } from './currentInstance'
3
+ import type { Component } from 'types/component'
3
4
 
4
5
  export interface InjectionKey<T> extends Symbol {}
5
6
 
@@ -9,19 +10,23 @@ export function provide<T>(key: InjectionKey<T> | string | number, value: T) {
9
10
  warn(`provide() can only be used inside setup().`)
10
11
  }
11
12
  } else {
12
- let provides = currentInstance._provided
13
- // by default an instance inherits its parent's provides object
14
- // but when it needs to provide values of its own, it creates its
15
- // own provides object using parent provides object as prototype.
16
- // this way in `inject` we can simply look up injections from direct
17
- // parent and let the prototype chain do the work.
18
- const parentProvides =
19
- currentInstance.$parent && currentInstance.$parent._provided
20
- if (parentProvides === provides) {
21
- provides = currentInstance._provided = Object.create(parentProvides)
22
- }
23
13
  // TS doesn't allow symbol as index type
24
- provides[key as string] = value
14
+ resolveProvided(currentInstance)[key as string] = value
15
+ }
16
+ }
17
+
18
+ export function resolveProvided(vm: Component): Record<string, any> {
19
+ // by default an instance inherits its parent's provides object
20
+ // but when it needs to provide values of its own, it creates its
21
+ // own provides object using parent provides object as prototype.
22
+ // this way in `inject` we can simply look up injections from direct
23
+ // parent and let the prototype chain do the work.
24
+ const existing = vm._provided
25
+ const parentProvides = vm.$parent && vm.$parent._provided
26
+ if (parentProvides === existing) {
27
+ return (vm._provided = Object.create(parentProvides))
28
+ } else {
29
+ return existing
25
30
  }
26
31
  }
27
32
 
@@ -19,6 +19,7 @@ import { proxyWithRefUnwrap } from './reactivity/ref'
19
19
  */
20
20
  export interface SetupContext {
21
21
  attrs: Record<string, any>
22
+ listeners: Record<string, Function | Function[]>
22
23
  slots: Record<string, () => VNode[]>
23
24
  emit: (event: string, ...args: any[]) => any
24
25
  expose: (exposed: Record<string, any>) => void
@@ -87,7 +88,19 @@ function createSetupContext(vm: Component): SetupContext {
87
88
  let exposeCalled = false
88
89
  return {
89
90
  get attrs() {
90
- return initAttrsProxy(vm)
91
+ if (!vm._attrsProxy) {
92
+ const proxy = (vm._attrsProxy = {})
93
+ def(proxy, '_v_attr_proxy', true)
94
+ syncSetupProxy(proxy, vm.$attrs, emptyObject, vm, '$attrs')
95
+ }
96
+ return vm._attrsProxy
97
+ },
98
+ get listeners() {
99
+ if (!vm._listenersProxy) {
100
+ const proxy = (vm._listenersProxy = {})
101
+ syncSetupProxy(proxy, vm.$listeners, emptyObject, vm, '$listeners')
102
+ }
103
+ return vm._listenersProxy
91
104
  },
92
105
  get slots() {
93
106
  return initSlotsProxy(vm)
@@ -109,26 +122,18 @@ function createSetupContext(vm: Component): SetupContext {
109
122
  }
110
123
  }
111
124
 
112
- function initAttrsProxy(vm: Component) {
113
- if (!vm._attrsProxy) {
114
- const proxy = (vm._attrsProxy = {})
115
- def(proxy, '_v_attr_proxy', true)
116
- syncSetupAttrs(proxy, vm.$attrs, emptyObject, vm)
117
- }
118
- return vm._attrsProxy
119
- }
120
-
121
- export function syncSetupAttrs(
125
+ export function syncSetupProxy(
122
126
  to: any,
123
127
  from: any,
124
128
  prev: any,
125
- instance: Component
129
+ instance: Component,
130
+ type: string
126
131
  ) {
127
132
  let changed = false
128
133
  for (const key in from) {
129
134
  if (!(key in to)) {
130
135
  changed = true
131
- defineProxyAttr(to, key, instance)
136
+ defineProxyAttr(to, key, instance, type)
132
137
  } else if (from[key] !== prev[key]) {
133
138
  changed = true
134
139
  }
@@ -142,12 +147,17 @@ export function syncSetupAttrs(
142
147
  return changed
143
148
  }
144
149
 
145
- function defineProxyAttr(proxy: any, key: string, instance: Component) {
150
+ function defineProxyAttr(
151
+ proxy: any,
152
+ key: string,
153
+ instance: Component,
154
+ type: string
155
+ ) {
146
156
  Object.defineProperty(proxy, key, {
147
157
  enumerable: true,
148
158
  configurable: true,
149
159
  get() {
150
- return instance.$attrs[key]
160
+ return instance[type][key]
151
161
  }
152
162
  })
153
163
  }
@@ -171,19 +181,30 @@ export function syncSetupSlots(to: any, from: any) {
171
181
  }
172
182
 
173
183
  /**
174
- * @internal use manual type def
184
+ * @internal use manual type def because public setup context type relies on
185
+ * legacy VNode types
175
186
  */
176
187
  export function useSlots(): SetupContext['slots'] {
177
188
  return getContext().slots
178
189
  }
179
190
 
180
191
  /**
181
- * @internal use manual type def
192
+ * @internal use manual type def because public setup context type relies on
193
+ * legacy VNode types
182
194
  */
183
195
  export function useAttrs(): SetupContext['attrs'] {
184
196
  return getContext().attrs
185
197
  }
186
198
 
199
+ /**
200
+ * Vue 2 only
201
+ * @internal use manual type def because public setup context type relies on
202
+ * legacy VNode types
203
+ */
204
+ export function useListeners(): SetupContext['listeners'] {
205
+ return getContext().listeners
206
+ }
207
+
187
208
  function getContext(): SetupContext {
188
209
  if (__DEV__ && !currentInstance) {
189
210
  warn(`useContext() called without active instance.`)
@@ -313,7 +313,7 @@ function doWatch(
313
313
  if (flush === 'sync') {
314
314
  watcher.update = watcher.run
315
315
  } else if (flush === 'post') {
316
- watcher.id = Infinity
316
+ watcher.post = true
317
317
  watcher.update = () => queueWatcher(watcher)
318
318
  } else {
319
319
  // pre
package/src/v3/index.ts CHANGED
@@ -77,7 +77,7 @@ export { provide, inject, InjectionKey } from './apiInject'
77
77
 
78
78
  export { h } from './h'
79
79
  export { getCurrentInstance } from './currentInstance'
80
- export { useSlots, useAttrs, mergeDefaults } from './apiSetup'
80
+ export { useSlots, useAttrs, useListeners, mergeDefaults } from './apiSetup'
81
81
  export { nextTick } from 'core/util/next-tick'
82
82
  export { set, del } from 'core/observer'
83
83
 
package/types/index.d.ts CHANGED
@@ -42,7 +42,7 @@ export * from './v3-setup-helpers'
42
42
  export { Data } from './common'
43
43
  export { SetupContext } from './v3-setup-context'
44
44
  export { defineComponent } from './v3-define-component'
45
- // export { defineAsyncComponent } from './defineAsyncComponent'
45
+ export { defineAsyncComponent } from './v3-define-async-component'
46
46
  export {
47
47
  SetupFunction,
48
48
  // v2 already has option with same name and it's for a single computed
@@ -179,9 +179,7 @@ interface Vue3Instance<
179
179
  ? Partial<Defaults> & Omit<P & PublicProps, keyof Defaults>
180
180
  : P & PublicProps
181
181
  >,
182
- ComponentPublicInstance | null,
183
182
  ComponentPublicInstance,
184
- ComponentPublicInstance[],
185
183
  Options & MergedComponentOptionsOverride,
186
184
  EmitFn<E>
187
185
  > {}
@@ -364,6 +364,8 @@ export declare function useCssModule(name?: string): Record<string, string>;
364
364
  */
365
365
  export declare function useCssVars(getter: (vm: Record<string, any>, setupProxy: Record<string, any>) => Record<string, string>): void;
366
366
 
367
+ /* Excluded from this release type: useListeners */
368
+
367
369
  /* Excluded from this release type: useSlots */
368
370
 
369
371
  /**
@@ -5,6 +5,6 @@ export function getCurrentInstance(): { proxy: Vue } | null
5
5
 
6
6
  export const h: CreateElement
7
7
 
8
- export function useAttrs(): SetupContext['attrs']
9
-
10
8
  export function useSlots(): SetupContext['slots']
9
+ export function useAttrs(): SetupContext['attrs']
10
+ export function useListeners(): SetupContext['listeners']
@@ -31,6 +31,11 @@ export type EmitFn<
31
31
 
32
32
  export interface SetupContext<E extends EmitsOptions = {}> {
33
33
  attrs: Data
34
+ /**
35
+ * Equivalent of `this.$listeners`, which is Vue 2 only.
36
+ */
37
+ listeners: Record<string, Function | Function[]>
34
38
  slots: Slots
35
39
  emit: EmitFn<E>
40
+ expose(exposed?: Record<string, any>): void
36
41
  }
package/types/vue.d.ts CHANGED
@@ -35,28 +35,33 @@ export interface CreateElement {
35
35
  ): VNode
36
36
  }
37
37
 
38
+ type NeverFallback<T, D> = [T] extends [never] ? D : T
39
+
38
40
  export interface Vue<
39
41
  Data = Record<string, any>,
40
42
  Props = Record<string, any>,
41
- Parent = never,
42
- Root = never,
43
- Children = never,
43
+ Instance = never,
44
44
  Options = never,
45
45
  Emit = (event: string, ...args: any[]) => Vue
46
46
  > {
47
47
  // properties with different types in defineComponent()
48
48
  readonly $data: Data
49
49
  readonly $props: Props
50
- readonly $parent: Parent extends never ? Vue : Parent
51
- readonly $root: Root extends never ? Vue : Root
52
- readonly $children: Children extends never ? Vue[] : Children
53
- readonly $options: ComponentOptions<Vue>
50
+ readonly $parent: NeverFallback<Instance, Vue> | null
51
+ readonly $root: NeverFallback<Instance, Vue>
52
+ readonly $children: NeverFallback<Instance, Vue>[]
53
+ readonly $options: NeverFallback<Options, ComponentOptions<Vue>>
54
54
  $emit: Emit
55
55
 
56
56
  // Vue 2 only or shared
57
57
  readonly $el: Element
58
58
  readonly $refs: {
59
- [key: string]: Vue | Element | (Vue | Element)[] | undefined
59
+ [key: string]:
60
+ | NeverFallback<Instance, Vue>
61
+ | Vue
62
+ | Element
63
+ | (NeverFallback<Instance, Vue> | Vue | Element)[]
64
+ | undefined
60
65
  }
61
66
  readonly $slots: { [key: string]: VNode[] | undefined }
62
67
  readonly $scopedSlots: { [key: string]: NormalizedScopedSlot | undefined }