From 00458cd38d209410d3c675729230a42a0a34a4b9 Mon Sep 17 00:00:00 2001 From: jf-paradis <89814003+jf-paradis@users.noreply.github.com> Date: Mon, 8 Aug 2022 17:50:53 -0700 Subject: [PATCH 01/41] fix(types): Make SetupBindings optional on ExtendedVue and CombinedVueInstance (#12727) fix #12726 fix #12717 --- types/vue.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/vue.d.ts b/types/vue.d.ts index 74bce2c8a45..8c91bc88a2f 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -100,7 +100,7 @@ export type CombinedVueInstance< Methods, Computed, Props, - SetupBindings + SetupBindings = {} > = Data & Methods & Computed & @@ -114,7 +114,7 @@ export type ExtendedVue< Methods, Computed, Props, - SetupBindings + SetupBindings = {} > = VueConstructor< CombinedVueInstance & Vue From 5c742eb2e0d8dad268fb29ed4f92d286b5e0f4b5 Mon Sep 17 00:00:00 2001 From: Jonas <30421456+jonaskuske@users.noreply.github.com> Date: Mon, 15 Aug 2022 03:37:08 +0200 Subject: [PATCH 02/41] fix(compiler-sfc): allow full hostnames in asset url base (#12732) fix #12731 --- .../compiler-sfc/src/templateCompilerModules/utils.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/compiler-sfc/src/templateCompilerModules/utils.ts b/packages/compiler-sfc/src/templateCompilerModules/utils.ts index 3e635b00207..8a2d19c6ce7 100644 --- a/packages/compiler-sfc/src/templateCompilerModules/utils.ts +++ b/packages/compiler-sfc/src/templateCompilerModules/utils.ts @@ -24,10 +24,15 @@ export function urlToRequire( // does not apply to absolute urls or urls that start with `@` // since they are aliases if (firstChar === '.' || firstChar === '~') { + // Allow for full hostnames provided in options.base + const base = parseUriParts(transformAssetUrlsOption.base) + const protocol = base.protocol || '' + const host = base.host ? protocol + '//' + base.host : '' + const basePath = base.path || '/' // when packaged in the browser, path will be using the posix- // only version provided by rollup-plugin-node-builtins. - return `"${(path.posix || path).join( - transformAssetUrlsOption.base, + return `"${host}${(path.posix || path).join( + basePath, uriParts.path + (uriParts.hash || '') )}"` } @@ -64,7 +69,7 @@ function parseUriParts(urlString: string): UrlWithStringQuery { // @see https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost if ('string' === typeof urlString) { // check is an uri - return uriParse(urlString) // take apart the uri + return uriParse(urlString, false, true) // take apart the uri } } return returnValue From 4b37b568c7c3fd238aa61fcc956f882223f8e87f Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Aug 2022 09:53:12 +0800 Subject: [PATCH 03/41] fix(types): fix options suggestions when using defineComponent functional component overloads should be moved last fix #12736 --- types/v3-define-component.d.ts | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/types/v3-define-component.d.ts b/types/v3-define-component.d.ts index 30f7046e403..a2c47322b49 100644 --- a/types/v3-define-component.d.ts +++ b/types/v3-define-component.d.ts @@ -67,30 +67,6 @@ type DefineComponent< props: PropsOrPropOptions } -/** - * overload 0.0: functional component with array props - */ -export function defineComponent< - PropNames extends string, - Props = Readonly<{ [key in PropNames]?: any }> ->(options: { - functional: true - props?: PropNames[] - render?: (h: CreateElement, context: RenderContext) => any -}): DefineComponent - -/** - * overload 0.1: functional component with object props - */ -export function defineComponent< - PropsOptions extends ComponentPropsOptions = ComponentPropsOptions, - Props = ExtractPropTypes ->(options: { - functional: true - props?: PropsOptions - render?: (h: CreateElement, context: RenderContext) => any -}): DefineComponent - /** * overload 1: object format with no props */ @@ -104,7 +80,7 @@ export function defineComponent< Emits extends EmitsOptions = {}, EmitsNames extends string = string >( - options: ComponentOptionsWithoutProps< + options: { functional?: never } & ComponentOptionsWithoutProps< {}, RawBindings, D, @@ -135,7 +111,7 @@ export function defineComponent< EmitsNames extends string = string, PropsOptions extends ComponentPropsOptions = ComponentPropsOptions >( - options: ComponentOptionsWithArrayProps< + options: { functional?: never } & ComponentOptionsWithArrayProps< PropNames, RawBindings, D, @@ -175,7 +151,7 @@ export function defineComponent< PropsOptions extends ComponentPropsOptions = ComponentPropsOptions >( options: HasDefined extends true - ? ComponentOptionsWithProps< + ? { functional?: never } & ComponentOptionsWithProps< PropsOptions, RawBindings, D, @@ -187,7 +163,7 @@ export function defineComponent< EmitsNames, Props > - : ComponentOptionsWithProps< + : { functional?: never } & ComponentOptionsWithProps< PropsOptions, RawBindings, D, @@ -199,3 +175,27 @@ export function defineComponent< EmitsNames > ): DefineComponent + +/** + * overload 4.1: functional component with array props + */ +export function defineComponent< + PropNames extends string, + Props = Readonly<{ [key in PropNames]?: any }> +>(options: { + functional: true + props?: PropNames[] + render?: (h: CreateElement, context: RenderContext) => any +}): DefineComponent + +/** + * overload 4.2: functional component with object props + */ +export function defineComponent< + PropsOptions extends ComponentPropsOptions = ComponentPropsOptions, + Props = ExtractPropTypes +>(options: { + functional: true + props?: PropsOptions + render?: (h: CreateElement, context: RenderContext) => any +}): DefineComponent From bd89ce53a9de417a9372630bb5d433a40acc1a53 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Aug 2022 15:37:13 +0800 Subject: [PATCH 04/41] fix: ensure render watcher of manually created instance is correctly tracked in owner scope fix #12701 --- src/core/instance/lifecycle.ts | 2 ++ src/core/observer/watcher.ts | 6 ++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/instance/lifecycle.ts b/src/core/instance/lifecycle.ts index df70b7113fd..fec330ad5fc 100644 --- a/src/core/instance/lifecycle.ts +++ b/src/core/instance/lifecycle.ts @@ -209,6 +209,7 @@ export function mountComponent( // we set this to vm._watcher inside the watcher's constructor // since the watcher's initial patch may call $forceUpdate (e.g. inside child // component's mounted hook), which relies on vm._watcher being already defined + vm._scope.on() new Watcher( vm, updateComponent, @@ -216,6 +217,7 @@ export function mountComponent( watcherOptions, true /* isRenderWatcher */ ) + vm._scope.off() hydrating = false // flush buffer for flush: "pre" watchers queued in setup() diff --git a/src/core/observer/watcher.ts b/src/core/observer/watcher.ts index b1f491acb33..00bf19b9123 100644 --- a/src/core/observer/watcher.ts +++ b/src/core/observer/watcher.ts @@ -72,10 +72,8 @@ export default class Watcher implements DepTarget { isRenderWatcher?: boolean ) { recordEffectScope(this, activeEffectScope || (vm ? vm._scope : undefined)) - if ((this.vm = vm)) { - if (isRenderWatcher) { - vm._watcher = this - } + if ((this.vm = vm) && isRenderWatcher) { + vm._watcher = this } // options if (options) { From f0057b101e6451d5095cdb7fd6308fd31ac0450c Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 15 Aug 2022 19:06:38 +0800 Subject: [PATCH 05/41] fix(watch): avoid pre watcher firing on unmount fix #12703 --- src/v3/apiWatch.ts | 5 +---- test/unit/features/v3/apiWatch.spec.ts | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/v3/apiWatch.ts b/src/v3/apiWatch.ts index 7fe5eeb9dcc..141a58eb686 100644 --- a/src/v3/apiWatch.ts +++ b/src/v3/apiWatch.ts @@ -274,10 +274,7 @@ function doWatch( let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE // overwrite default run watcher.run = () => { - if ( - !watcher.active && - !(flush === 'pre' && instance && instance._isBeingDestroyed) - ) { + if (!watcher.active) { return } if (cb) { diff --git a/test/unit/features/v3/apiWatch.spec.ts b/test/unit/features/v3/apiWatch.spec.ts index 0206e8df3bf..c92bc150ae7 100644 --- a/test/unit/features/v3/apiWatch.spec.ts +++ b/test/unit/features/v3/apiWatch.spec.ts @@ -542,7 +542,7 @@ describe('api: watch', () => { expect(cb).not.toHaveBeenCalled() }) - it('should fire on component unmount w/ flush: pre', async () => { + it('should not fire on component unmount w/ flush: pre', async () => { const toggle = ref(true) const cb = vi.fn() const Comp = { @@ -560,7 +560,7 @@ describe('api: watch', () => { expect(cb).not.toHaveBeenCalled() toggle.value = false await nextTick() - expect(cb).toHaveBeenCalledTimes(1) + expect(cb).not.toHaveBeenCalled() }) // vuejs/core#1763 From 80d1baf92050da411fb1bfe714401c498001dd36 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 18 Aug 2022 15:23:47 +0800 Subject: [PATCH 06/41] feat(types): export DefineComponent close #12748 --- types/index.d.ts | 2 +- types/v3-define-component.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index 2b5d3a89da2..19fce98726f 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -41,7 +41,7 @@ export * from './v3-setup-helpers' export { Data } from './common' export { SetupContext } from './v3-setup-context' -export { defineComponent } from './v3-define-component' +export { defineComponent, DefineComponent } from './v3-define-component' export { defineAsyncComponent } from './v3-define-async-component' export { SetupFunction, diff --git a/types/v3-define-component.d.ts b/types/v3-define-component.d.ts index a2c47322b49..69f65a2ec35 100644 --- a/types/v3-define-component.d.ts +++ b/types/v3-define-component.d.ts @@ -20,7 +20,7 @@ import { Data, HasDefined } from './common' import { EmitsOptions } from './v3-setup-context' import { CreateElement, RenderContext } from './umd' -type DefineComponent< +export type DefineComponent< PropsOrPropOptions = {}, RawBindings = {}, D = {}, From b4bf4c52ad31e02307cfd4d643dc5610c893e3ba Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 18 Aug 2022 15:32:12 +0800 Subject: [PATCH 07/41] fix(types): allow attaching unknown options to defined component fix #12742 --- types/test/v3/define-component-test.tsx | 4 ++++ types/v3-component-options.d.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/types/test/v3/define-component-test.tsx b/types/test/v3/define-component-test.tsx index 42d0f32eb0d..481a2d11ac7 100644 --- a/types/test/v3/define-component-test.tsx +++ b/types/test/v3/define-component-test.tsx @@ -1165,3 +1165,7 @@ defineComponent({ return h('div', {}, [...this.$slots.default!]) } }) + +// #12742 allow attaching custom properties (consistent with v3) +const Foo = defineComponent({}) +Foo.foobar = 123 diff --git a/types/v3-component-options.d.ts b/types/v3-component-options.d.ts index d8c64ab13ab..e2da34e753f 100644 --- a/types/v3-component-options.d.ts +++ b/types/v3-component-options.d.ts @@ -88,6 +88,9 @@ export interface ComponentOptionsBase< 'data' | 'computed' | 'methods' | 'setup' | 'props' | 'mixins' | 'extends' >, ComponentCustomOptions { + // allow any options + [key: string]: any + // rewrite options api types data?: ( this: CreateComponentPublicInstance, From 89a6b5e8658a6e3ae2cf649829901784ac9deb3c Mon Sep 17 00:00:00 2001 From: gu <11851303+gulewei@users.noreply.github.com> Date: Thu, 18 Aug 2022 15:56:37 +0800 Subject: [PATCH 08/41] feat(types): support mixins inference for new Vue() (#12737) close #12730 --- types/options.d.ts | 33 +++++-- types/test/vue-test.ts | 39 +++++++- types/v3-component-public-instance.d.ts | 4 +- types/vue.d.ts | 124 +++++++++++++++++++----- 4 files changed, 166 insertions(+), 34 deletions(-) diff --git a/types/options.d.ts b/types/options.d.ts index e11020f4948..c82c6c5f8ae 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -3,6 +3,7 @@ import { VNode, VNodeData, VNodeDirective, NormalizedScopedSlot } from './vnode' import { SetupContext } from './v3-setup-context' import { DebuggerEvent } from './v3-generated' import { DefineComponent } from './v3-define-component' +import { ComponentOptionsMixin } from './v3-component-options' type Constructor = { new (...args: any[]): any @@ -93,7 +94,9 @@ export type ThisTypedComponentOptionsWithArrayProps< Methods, Computed, PropNames extends string, - SetupBindings + SetupBindings, + Mixin, + Extends > = object & ComponentOptions< V, @@ -102,7 +105,9 @@ export type ThisTypedComponentOptionsWithArrayProps< Computed, PropNames[], Record, - SetupBindings + SetupBindings, + Mixin, + Extends > & ThisType< CombinedVueInstance< @@ -111,7 +116,9 @@ export type ThisTypedComponentOptionsWithArrayProps< Methods, Computed, Readonly>, - SetupBindings + SetupBindings, + Mixin, + Extends > > @@ -124,7 +131,9 @@ export type ThisTypedComponentOptionsWithRecordProps< Methods, Computed, Props, - SetupBindings + SetupBindings, + Mixin, + Extends > = object & ComponentOptions< V, @@ -133,7 +142,9 @@ export type ThisTypedComponentOptionsWithRecordProps< Computed, RecordPropsDefinition, Props, - SetupBindings + SetupBindings, + Mixin, + Extends > & ThisType< CombinedVueInstance< @@ -142,7 +153,9 @@ export type ThisTypedComponentOptionsWithRecordProps< Methods, Computed, Readonly, - SetupBindings + SetupBindings, + Mixin, + Extends > > @@ -158,7 +171,9 @@ export interface ComponentOptions< Computed = DefaultComputed, PropsDef = PropsDefinition, Props = DefaultProps, - RawBindings = {} + RawBindings = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin > { data?: Data props?: PropsDef @@ -217,12 +232,12 @@ export interface ComponentOptions< } parent?: Vue - mixins?: (ComponentOptions | typeof Vue)[] + mixins?: (Mixin | ComponentOptions | typeof Vue)[] name?: string // for SFC auto name inference w/ ts-loader check __name?: string // TODO: support properly inferred 'extends' - extends?: ComponentOptions | typeof Vue + extends?: Extends | ComponentOptions | typeof Vue delimiters?: [string, string] comments?: boolean inheritAttrs?: boolean diff --git a/types/test/vue-test.ts b/types/test/vue-test.ts index 8489f3ae8ed..b792cec583d 100644 --- a/types/test/vue-test.ts +++ b/types/test/vue-test.ts @@ -1,4 +1,4 @@ -import Vue, { VNode } from '../index' +import Vue, { VNode, defineComponent } from '../index' import { ComponentOptions } from '../options' class Test extends Vue { @@ -246,3 +246,40 @@ const ComponentWithStyleInVNodeData = Vue.extend({ ]) } }) + +// infer mixin type with new Vue() #12730 +new Vue({ + mixins: [ + defineComponent({ + props: { + p1: String, + p2: { + type: Number, + default: 0 + } + }, + data() { + return { + foo: 123 + } + }, + computed: { + bar() { + return 123 + } + } + }), + { + methods: { + hello(n: number) {} + } + } + ], + created() { + this.hello(this.foo) + this.hello(this.bar) + // @ts-expect-error + this.hello(this.p1) + this.hello(this.p2) + } +}) diff --git a/types/v3-component-public-instance.d.ts b/types/v3-component-public-instance.d.ts index 3586f4031e8..1c55908ac73 100644 --- a/types/v3-component-public-instance.d.ts +++ b/types/v3-component-public-instance.d.ts @@ -79,11 +79,11 @@ type ExtractMixin = { Mixin: MixinToOptionTypes }[T extends ComponentOptionsMixin ? 'Mixin' : never] -type IntersectionMixin = IsDefaultMixinComponent extends true +export type IntersectionMixin = IsDefaultMixinComponent extends true ? OptionTypesType<{}, {}, {}, {}, {}, {}> : UnionToIntersection> -type UnwrapMixinsType< +export type UnwrapMixinsType< T, Type extends OptionTypesKeys > = T extends OptionTypesType ? T[Type] : never diff --git a/types/vue.d.ts b/types/vue.d.ts index 8c91bc88a2f..5a2027a0179 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -13,7 +13,15 @@ import { import { VNode, VNodeData, VNodeChildren, NormalizedScopedSlot } from './vnode' import { PluginFunction, PluginObject } from './plugin' import { DefineComponent } from './v3-define-component' -import { nextTick } from './v3-generated' +import { nextTick, UnwrapNestedRefs, ShallowUnwrapRef } from './v3-generated' +import { + UnwrapMixinsType, + IntersectionMixin +} from './v3-component-public-instance' +import { + ExtractComputedReturns, + ComponentOptionsMixin +} from './v3-component-options' export interface CreateElement { ( @@ -100,12 +108,20 @@ export type CombinedVueInstance< Methods, Computed, Props, - SetupBindings = {} -> = Data & + SetupBindings = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin, + PublicMixin = IntersectionMixin & IntersectionMixin +> = UnwrapNestedRefs> & + Data & + UnwrapMixinsType & Methods & + ExtractComputedReturns> & Computed & + UnwrapMixinsType & Props & Instance & + ShallowUnwrapRef> & (SetupBindings extends void ? {} : SetupBindings) export type ExtendedVue< @@ -114,9 +130,20 @@ export type ExtendedVue< Methods, Computed, Props, - SetupBindings = {} + SetupBindings = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin > = VueConstructor< - CombinedVueInstance & + CombinedVueInstance< + Instance, + Data, + Methods, + Computed, + Props, + SetupBindings, + Mixin, + Extends + > & Vue > @@ -142,7 +169,9 @@ export interface VueConstructor { Methods = object, Computed = object, PropNames extends string = never, - SetupBindings = {} + SetupBindings = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin >( options?: ThisTypedComponentOptionsWithArrayProps< V, @@ -150,7 +179,9 @@ export interface VueConstructor { Methods, Computed, PropNames, - SetupBindings + SetupBindings, + Mixin, + Extends > ): CombinedVueInstance< V, @@ -158,7 +189,9 @@ export interface VueConstructor { Methods, Computed, Record, - SetupBindings + SetupBindings, + Mixin, + Extends > /** @@ -172,7 +205,9 @@ export interface VueConstructor { Methods = object, Computed = object, Props = object, - SetupBindings = {} + SetupBindings = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin >( options?: ThisTypedComponentOptionsWithRecordProps< V, @@ -180,7 +215,9 @@ export interface VueConstructor { Methods, Computed, Props, - SetupBindings + SetupBindings, + Mixin, + Extends > ): CombinedVueInstance< V, @@ -188,7 +225,9 @@ export interface VueConstructor { Methods, Computed, Record, - SetupBindings + SetupBindings, + Mixin, + Extends > /** @@ -211,7 +250,9 @@ export interface VueConstructor { Methods, Computed, PropNames extends string = never, - SetupBindings = {} + SetupBindings = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin >( options?: ThisTypedComponentOptionsWithArrayProps< V, @@ -219,7 +260,9 @@ export interface VueConstructor { Methods, Computed, PropNames, - SetupBindings + SetupBindings, + Mixin, + Extends > ): ExtendedVue< V, @@ -227,22 +270,43 @@ export interface VueConstructor { Methods, Computed, Record, - SetupBindings + SetupBindings, + Mixin, + Extends > /** * extend with object props */ - extend( + extend< + Data, + Methods, + Computed, + Props, + SetupBindings = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin + >( options?: ThisTypedComponentOptionsWithRecordProps< V, Data, Methods, Computed, Props, - SetupBindings + SetupBindings, + Mixin, + Extends > - ): ExtendedVue + ): ExtendedVue< + V, + Data, + Methods, + Computed, + Props, + SetupBindings, + Mixin, + Extends + > /** * extend with functional + array props @@ -287,7 +351,9 @@ export interface VueConstructor { Methods, Computed, PropNames extends string = never, - SetupBindings = {} + SetupBindings = {}, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin >( id: string, definition?: ThisTypedComponentOptionsWithArrayProps< @@ -296,7 +362,9 @@ export interface VueConstructor { Methods, Computed, PropNames, - SetupBindings + SetupBindings, + Mixin, + Extends > ): ExtendedVue< V, @@ -304,9 +372,19 @@ export interface VueConstructor { Methods, Computed, Record, - SetupBindings + SetupBindings, + Mixin, + Extends > - component( + component< + Data, + Methods, + Computed, + Props, + SetupBindings, + Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, + Extends extends ComponentOptionsMixin = ComponentOptionsMixin + >( id: string, definition?: ThisTypedComponentOptionsWithRecordProps< V, @@ -314,7 +392,9 @@ export interface VueConstructor { Methods, Computed, Props, - SetupBindings + SetupBindings, + Mixin, + Extends > ): ExtendedVue component( From 5221d4d3b6049c87d196d99dbb64bcd3f3b07279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E7=83=81=E5=A3=95?= <1138674510@qq.com> Date: Thu, 18 Aug 2022 16:01:00 +0800 Subject: [PATCH 09/41] fix(compiler-sfc): rewriteDefault for class with decorators (#12747) --- packages/compiler-sfc/src/rewriteDefault.ts | 7 +- .../compiler-sfc/test/rewriteDefault.spec.ts | 245 ++++++++++++++++++ 2 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 packages/compiler-sfc/test/rewriteDefault.spec.ts diff --git a/packages/compiler-sfc/src/rewriteDefault.ts b/packages/compiler-sfc/src/rewriteDefault.ts index ced9f56d8a9..e61cf691bba 100644 --- a/packages/compiler-sfc/src/rewriteDefault.ts +++ b/packages/compiler-sfc/src/rewriteDefault.ts @@ -42,7 +42,12 @@ export function rewriteDefault( }).program.body ast.forEach(node => { if (node.type === 'ExportDefaultDeclaration') { - s.overwrite(node.start!, node.declaration.start!, `const ${as} = `) + if (node.declaration.type === 'ClassDeclaration') { + s.overwrite(node.start!, node.declaration.id.start!, `class `) + s.append(`\nconst ${as} = ${node.declaration.id.name}`) + } else { + s.overwrite(node.start!, node.declaration.start!, `const ${as} = `) + } } if (node.type === 'ExportNamedDeclaration') { for (const specifier of node.specifiers) { diff --git a/packages/compiler-sfc/test/rewriteDefault.spec.ts b/packages/compiler-sfc/test/rewriteDefault.spec.ts new file mode 100644 index 00000000000..9fb4c64bbb3 --- /dev/null +++ b/packages/compiler-sfc/test/rewriteDefault.spec.ts @@ -0,0 +1,245 @@ +import { rewriteDefault } from '../src' + +describe('compiler sfc: rewriteDefault', () => { + test('without export default', () => { + expect(rewriteDefault(`export a = {}`, 'script')).toMatchInlineSnapshot(` + "export a = {} + const script = {}" + `) + }) + + test('rewrite export default', () => { + expect( + rewriteDefault(`export default {}`, 'script') + ).toMatchInlineSnapshot(`"const script = {}"`) + }) + + test('rewrite export named default', () => { + expect( + rewriteDefault( + `const a = 1 \n export { a as b, a as default, a as c}`, + 'script' + ) + ).toMatchInlineSnapshot(` + "const a = 1 + export { a as b, a as c} + const script = a" + `) + + expect( + rewriteDefault( + `const a = 1 \n export { a as b, a as default , a as c}`, + 'script' + ) + ).toMatchInlineSnapshot(` + "const a = 1 + export { a as b, a as c} + const script = a" + `) + }) + + test('w/ comments', async () => { + expect(rewriteDefault(`// export default\nexport default {}`, 'script')) + .toMatchInlineSnapshot(` + "// export default + const script = {}" + `) + }) + + test('export named default multiline', () => { + expect( + rewriteDefault(`let App = {}\n export {\nApp as default\n}`, '_sfc_main') + ).toMatchInlineSnapshot(` + "let App = {} + export { + + } + const _sfc_main = App" + `) + }) + + test('export named default multiline /w comments', () => { + expect( + rewriteDefault( + `const a = 1 \n export {\n a as b,\n a as default,\n a as c}\n` + + `// export { myFunction as default }`, + 'script' + ) + ).toMatchInlineSnapshot(` + "const a = 1 + export { + a as b, + + a as c} + // export { myFunction as default } + const script = a" + `) + + expect( + rewriteDefault( + `const a = 1 \n export {\n a as b,\n a as default ,\n a as c}\n` + + `// export { myFunction as default }`, + 'script' + ) + ).toMatchInlineSnapshot(` + "const a = 1 + export { + a as b, + + a as c} + // export { myFunction as default } + const script = a" + `) + }) + + test(`export { default } from '...'`, async () => { + expect( + rewriteDefault(`export { default, foo } from './index.js'`, 'script') + ).toMatchInlineSnapshot(` + "import { default as __VUE_DEFAULT__ } from './index.js' + export { foo } from './index.js' + const script = __VUE_DEFAULT__" + `) + + expect( + rewriteDefault(`export { default , foo } from './index.js'`, 'script') + ).toMatchInlineSnapshot(` + "import { default as __VUE_DEFAULT__ } from './index.js' + export { foo } from './index.js' + const script = __VUE_DEFAULT__" + `) + + expect( + rewriteDefault(`export { foo, default } from './index.js'`, 'script') + ).toMatchInlineSnapshot(` + "import { default as __VUE_DEFAULT__ } from './index.js' + export { foo, } from './index.js' + const script = __VUE_DEFAULT__" + `) + + expect( + rewriteDefault( + `export { foo as default, bar } from './index.js'`, + 'script' + ) + ).toMatchInlineSnapshot(` + "import { foo } from './index.js' + export { bar } from './index.js' + const script = foo" + `) + + expect( + rewriteDefault( + `export { foo as default , bar } from './index.js'`, + 'script' + ) + ).toMatchInlineSnapshot(` + "import { foo } from './index.js' + export { bar } from './index.js' + const script = foo" + `) + + expect( + rewriteDefault( + `export { bar, foo as default } from './index.js'`, + 'script' + ) + ).toMatchInlineSnapshot(` + "import { foo } from './index.js' + export { bar, } from './index.js' + const script = foo" + `) + }) + + test('export default class', async () => { + expect(rewriteDefault(`export default class Foo {}`, 'script')) + .toMatchInlineSnapshot(` + "class Foo {} + const script = Foo" + `) + }) + + test('export default class w/ comments', async () => { + expect( + rewriteDefault(`// export default\nexport default class Foo {}`, 'script') + ).toMatchInlineSnapshot(` + "// export default + class Foo {} + const script = Foo" + `) + }) + + test('export default class w/ comments 2', async () => { + expect( + rewriteDefault( + `export default {}\n` + `// export default class Foo {}`, + 'script' + ) + ).toMatchInlineSnapshot(` + "const script = {} + // export default class Foo {}" + `) + }) + + test('export default class w/ comments 3', async () => { + expect( + rewriteDefault( + `/*\nexport default class Foo {}*/\n` + `export default class Bar {}`, + 'script' + ) + ).toMatchInlineSnapshot(` + "/* + export default class Foo {}*/ + class Bar {} + const script = Bar" + `) + }) + + test('@Component\nexport default class', async () => { + expect(rewriteDefault(`@Component\nexport default class Foo {}`, 'script')) + .toMatchInlineSnapshot(` + "@Component + class Foo {} + const script = Foo" + `) + }) + + test('@Component\nexport default class w/ comments', async () => { + expect( + rewriteDefault(`// export default\n@Component\nexport default class Foo {}`, 'script') + ).toMatchInlineSnapshot(` + "// export default + @Component + class Foo {} + const script = Foo" + `) + }) + + test('@Component\nexport default class w/ comments 2', async () => { + expect( + rewriteDefault( + `export default {}\n` + `// @Component\n// export default class Foo {}`, + 'script' + ) + ).toMatchInlineSnapshot(` + "const script = {} + // @Component + // export default class Foo {}" + `) + }) + + test('@Component\nexport default class w/ comments 3', async () => { + expect( + rewriteDefault( + `/*\n@Component\nexport default class Foo {}*/\n` + `export default class Bar {}`, + 'script' + ) + ).toMatchInlineSnapshot(` + "/* + @Component + export default class Foo {}*/ + class Bar {} + const script = Bar" + `) + }) +}) From 2263948c249e7486403bc5880712e6d9fd15c17f Mon Sep 17 00:00:00 2001 From: JuniorTour Date: Thu, 18 Aug 2022 16:11:47 +0800 Subject: [PATCH 10/41] fix: directives shorthand normalize error (#12744) fix #12743 --- src/core/vdom/modules/directives.ts | 10 +++++++++- test/unit/features/v3/apiSetup.spec.ts | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/core/vdom/modules/directives.ts b/src/core/vdom/modules/directives.ts index 9e4a87f7f6e..853b20021e7 100644 --- a/src/core/vdom/modules/directives.ts +++ b/src/core/vdom/modules/directives.ts @@ -103,7 +103,15 @@ function normalizeDirectives( } res[getRawDirName(dir)] = dir if (vm._setupState && vm._setupState.__sfc) { - dir.def = dir.def || resolveAsset(vm, '_setupState', 'v-' + dir.name) + const setupDef = dir.def || resolveAsset(vm, '_setupState', 'v-' + dir.name) + if (typeof setupDef === 'function') { + dir.def = { + bind: setupDef, + update: setupDef, + } + } else { + dir.def = setupDef + } } dir.def = dir.def || resolveAsset(vm.$options, 'directives', dir.name, true) } diff --git a/test/unit/features/v3/apiSetup.spec.ts b/test/unit/features/v3/apiSetup.spec.ts index 7c69d06d1c4..11757878e9c 100644 --- a/test/unit/features/v3/apiSetup.spec.ts +++ b/test/unit/features/v3/apiSetup.spec.ts @@ -251,6 +251,19 @@ describe('api: setup context', () => { expect(spy).toHaveBeenCalled() }) + // #12743 + it('directive resolution for shorthand', () => { + const spy = vi.fn() + new Vue({ + setup: () => ({ + __sfc: true, + vDir: spy + }), + template: `
` + }).$mount() + expect(spy).toHaveBeenCalled() + }) + // #12561 it('setup props should be reactive', () => { const msg = ref('hi') From 9eb8ea5b63eec2b09f268738e9d0e311d9eafb19 Mon Sep 17 00:00:00 2001 From: GU Yiling Date: Thu, 18 Aug 2022 16:12:20 +0800 Subject: [PATCH 11/41] chore: fix some legacy doc urls in warnings and readme (#12725) [ci skip] --- README.md | 2 +- src/core/instance/proxy.ts | 4 ++-- src/core/instance/state.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5a3f576d716..5eef53d3fa0 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Vue.js supports all browsers that are [ES5-compliant](https://kangax.github.io/c ## Documentation -To check out [live examples](https://vuejs.org/v2/examples/) and docs, visit [vuejs.org](https://vuejs.org). +To check out [live examples](https://v2.vuejs.org/v2/examples/) and docs, visit [vuejs.org](https://v2.vuejs.org). ## Questions diff --git a/src/core/instance/proxy.ts b/src/core/instance/proxy.ts index 3543d74954a..685d9651fcc 100644 --- a/src/core/instance/proxy.ts +++ b/src/core/instance/proxy.ts @@ -19,7 +19,7 @@ if (__DEV__) { 'referenced during render. Make sure that this property is reactive, ' + 'either in the data option, or for class-based components, by ' + 'initializing the property. ' + - 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', + 'See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', target ) } @@ -29,7 +29,7 @@ if (__DEV__) { `Property "${key}" must be accessed with "$data.${key}" because ` + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + 'prevent conflicts with Vue internals. ' + - 'See: https://vuejs.org/v2/api/#data', + 'See: https://v2.vuejs.org/v2/api/#data', target ) } diff --git a/src/core/instance/state.ts b/src/core/instance/state.ts index e22ec6705fb..aedb72555c9 100644 --- a/src/core/instance/state.ts +++ b/src/core/instance/state.ts @@ -127,7 +127,7 @@ function initData(vm: Component) { __DEV__ && warn( 'data functions should return an object:\n' + - 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', + 'https://v2.vuejs.org/v2/guide/components.html#data-Must-Be-a-Function', vm ) } From bba6b3d6b4e3e26d28abbf20e74ec2f3e64f1a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E8=A7=81=E6=9C=88?= <61452855+nooooooom@users.noreply.github.com> Date: Thu, 18 Aug 2022 16:20:27 +0800 Subject: [PATCH 12/41] feat(types): enhance type for onErrorCaptured (#12735) --- src/v3/apiLifecycle.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/v3/apiLifecycle.ts b/src/v3/apiLifecycle.ts index dc47bc4652b..31e0542920c 100644 --- a/src/v3/apiLifecycle.ts +++ b/src/v3/apiLifecycle.ts @@ -42,7 +42,6 @@ export const onBeforeUpdate = createLifeCycle('beforeUpdate') export const onUpdated = createLifeCycle('updated') export const onBeforeUnmount = createLifeCycle('beforeDestroy') export const onUnmounted = createLifeCycle('destroyed') -export const onErrorCaptured = createLifeCycle('errorCaptured') export const onActivated = createLifeCycle('activated') export const onDeactivated = createLifeCycle('deactivated') export const onServerPrefetch = createLifeCycle('serverPrefetch') @@ -51,3 +50,19 @@ export const onRenderTracked = createLifeCycle<(e: DebuggerEvent) => any>('renderTracked') export const onRenderTriggered = createLifeCycle<(e: DebuggerEvent) => any>('renderTriggered') + +export type ErrorCapturedHook = ( + err: TError, + instance: any, + info: string +) => boolean | void + +const injectErrorCapturedHook = + createLifeCycle>('errorCaptured') + +export function onErrorCaptured( + hook: ErrorCapturedHook, + target: any = currentInstance +) { + injectErrorCapturedHook(hook, target) +} From 165a14a6c6c406176037465d2961259c5c980399 Mon Sep 17 00:00:00 2001 From: Alexander Lichter Date: Thu, 18 Aug 2022 10:22:55 +0200 Subject: [PATCH 13/41] fix(ssr): fix on-component directives rendering (#12661) fix #10733 --- packages/server-renderer/src/render.ts | 10 +- .../server-renderer/test/ssr-string.spec.ts | 94 ++++++++++++++++++- src/core/vdom/vnode.ts | 1 + 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/packages/server-renderer/src/render.ts b/packages/server-renderer/src/render.ts index 907b795d797..b1116840ed5 100644 --- a/packages/server-renderer/src/render.ts +++ b/packages/server-renderer/src/render.ts @@ -206,6 +206,11 @@ function renderComponentInner(node, isRoot, context) { type: 'Component', prevActive }) + if (isDef(node.data) && isDef(node.data.directives)) { + childNode.data = childNode.data || {} + childNode.data.directives = node.data.directives + childNode.isComponentRootElement = true + } renderNode(childNode, isRoot, context) } @@ -372,7 +377,10 @@ function renderStartingTag(node: VNode, context) { if (dirRenderer) { // directives mutate the node's data // which then gets rendered by modules - dirRenderer(node, dirs[i]) + dirRenderer( + node.isComponentRootElement ? node.parent : node, + dirs[i] + ) } } } diff --git a/packages/server-renderer/test/ssr-string.spec.ts b/packages/server-renderer/test/ssr-string.spec.ts index 35810c8ed58..391671c1e52 100644 --- a/packages/server-renderer/test/ssr-string.spec.ts +++ b/packages/server-renderer/test/ssr-string.spec.ts @@ -1086,7 +1086,7 @@ describe('SSR: renderToString', () => { ) }) - it('custom directives', done => { + it('custom directives on raw element', done => { const renderer = createRenderer({ directives: { 'class-prefixer': (node, dir) => { @@ -1129,6 +1129,98 @@ describe('SSR: renderToString', () => { ) }) + it('custom directives on component', done => { + const Test = { + template: 'hello world' + } + const renderer = createRenderer({ + directives: { + 'class-prefixer': (node, dir) => { + if (node.data.class) { + node.data.class = `${dir.value}-${node.data.class}` + } + if (node.data.staticClass) { + node.data.staticClass = `${dir.value}-${node.data.staticClass}` + } + } + } + }) + renderer.renderToString( + new Vue({ + template: + '

', + components: { Test } + }), + (err, result) => { + expect(err).toBeNull() + expect(result).toContain( + '

hello world

' + ) + done() + } + ) + }) + + it('custom directives on element root of a component', done => { + const Test = { + template: + 'hello world' + } + const renderer = createRenderer({ + directives: { + 'class-prefixer': (node, dir) => { + if (node.data.class) { + node.data.class = `${dir.value}-${node.data.class}` + } + if (node.data.staticClass) { + node.data.staticClass = `${dir.value}-${node.data.staticClass}` + } + } + } + }) + renderer.renderToString( + new Vue({ + template: '

', + components: { Test } + }), + (err, result) => { + expect(err).toBeNull() + expect(result).toContain( + '

hello world

' + ) + done() + } + ) + }) + + it('custom directives on element with parent element', done => { + const renderer = createRenderer({ + directives: { + 'class-prefixer': (node, dir) => { + if (node.data.class) { + node.data.class = `${dir.value}-${node.data.class}` + } + if (node.data.staticClass) { + node.data.staticClass = `${dir.value}-${node.data.staticClass}` + } + } + } + }) + renderer.renderToString( + new Vue({ + template: + '

hello world

' + }), + (err, result) => { + expect(err).toBeNull() + expect(result).toContain( + '

hello world

' + ) + done() + } + ) + }) + it('should not warn for custom directives that do not have server-side implementation', done => { renderToString( new Vue({ diff --git a/src/core/vdom/vnode.ts b/src/core/vdom/vnode.ts index 80f784167ba..3b57f9aca0c 100644 --- a/src/core/vdom/vnode.ts +++ b/src/core/vdom/vnode.ts @@ -33,6 +33,7 @@ export default class VNode { fnOptions?: ComponentOptions | null // for SSR caching devtoolsMeta?: Object | null // used to store functional render context for devtools fnScopeId?: string | null // functional scope id support + isComponentRootElement?: boolean | null // for SSR directives constructor( tag?: string, From 7161176cd0dff10d65ab58e266018aff2660610f Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 18 Aug 2022 18:14:50 +0800 Subject: [PATCH 14/41] fix: fix effect scope tracking for manually created instances fix #12705 --- src/core/instance/init.ts | 1 + src/core/instance/lifecycle.ts | 2 -- src/core/observer/watcher.ts | 11 ++++++++++- src/v3/reactivity/effectScope.ts | 6 +++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/core/instance/init.ts b/src/core/instance/init.ts index 876c9ddbf96..91456c21920 100644 --- a/src/core/instance/init.ts +++ b/src/core/instance/init.ts @@ -34,6 +34,7 @@ export function initMixin(Vue: typeof Component) { vm.__v_skip = true // effect scope vm._scope = new EffectScope(true /* detached */) + vm._scope._vm = true // merge options if (options && options._isComponent) { // optimize internal component instantiation diff --git a/src/core/instance/lifecycle.ts b/src/core/instance/lifecycle.ts index fec330ad5fc..df70b7113fd 100644 --- a/src/core/instance/lifecycle.ts +++ b/src/core/instance/lifecycle.ts @@ -209,7 +209,6 @@ export function mountComponent( // we set this to vm._watcher inside the watcher's constructor // since the watcher's initial patch may call $forceUpdate (e.g. inside child // component's mounted hook), which relies on vm._watcher being already defined - vm._scope.on() new Watcher( vm, updateComponent, @@ -217,7 +216,6 @@ export function mountComponent( watcherOptions, true /* isRenderWatcher */ ) - vm._scope.off() hydrating = false // flush buffer for flush: "pre" watchers queued in setup() diff --git a/src/core/observer/watcher.ts b/src/core/observer/watcher.ts index 00bf19b9123..b2989b53772 100644 --- a/src/core/observer/watcher.ts +++ b/src/core/observer/watcher.ts @@ -71,7 +71,16 @@ export default class Watcher implements DepTarget { options?: WatcherOptions | null, isRenderWatcher?: boolean ) { - recordEffectScope(this, activeEffectScope || (vm ? vm._scope : undefined)) + recordEffectScope( + this, + // if the active effect scope is manually created (not a component scope), + // prioritize it + activeEffectScope && !activeEffectScope._vm + ? activeEffectScope + : vm + ? vm._scope + : undefined + ) if ((this.vm = vm) && isRenderWatcher) { vm._watcher = this } diff --git a/src/v3/reactivity/effectScope.ts b/src/v3/reactivity/effectScope.ts index bc15380eb4a..2ba50cd9ca8 100644 --- a/src/v3/reactivity/effectScope.ts +++ b/src/v3/reactivity/effectScope.ts @@ -27,10 +27,14 @@ export class EffectScope { * @internal */ scopes: EffectScope[] | undefined + /** + * indicates this being a component root scope + * @internal + */ + _vm?: boolean /** * track a child scope's index in its parent's scopes array for optimized * removal - * @internal */ private index: number | undefined From 8521f9d3f63d26bde99b747f0cb14d0ac5ba5971 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 19 Aug 2022 12:22:51 +0800 Subject: [PATCH 15/41] fix(types): fix missing error for accessing undefined instance properties fix #12718 --- types/test/v3/define-component-test.tsx | 62 +++++++++++++++++++++++-- types/v3-define-component.d.ts | 10 ++-- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/types/test/v3/define-component-test.tsx b/types/test/v3/define-component-test.tsx index 481a2d11ac7..7e6d1968ca3 100644 --- a/types/test/v3/define-component-test.tsx +++ b/types/test/v3/define-component-test.tsx @@ -1166,6 +1166,62 @@ defineComponent({ } }) -// #12742 allow attaching custom properties (consistent with v3) -const Foo = defineComponent({}) -Foo.foobar = 123 +describe('constructor attach custom properties', () => { + // #12742 allow attaching custom properties (consistent with v3) + const Foo = defineComponent({}) + Foo.foobar = 123 +}) + +describe('constructor instance type', () => { + const Comp = defineComponent({ + data() { + return { + a: 1 + } + }, + + computed: { + ac() { + return 1 + } + }, + + methods: { + callA(b: number) { + return b + } + }, + + setup() { + return { + sa: '1' + } + } + }) + + const comp = new Comp() + + expectType(comp.a) + expectType(comp.ac) + expectType(comp.sa) + expectType<(b: number) => number>(comp.callA) +}) + +describe('should report non-existent properties in instance', () => { + const Foo = defineComponent({}) + const instance = new Foo() + // @ts-expect-error + instance.foo + + const Foo2 = defineComponent({ + data() { + return {} + }, + methods: { + example() {} + } + }) + const instance2 = new Foo2() + // @ts-expect-error + instance2.foo +}) diff --git a/types/v3-define-component.d.ts b/types/v3-define-component.d.ts index 69f65a2ec35..03ef52d1856 100644 --- a/types/v3-define-component.d.ts +++ b/types/v3-define-component.d.ts @@ -72,7 +72,7 @@ export type DefineComponent< */ export function defineComponent< RawBindings, - D = Data, + D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, @@ -101,8 +101,8 @@ export function defineComponent< */ export function defineComponent< PropNames extends string, - RawBindings = Data, - D = Data, + RawBindings = {}, + D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, @@ -140,8 +140,8 @@ export function defineComponent< */ export function defineComponent< Props, - RawBindings = Data, - D = Data, + RawBindings = {}, + D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, From 15618888cb6aae2b6f0151b32c261b1fc19db1b8 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 19 Aug 2022 12:27:12 +0800 Subject: [PATCH 16/41] release: v2.7.9 --- CHANGELOG.md | 26 +++++++++++++++++++++++++ package.json | 2 +- packages/compiler-sfc/package.json | 2 +- packages/server-renderer/package.json | 2 +- packages/template-compiler/package.json | 2 +- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc98f138f33..b5d38472209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +## [2.7.9](https://github.com/vuejs/vue/compare/v2.7.8...v2.7.9) (2022-08-19) + + +### Bug Fixes + +* **compiler-sfc:** allow full hostnames in asset url base ([#12732](https://github.com/vuejs/vue/issues/12732)) ([5c742eb](https://github.com/vuejs/vue/commit/5c742eb2e0d8dad268fb29ed4f92d286b5e0f4b5)), closes [#12731](https://github.com/vuejs/vue/issues/12731) +* **compiler-sfc:** rewriteDefault for class with decorators ([#12747](https://github.com/vuejs/vue/issues/12747)) ([5221d4d](https://github.com/vuejs/vue/commit/5221d4d3b6049c87d196d99dbb64bcd3f3b07279)) +* directives shorthand normalize error ([#12744](https://github.com/vuejs/vue/issues/12744)) ([2263948](https://github.com/vuejs/vue/commit/2263948c249e7486403bc5880712e6d9fd15c17f)), closes [#12743](https://github.com/vuejs/vue/issues/12743) +* ensure render watcher of manually created instance is correctly tracked in owner scope ([bd89ce5](https://github.com/vuejs/vue/commit/bd89ce53a9de417a9372630bb5d433a40acc1a53)), closes [#12701](https://github.com/vuejs/vue/issues/12701) +* fix effect scope tracking for manually created instances ([7161176](https://github.com/vuejs/vue/commit/7161176cd0dff10d65ab58e266018aff2660610f)), closes [#12705](https://github.com/vuejs/vue/issues/12705) +* **ssr:** fix on-component directives rendering ([#12661](https://github.com/vuejs/vue/issues/12661)) ([165a14a](https://github.com/vuejs/vue/commit/165a14a6c6c406176037465d2961259c5c980399)), closes [#10733](https://github.com/vuejs/vue/issues/10733) +* **types:** allow attaching unknown options to defined component ([b4bf4c5](https://github.com/vuejs/vue/commit/b4bf4c52ad31e02307cfd4d643dc5610c893e3ba)), closes [#12742](https://github.com/vuejs/vue/issues/12742) +* **types:** fix missing error for accessing undefined instance properties ([8521f9d](https://github.com/vuejs/vue/commit/8521f9d3f63d26bde99b747f0cb14d0ac5ba5971)), closes [#12718](https://github.com/vuejs/vue/issues/12718) +* **types:** fix options suggestions when using defineComponent ([4b37b56](https://github.com/vuejs/vue/commit/4b37b568c7c3fd238aa61fcc956f882223f8e87f)), closes [#12736](https://github.com/vuejs/vue/issues/12736) +* **types:** Make SetupBindings optional on ExtendedVue and CombinedVueInstance ([#12727](https://github.com/vuejs/vue/issues/12727)) ([00458cd](https://github.com/vuejs/vue/commit/00458cd38d209410d3c675729230a42a0a34a4b9)), closes [#12726](https://github.com/vuejs/vue/issues/12726) [#12717](https://github.com/vuejs/vue/issues/12717) +* **watch:** avoid pre watcher firing on unmount ([f0057b1](https://github.com/vuejs/vue/commit/f0057b101e6451d5095cdb7fd6308fd31ac0450c)), closes [#12703](https://github.com/vuejs/vue/issues/12703) + + +### Features + +* **types:** enhance type for onErrorCaptured ([#12735](https://github.com/vuejs/vue/issues/12735)) ([bba6b3d](https://github.com/vuejs/vue/commit/bba6b3d6b4e3e26d28abbf20e74ec2f3e64f1a92)) +* **types:** export DefineComponent ([80d1baf](https://github.com/vuejs/vue/commit/80d1baf92050da411fb1bfe714401c498001dd36)), closes [#12748](https://github.com/vuejs/vue/issues/12748) +* **types:** support mixins inference for new Vue() ([#12737](https://github.com/vuejs/vue/issues/12737)) ([89a6b5e](https://github.com/vuejs/vue/commit/89a6b5e8658a6e3ae2cf649829901784ac9deb3c)), closes [#12730](https://github.com/vuejs/vue/issues/12730) + + + ## [2.7.8](https://github.com/vuejs/vue/compare/v2.7.7...v2.7.8) (2022-07-22) diff --git a/package.json b/package.json index 039c8882375..0bbc17d6b0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "2.7.8", + "version": "2.7.9", "packageManager": "pnpm@7.1.0", "description": "Reactive, component-oriented view layer for modern web interfaces.", "main": "dist/vue.runtime.common.js", diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index 6a6037a3111..3e88b96778c 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-sfc", - "version": "2.7.8", + "version": "2.7.9", "description": "compiler-sfc for Vue 2", "main": "dist/compiler-sfc.js", "types": "dist/compiler-sfc.d.ts", diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json index 5e8adfd83f6..784b7838cbe 100644 --- a/packages/server-renderer/package.json +++ b/packages/server-renderer/package.json @@ -1,6 +1,6 @@ { "name": "vue-server-renderer", - "version": "2.7.8", + "version": "2.7.9", "description": "server renderer for Vue 2.0", "main": "index.js", "types": "types/index.d.ts", diff --git a/packages/template-compiler/package.json b/packages/template-compiler/package.json index d1be678cdfb..26bf6586f94 100644 --- a/packages/template-compiler/package.json +++ b/packages/template-compiler/package.json @@ -1,6 +1,6 @@ { "name": "vue-template-compiler", - "version": "2.7.8", + "version": "2.7.9", "description": "template compiler for Vue 2.0", "main": "index.js", "unpkg": "browser.js", From 810f6d12edea47cde7f39eaf7ec3ae1b7300d40c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Sun, 21 Aug 2022 19:28:39 -0700 Subject: [PATCH 17/41] fix(types): Add missing type parameter constraints (#12754) --- types/options.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/types/options.d.ts b/types/options.d.ts index c82c6c5f8ae..9fcdaca624f 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -95,8 +95,8 @@ export type ThisTypedComponentOptionsWithArrayProps< Computed, PropNames extends string, SetupBindings, - Mixin, - Extends + Mixin extends ComponentOptionsMixin, + Extends extends ComponentOptionsMixin > = object & ComponentOptions< V, @@ -132,8 +132,8 @@ export type ThisTypedComponentOptionsWithRecordProps< Computed, Props, SetupBindings, - Mixin, - Extends + Mixin extends ComponentOptionsMixin, + Extends extends ComponentOptionsMixin > = object & ComponentOptions< V, From 46ca7bcddc06c50796ccff82d8c45693f1f14f47 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 23 Aug 2022 09:18:36 +0800 Subject: [PATCH 18/41] fix(compiler-sfc): avoid deindent when lang is jsx/tsx fix #12755 --- packages/compiler-sfc/src/parseComponent.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compiler-sfc/src/parseComponent.ts b/packages/compiler-sfc/src/parseComponent.ts index 65b858c9fc0..05489280e63 100644 --- a/packages/compiler-sfc/src/parseComponent.ts +++ b/packages/compiler-sfc/src/parseComponent.ts @@ -179,11 +179,11 @@ export function parseComponent( let text = source.slice(currentBlock.start, currentBlock.end) if ( options.deindent === true || - // by default, deindent unless it's script with default lang or ts + // by default, deindent unless it's script with default lang or (j/t)sx? (options.deindent !== false && !( currentBlock.type === 'script' && - (!currentBlock.lang || currentBlock.lang === 'ts') + (!currentBlock.lang || /^(j|t)sx?$/.test(currentBlock.lang)) )) ) { text = deindent(text) From e0b26c483a1ba407a818b1fcba1a439df24e84a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E8=A7=81=E6=9C=88?= <61452855+nooooooom@users.noreply.github.com> Date: Tue, 23 Aug 2022 09:22:45 +0800 Subject: [PATCH 19/41] fix: fix parent of multi-nested HOC $el not updating (#12757) fix #12589 --- src/core/instance/lifecycle.ts | 11 +++++++++-- test/unit/modules/vdom/patch/edge-cases.spec.ts | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/core/instance/lifecycle.ts b/src/core/instance/lifecycle.ts index df70b7113fd..94f42e27eb5 100644 --- a/src/core/instance/lifecycle.ts +++ b/src/core/instance/lifecycle.ts @@ -83,8 +83,15 @@ export function lifecycleMixin(Vue: typeof Component) { vm.$el.__vue__ = vm } // if parent is an HOC, update its $el as well - if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) { - vm.$parent.$el = vm.$el + let wrapper: Component | undefined = vm + while ( + wrapper && + wrapper.$vnode && + wrapper.$parent && + wrapper.$vnode === wrapper.$parent._vnode + ) { + wrapper.$parent.$el = wrapper.$el + wrapper = wrapper.$parent } // updated hook is called by the scheduler to ensure that children are // updated in a parent's updated hook. diff --git a/test/unit/modules/vdom/patch/edge-cases.spec.ts b/test/unit/modules/vdom/patch/edge-cases.spec.ts index 10b8ad8179d..f9295533b1e 100644 --- a/test/unit/modules/vdom/patch/edge-cases.spec.ts +++ b/test/unit/modules/vdom/patch/edge-cases.spec.ts @@ -257,11 +257,15 @@ describe('vdom patch: edge cases', () => { expect(vm.$refs.foo.$refs.bar.$el.tagName).toBe('DIV') expect(vm.$refs.foo.$refs.bar.$el.className).toBe(`hello`) + expect(vm.$el.tagName).toBe('DIV') + expect(vm.$el.className).toBe(`hello`) vm.$refs.foo.$refs.bar.ok = false waitForUpdate(() => { expect(vm.$refs.foo.$refs.bar.$el.tagName).toBe('SPAN') expect(vm.$refs.foo.$refs.bar.$el.className).toBe(`hello`) + expect(vm.$el.tagName).toBe('SPAN') + expect(vm.$el.className).toBe(`hello`) }).then(done) }) From ee57d9fd1d51abe245c6c37e6f8f2d45977b929e Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 23 Aug 2022 09:29:42 +0800 Subject: [PATCH 20/41] release: v2.7.10 --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- packages/compiler-sfc/package.json | 2 +- packages/server-renderer/package.json | 2 +- packages/template-compiler/package.json | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5d38472209..3ccae2540a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [2.7.10](https://github.com/vuejs/vue/compare/v2.7.9...v2.7.10) (2022-08-23) + + +### Bug Fixes + +* **compiler-sfc:** avoid deindent when lang is jsx/tsx ([46ca7bc](https://github.com/vuejs/vue/commit/46ca7bcddc06c50796ccff82d8c45693f1f14f47)), closes [#12755](https://github.com/vuejs/vue/issues/12755) +* fix parent of multi-nested HOC $el not updating ([#12757](https://github.com/vuejs/vue/issues/12757)) ([e0b26c4](https://github.com/vuejs/vue/commit/e0b26c483a1ba407a818b1fcba1a439df24e84a8)), closes [#12589](https://github.com/vuejs/vue/issues/12589) +* **types:** Add missing type parameter constraints ([#12754](https://github.com/vuejs/vue/issues/12754)) ([810f6d1](https://github.com/vuejs/vue/commit/810f6d12edea47cde7f39eaf7ec3ae1b7300d40c)) + + + ## [2.7.9](https://github.com/vuejs/vue/compare/v2.7.8...v2.7.9) (2022-08-19) diff --git a/package.json b/package.json index 0bbc17d6b0b..e8750ff7385 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "2.7.9", + "version": "2.7.10", "packageManager": "pnpm@7.1.0", "description": "Reactive, component-oriented view layer for modern web interfaces.", "main": "dist/vue.runtime.common.js", diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index 3e88b96778c..115ae6e775b 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-sfc", - "version": "2.7.9", + "version": "2.7.10", "description": "compiler-sfc for Vue 2", "main": "dist/compiler-sfc.js", "types": "dist/compiler-sfc.d.ts", diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json index 784b7838cbe..5470ebfaca3 100644 --- a/packages/server-renderer/package.json +++ b/packages/server-renderer/package.json @@ -1,6 +1,6 @@ { "name": "vue-server-renderer", - "version": "2.7.9", + "version": "2.7.10", "description": "server renderer for Vue 2.0", "main": "index.js", "types": "types/index.d.ts", diff --git a/packages/template-compiler/package.json b/packages/template-compiler/package.json index 26bf6586f94..75285bc0e85 100644 --- a/packages/template-compiler/package.json +++ b/packages/template-compiler/package.json @@ -1,6 +1,6 @@ { "name": "vue-template-compiler", - "version": "2.7.9", + "version": "2.7.10", "description": "template compiler for Vue 2.0", "main": "index.js", "unpkg": "browser.js", From d189b4beaa90b9fe58fd3f66ba9e09d340755196 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 1 Sep 2022 14:30:09 +0800 Subject: [PATCH 21/41] chore: special sponsor [ci skip] --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 5eef53d3fa0..cfaef3e361e 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,16 @@ You are looking at the repository for Vue 2. The repo for Vue 3 is [vuejs/core]( Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [backers](https://github.com/vuejs/core/blob/main/BACKERS.md). If you'd like to join them, please consider [ sponsor Vue's development](https://vuejs.org/sponsor/). +

+

Special Sponsor

+

+ +

+ + special sponsor appwrite + +

+

sponsors From 60d268c4261a0b9c5125f308468b31996a8145ad Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 1 Sep 2022 14:31:31 +0800 Subject: [PATCH 22/41] chore: cache bust svg [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cfaef3e361e..bca41a49405 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Vue.js is an MIT-licensed open source project with its ongoing development made

- sponsors + sponsors

From 2f335b2f9d09b962f40e38740826d444e4fff073 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2022 11:41:31 +0800 Subject: [PATCH 23/41] fix(sfc): remove sfc scoped deep syntax deprecation warnings --- .../compiler-sfc/src/stylePlugins/scoped.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/compiler-sfc/src/stylePlugins/scoped.ts b/packages/compiler-sfc/src/stylePlugins/scoped.ts index 25332731ef6..55f17c386f3 100644 --- a/packages/compiler-sfc/src/stylePlugins/scoped.ts +++ b/packages/compiler-sfc/src/stylePlugins/scoped.ts @@ -1,6 +1,5 @@ import { PluginCreator, Rule, AtRule } from 'postcss' import selectorParser from 'postcss-selector-parser' -import { warn } from '../warn' const animationNameRE = /^(-\w+-)?animation-name$/ const animationRE = /^(-\w+-)?animation$/ @@ -94,10 +93,10 @@ function rewriteSelector( ) { n.value = ' ' n.spaces.before = n.spaces.after = '' - warn( - `the >>> and /deep/ combinators have been deprecated. ` + - `Use :deep() instead.` - ) + // warn( + // `the >>> and /deep/ combinators have been deprecated. ` + + // `Use :deep() instead.` + // ) return false } @@ -126,12 +125,12 @@ function rewriteSelector( } selector.removeChild(n) } else { - // DEPRECATED usage + // DEPRECATED usage in v3 // .foo ::v-deep .bar -> .foo[xxxxxxx] .bar - warn( - `::v-deep usage as a combinator has ` + - `been deprecated. Use :deep() instead.` - ) + // warn( + // `::v-deep usage as a combinator has ` + + // `been deprecated. Use :deep() instead.` + // ) const prev = selector.at(selector.index(n) - 1) if (prev && isSpaceCombinator(prev)) { selector.removeChild(prev) From 27eed829ccf9978a63b8cd989ff4c03897276bc2 Mon Sep 17 00:00:00 2001 From: ZHAO Jinxiang Date: Mon, 10 Oct 2022 20:51:05 -0700 Subject: [PATCH 24/41] fix(types): vue 3 directive type compatibility (#12792) --- types/options.d.ts | 7 +++++++ types/vue.d.ts | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/types/options.d.ts b/types/options.d.ts index 9fcdaca624f..93379edbef2 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -4,6 +4,7 @@ import { SetupContext } from './v3-setup-context' import { DebuggerEvent } from './v3-generated' import { DefineComponent } from './v3-define-component' import { ComponentOptionsMixin } from './v3-component-options' +import { ObjectDirective, FunctionDirective } from './v3-directive' type Constructor = { new (...args: any[]): any @@ -318,6 +319,9 @@ export interface DirectiveBinding extends Readonly { readonly modifiers: { [key: string]: boolean } } +/** + * @deprecated use {@link FunctionDirective} instead + */ export type DirectiveFunction = ( el: HTMLElement, binding: DirectiveBinding, @@ -325,6 +329,9 @@ export type DirectiveFunction = ( oldVnode: VNode ) => void +/** + * @deprecated use {@link ObjectDirective} instead + */ export interface DirectiveOptions { bind?: DirectiveFunction inserted?: DirectiveFunction diff --git a/types/vue.d.ts b/types/vue.d.ts index 5a2027a0179..f158cf01716 100644 --- a/types/vue.d.ts +++ b/types/vue.d.ts @@ -22,6 +22,7 @@ import { ExtractComputedReturns, ComponentOptionsMixin } from './v3-component-options' +import { Directive, ObjectDirective } from './v3-directive' export interface CreateElement { ( @@ -338,6 +339,10 @@ export interface VueConstructor { id: string, definition?: DirectiveOptions | DirectiveFunction ): DirectiveOptions + directive( + id: string, + definition?: Directive + ): ObjectDirective filter(id: string, definition?: Function): Function component(id: string): VueConstructor From 4a0d88e46e4180edc7f22e36c25df3f8ac5d60d2 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2022 12:26:19 +0800 Subject: [PATCH 25/41] fix(reactivity): use WeakMap for proxy/raw checks, compat with non-extensible objects fix #12799 close #12798 --- src/core/observer/index.ts | 4 +++- src/v3/reactivity/reactive.ts | 9 +++++++-- src/v3/reactivity/readonly.ts | 10 +++++----- test/unit/features/v3/reactivity/reactive.spec.ts | 6 ++++++ test/unit/features/v3/reactivity/readonly.spec.ts | 7 +++++++ 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/core/observer/index.ts b/src/core/observer/index.ts index 3770c489a9f..dcf6bde1e76 100644 --- a/src/core/observer/index.ts +++ b/src/core/observer/index.ts @@ -17,6 +17,7 @@ import { noop } from '../util/index' import { isReadonly, isRef, TrackOpTypes, TriggerOpTypes } from '../../v3' +import { rawMap } from '../../v3/reactivity/reactive' const arrayKeys = Object.getOwnPropertyNames(arrayMethods) @@ -118,7 +119,8 @@ export function observe( (ssrMockReactivity || !isServerRendering()) && (isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && - !value.__v_skip /* ReactiveFlags.SKIP */ + !value.__v_skip /* ReactiveFlags.SKIP */ && + !rawMap.has(value) ) { ob = new Observer(value, shallow, ssrMockReactivity) } diff --git a/src/v3/reactivity/reactive.ts b/src/v3/reactivity/reactive.ts index 0ff682243a2..c65f25e67d8 100644 --- a/src/v3/reactivity/reactive.ts +++ b/src/v3/reactivity/reactive.ts @@ -5,10 +5,13 @@ import { isPrimitive, warn, toRawType, - isServerRendering + isServerRendering, + isObject } from 'core/util' import type { Ref, UnwrapRefSimple, RawSymbol } from './ref' +export const rawMap = new WeakMap() + export const enum ReactiveFlags { SKIP = '__v_skip', IS_READONLY = '__v_isReadonly', @@ -119,7 +122,9 @@ export function toRaw(observed: T): T { export function markRaw( value: T ): T & { [RawSymbol]?: true } { - def(value, ReactiveFlags.SKIP, true) + if (isObject(value)) { + rawMap.set(value, true) + } return value } diff --git a/src/v3/reactivity/readonly.ts b/src/v3/reactivity/readonly.ts index 933f910b69f..c3794bb53b3 100644 --- a/src/v3/reactivity/readonly.ts +++ b/src/v3/reactivity/readonly.ts @@ -32,8 +32,8 @@ export type DeepReadonly = T extends Builtin ? { readonly [K in keyof T]: DeepReadonly } : Readonly -const rawToReadonlyFlag = `__v_rawToReadonly` -const rawToShallowReadonlyFlag = `__v_rawToShallowReadonly` +const rawToReadonlyMap = new WeakMap() +const rawToShallowReadonlyMap = new WeakMap() export function readonly( target: T @@ -63,14 +63,14 @@ function createReadonly(target: any, shallow: boolean) { } // already has a readonly proxy - const existingFlag = shallow ? rawToShallowReadonlyFlag : rawToReadonlyFlag - const existingProxy = target[existingFlag] + const map = shallow ? rawToShallowReadonlyMap : rawToReadonlyMap + const existingProxy = map.get(target) if (existingProxy) { return existingProxy } const proxy = Object.create(Object.getPrototypeOf(target)) - def(target, existingFlag, proxy) + map.set(target, proxy) def(proxy, ReactiveFlags.IS_READONLY, true) def(proxy, ReactiveFlags.RAW, target) diff --git a/test/unit/features/v3/reactivity/reactive.spec.ts b/test/unit/features/v3/reactivity/reactive.spec.ts index f636d44d4a1..a8a30e0353c 100644 --- a/test/unit/features/v3/reactivity/reactive.spec.ts +++ b/test/unit/features/v3/reactivity/reactive.spec.ts @@ -258,6 +258,12 @@ describe('reactivity/reactive', () => { expect(isReactive(obj.bar)).toBe(false) }) + test('markRaw on non-extensible objects', () => { + const foo = Object.freeze({}) + markRaw(foo) + expect(isReactive(reactive(foo))).toBe(false) + }) + test('should not observe non-extensible objects', () => { const obj = reactive({ foo: Object.preventExtensions({ a: 1 }), diff --git a/test/unit/features/v3/reactivity/readonly.spec.ts b/test/unit/features/v3/reactivity/readonly.spec.ts index 012e142d451..fab99bbfbcb 100644 --- a/test/unit/features/v3/reactivity/readonly.spec.ts +++ b/test/unit/features/v3/reactivity/readonly.spec.ts @@ -525,4 +525,11 @@ describe('reactivity/readonly', () => { expect(readonlyFoo.x).toBe(1) expect(`et operation on key "x" failed`).toHaveBeenWarned() }) + + test('compatibility with non-extensible objects', () => { + const foo = Object.freeze({ a: 1 }) + const bar = readonly(foo) + expect(isReadonly(bar)).toBe(true) + expect(readonly(foo)).toBe(bar) + }) }) From 5d26f815c643d41e6ca6f29329593223b981fc24 Mon Sep 17 00:00:00 2001 From: Xiersa Date: Tue, 11 Oct 2022 13:26:20 +0800 Subject: [PATCH 26/41] fix(reactivity): check skip first before checking ref when creating observer (#12813) fix #12812 --- src/core/observer/index.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/core/observer/index.ts b/src/core/observer/index.ts index dcf6bde1e76..51f928c5c24 100644 --- a/src/core/observer/index.ts +++ b/src/core/observer/index.ts @@ -7,7 +7,6 @@ import { hasOwn, isArray, hasProto, - isObject, isPlainObject, isPrimitive, isUndef, @@ -108,23 +107,21 @@ export function observe( shallow?: boolean, ssrMockReactivity?: boolean ): Observer | void { - if (!isObject(value) || isRef(value) || value instanceof VNode) { - return + if (value && hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { + return value.__ob__ } - let ob: Observer | void - if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { - ob = value.__ob__ - } else if ( + if ( shouldObserve && (ssrMockReactivity || !isServerRendering()) && (isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value.__v_skip /* ReactiveFlags.SKIP */ && - !rawMap.has(value) + !rawMap.has(value) && + !isRef(value) && + !(value instanceof VNode) ) { - ob = new Observer(value, shallow, ssrMockReactivity) + return new Observer(value, shallow, ssrMockReactivity) } - return ob } /** From 87f69aa26f195390b948fbb0ff62cf954b58c82c Mon Sep 17 00:00:00 2001 From: Shiluo34 Date: Tue, 11 Oct 2022 13:36:50 +0800 Subject: [PATCH 27/41] fix(types): support Ref and function types in tsx ref attribute (#12759) fix #12758 --- types/test/v3/tsx-test.tsx | 7 ++++++- types/vnode.d.ts | 12 +++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/types/test/v3/tsx-test.tsx b/types/test/v3/tsx-test.tsx index 16a767acc5a..286c16997a0 100644 --- a/types/test/v3/tsx-test.tsx +++ b/types/test/v3/tsx-test.tsx @@ -1,4 +1,4 @@ -import { VNode, defineComponent } from '../../index' +import { VNode, defineComponent, ref } from '../../index' import { expectType } from '../utils' expectType(
) @@ -22,6 +22,11 @@ expectError(
) expectType(
) expectType(
) +// allow Ref type type on arbitrary element +const fooRef = ref() +expectType(
) +expectType(
{fooRef.value = el as HTMLElement}} />) + expectType( { diff --git a/types/vnode.d.ts b/types/vnode.d.ts index 7a543f0a39b..29bdb398c1c 100644 --- a/types/vnode.d.ts +++ b/types/vnode.d.ts @@ -1,5 +1,7 @@ import { Vue } from './vue' import { DirectiveFunction, DirectiveOptions } from './options' +import { Ref } from './v3-generated' +import { ComponentPublicInstance } from './v3-component-public-instance' /** * For extending allowed non-declared props on components in TSX @@ -65,11 +67,19 @@ export interface VNodeComponentOptions { tag?: string } +export type VNodeRef = + | string + | Ref + | (( + ref: Element | ComponentPublicInstance | null, + refs: Record + ) => void) + export interface VNodeData { key?: string | number slot?: string scopedSlots?: { [key: string]: ScopedSlot | undefined } - ref?: string + ref?: VNodeRef refInFor?: boolean tag?: string staticClass?: string From fb1393009660b38046b1f6dfb532b481cc53b3b7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 11 Oct 2022 14:09:10 +0800 Subject: [PATCH 28/41] fix(sfc): prune returned bindings for non-TS as well In Vue 3, pruning is only done for TS to produce valid code and tree-shaking is done by inlining the template for production. In Vue 2 we do not inline the template in production, so return binding pruning is needed in all cases. fix #12765 --- packages/compiler-sfc/src/compileScript.ts | 36 +++++++++---------- .../__snapshots__/compileScript.spec.ts.snap | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 094120c38f7..7d171a9b78a 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -284,11 +284,9 @@ export function compileScript( userImportAlias[imported] = local } - // template usage check is only needed in non-inline mode, so we can skip - // the work if inlineTemplate is true. let isUsedInTemplate = true - if (isTS && sfc.template && !sfc.template.src && !sfc.template.lang) { - isUsedInTemplate = isImportUsed(local, sfc) + if (sfc.template && !sfc.template.src && !sfc.template.lang) { + isUsedInTemplate = isImportUsed(local, sfc, isTS) } userImports[local] = { @@ -1782,7 +1780,7 @@ function getObjectOrArrayExpressionKeys(value: Node): string[] { const templateUsageCheckCache = new LRU(512) -function resolveTemplateUsageCheckString(sfc: SFCDescriptor) { +function resolveTemplateUsageCheckString(sfc: SFCDescriptor, isTS: boolean) { const { content } = sfc.template! const cached = templateUsageCheckCache.get(content) if (cached) { @@ -1809,7 +1807,7 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) { code += `,v${capitalize(camelize(baseName))}` } if (value) { - code += `,${processExp(value, baseName)}` + code += `,${processExp(value, isTS, baseName)}` } } } @@ -1817,7 +1815,7 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) { chars(text) { const res = parseText(text) if (res) { - code += `,${processExp(res.expression)}` + code += `,${processExp(res.expression, isTS)}` } } }) @@ -1829,8 +1827,8 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) { const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/ -function processExp(exp: string, dir?: string): string { - if (/ as\s+\w|<.*>|:/.test(exp)) { +function processExp(exp: string, isTS: boolean, dir?: string): string { + if (isTS && / as\s+\w|<.*>|:/.test(exp)) { if (dir === 'slot') { exp = `(${exp})=>{}` } else if (dir === 'on') { @@ -1839,7 +1837,7 @@ function processExp(exp: string, dir?: string): string { const inMatch = exp.match(forAliasRE) if (inMatch) { const [, LHS, RHS] = inMatch - return processExp(`(${LHS})=>{}`) + processExp(RHS) + return processExp(`(${LHS})=>{}`, true) + processExp(RHS, true) } } let ret = '' @@ -1867,36 +1865,38 @@ function stripTemplateString(str: string): string { return '' } -function isImportUsed(local: string, sfc: SFCDescriptor): boolean { +function isImportUsed( + local: string, + sfc: SFCDescriptor, + isTS: boolean +): boolean { return new RegExp( // #4274 escape $ since it's a special char in regex // (and is the only regex special char that is valid in identifiers) `[^\\w$_]${local.replace(/\$/g, '\\$')}[^\\w$_]` - ).test(resolveTemplateUsageCheckString(sfc)) + ).test(resolveTemplateUsageCheckString(sfc, isTS)) } /** * Note: this comparison assumes the prev/next script are already identical, - * and only checks the special case where