Skip to content

Commit

Permalink
Merge branch 'main' into fix/suspence
Browse files Browse the repository at this point in the history
  • Loading branch information
yangxiuxiu1115 authored Jan 8, 2024
2 parents 1cd0b94 + 3bf34b7 commit c81cc14
Show file tree
Hide file tree
Showing 17 changed files with 471 additions and 123 deletions.
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@
"@rollup/plugin-terser": "^0.4.4",
"@types/hash-sum": "^1.0.2",
"@types/minimist": "^1.2.5",
"@types/node": "^20.10.6",
"@types/node": "^20.10.7",
"@types/semver": "^7.5.6",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
"@vitest/coverage-istanbul": "^1.1.1",
"@vitest/coverage-istanbul": "^1.1.3",
"@vue/consolidate": "0.17.3",
"conventional-changelog-cli": "^4.1.0",
"enquirer": "^2.4.1",
Expand All @@ -86,7 +86,7 @@
"eslint-plugin-jest": "^27.6.1",
"estree-walker": "^2.0.2",
"execa": "^8.0.1",
"jsdom": "^23.0.1",
"jsdom": "^23.2.0",
"lint-staged": "^15.2.0",
"lodash": "^4.17.21",
"magic-string": "^0.30.5",
Expand All @@ -98,7 +98,7 @@
"prettier": "^3.1.1",
"pretty-bytes": "^6.1.1",
"pug": "^3.0.2",
"puppeteer": "~21.6.1",
"puppeteer": "~21.7.0",
"rimraf": "^5.0.5",
"rollup": "^4.1.4",
"rollup-plugin-dts": "^6.1.0",
Expand All @@ -113,6 +113,6 @@
"tsx": "^4.7.0",
"typescript": "^5.2.2",
"vite": "^5.0.5",
"vitest": "^1.1.1"
"vitest": "^1.1.3"
}
}
2 changes: 1 addition & 1 deletion packages/reactivity/src/reactiveEffect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
// The main WeakMap that stores {target -> key -> dep} connections.
// Conceptually, it's easier to think of a dependency as a Dep class
// which maintains a Set of subscribers, but we simply store them as
// raw Sets to reduce memory overhead.
// raw Maps to reduce memory overhead.
type KeyToDepMap = Map<any, Dep>
const targetMap = new WeakMap<object, KeyToDepMap>()

Expand Down
44 changes: 44 additions & 0 deletions packages/runtime-core/__tests__/apiComputed.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
computed,
getCurrentInstance,
h,
nodeOps,
render,
} from '@vue/runtime-test'

describe('api: computed', () => {
test('should warn if getCurrentInstance is called inside computed getter', () => {
const Comp = {
setup() {
const c = computed(() => {
getCurrentInstance()
return 1
})
return () => c.value
},
}
render(h(Comp), nodeOps.createElement('div'))
expect(
'getCurrentInstance() called inside a computed getter',
).toHaveBeenWarned()
})

test('should warn if getCurrentInstance is called inside computed getter (object syntax)', () => {
const Comp = {
setup() {
const c = computed({
get: () => {
getCurrentInstance()
return 1
},
set: () => {},
})
return () => c.value
},
}
render(h(Comp), nodeOps.createElement('div'))
expect(
'getCurrentInstance() called inside a computed getter',
).toHaveBeenWarned()
})
})
32 changes: 32 additions & 0 deletions packages/runtime-core/__tests__/apiCreateApp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import {
getCurrentInstance,
h,
inject,
nextTick,
nodeOps,
onMounted,
provide,
ref,
resolveComponent,
resolveDirective,
serializeInner,
watch,
withDirectives,
} from '@vue/runtime-test'

Expand Down Expand Up @@ -551,6 +554,35 @@ describe('api: createApp', () => {
).not.toHaveBeenWarned()
})

// #10005
test('flush order edge case on nested createApp', async () => {
const order: string[] = []
const App = defineComponent({
setup(props) {
const message = ref('m1')
watch(
message,
() => {
order.push('post watcher')
},
{ flush: 'post' },
)
onMounted(() => {
message.value = 'm2'
createApp(() => '').mount(nodeOps.createElement('div'))
})
return () => {
order.push('render')
return h('div', [message.value])
}
},
})

createApp(App).mount(nodeOps.createElement('div'))
await nextTick()
expect(order).toMatchObject(['render', 'render', 'post watcher'])
})

// config.compilerOptions is tested in packages/vue since it is only
// supported in the full build.
})
32 changes: 31 additions & 1 deletion packages/runtime-core/__tests__/hydration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1431,11 +1431,35 @@ describe('SSR hydration', () => {
mountWithHydration(`<div style="color:red;"></div>`, () =>
h('div', { style: `color:red;` }),
)
mountWithHydration(
`<div style="color:red; font-size: 12px;"></div>`,
() => h('div', { style: `font-size: 12px; color:red;` }),
)
mountWithHydration(`<div style="color:red;display:none;"></div>`, () =>
withDirectives(createVNode('div', { style: 'color: red' }, ''), [
[vShow, false],
]),
)
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
mountWithHydration(`<div style="color:red;"></div>`, () =>
h('div', { style: { color: 'green' } }),
)
expect(`Hydration style mismatch`).toHaveBeenWarned()
expect(`Hydration style mismatch`).toHaveBeenWarnedTimes(1)
})

test('style mismatch w/ v-show', () => {
mountWithHydration(`<div style="color:red;display:none"></div>`, () =>
withDirectives(createVNode('div', { style: 'color: red' }, ''), [
[vShow, false],
]),
)
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
mountWithHydration(`<div style="color:red;"></div>`, () =>
withDirectives(createVNode('div', { style: 'color: red' }, ''), [
[vShow, false],
]),
)
expect(`Hydration style mismatch`).toHaveBeenWarnedTimes(1)
})

test('attr mismatch', () => {
Expand All @@ -1451,6 +1475,12 @@ describe('SSR hydration', () => {
mountWithHydration(`<select multiple></div>`, () =>
h('select', { multiple: 'multiple' }),
)
mountWithHydration(`<textarea>foo</textarea>`, () =>
h('textarea', { value: 'foo' }),
)
mountWithHydration(`<textarea></textarea>`, () =>
h('textarea', { value: '' }),
)
expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()

mountWithHydration(`<div></div>`, () => h('div', { id: 'foo' }))
Expand Down
12 changes: 12 additions & 0 deletions packages/runtime-core/__tests__/rendererFragment.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,4 +351,16 @@ describe('renderer: fragment', () => {
render(renderFn(['two', 'one']), root)
expect(serializeInner(root)).toBe(`text<div>two</div>text<div>one</div>`)
})

// #10007
test('empty fragment', () => {
const root = nodeOps.createElement('div')

const renderFn = () => {
return openBlock(true), createBlock(Fragment, null)
}

render(renderFn(), root)
expect(serializeInner(root)).toBe('')
})
})
21 changes: 21 additions & 0 deletions packages/runtime-core/__tests__/scheduler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,4 +610,25 @@ describe('scheduler', () => {
expect(await p).toBe(1)
expect(fn).toHaveBeenCalledTimes(1)
})

// #10003
test('nested flushPostFlushCbs', async () => {
const calls: string[] = []
const cb1 = () => calls.push('cb1')
// cb1 has no id
const cb2 = () => calls.push('cb2')
cb2.id = -1
const queueAndFlush = (hook: Function) => {
queuePostFlushCb(hook)
flushPostFlushCbs()
}

queueAndFlush(() => {
queuePostFlushCb([cb1, cb2])
flushPostFlushCbs()
})

await nextTick()
expect(calls).toEqual(['cb2', 'cb1'])
})
})
38 changes: 35 additions & 3 deletions packages/runtime-core/src/apiComputed.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,42 @@
import { computed as _computed } from '@vue/reactivity'
import {
type ComputedGetter,
type WritableComputedOptions,
computed as _computed,
} from '@vue/reactivity'
import { isInSSRComponentSetup } from './component'
import { isFunction } from '@vue/shared'

/**
* For dev warning only.
* Context: https://github.com/vuejs/core/discussions/9974
*/
export let isInComputedGetter = false

function wrapComputedGetter(
getter: ComputedGetter<unknown>,
): ComputedGetter<unknown> {
return () => {
isInComputedGetter = true
try {
return getter()
} finally {
isInComputedGetter = false
}
}
}

export const computed: typeof _computed = (
getterOrOptions: any,
getterOrOptions: ComputedGetter<unknown> | WritableComputedOptions<unknown>,
debugOptions?: any,
) => {
// @ts-expect-error
if (__DEV__) {
if (isFunction(getterOrOptions)) {
getterOrOptions = wrapComputedGetter(getterOrOptions)
} else {
getterOrOptions.get = wrapComputedGetter(getterOrOptions.get)
}
}

// @ts-expect-error the 3rd argument is hidden from public types
return _computed(getterOrOptions, debugOptions, isInSSRComponentSetup)
}
15 changes: 13 additions & 2 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ import {
} from './compat/compatConfig'
import type { SchedulerJob } from './scheduler'
import type { LifecycleHooks } from './enums'
import { isInComputedGetter } from './apiComputed'

export type Data = Record<string, unknown>

Expand Down Expand Up @@ -631,8 +632,18 @@ export function createComponentInstance(

export let currentInstance: ComponentInternalInstance | null = null

export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
currentInstance || currentRenderingInstance
export const getCurrentInstance: () => ComponentInternalInstance | null =
() => {
if (__DEV__ && isInComputedGetter) {
warn(
`getCurrentInstance() called inside a computed getter. ` +
`This is incorrect usage as computed getters are not guaranteed ` +
`to be executed with an active component instance. If you are using ` +
`a composable inside a computed getter, move it ouside to the setup scope.`,
)
}
return currentInstance || currentRenderingInstance
}

let internalSetCurrentInstance: (
instance: ComponentInternalInstance | null,
Expand Down
19 changes: 13 additions & 6 deletions packages/runtime-core/src/components/Suspense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,6 @@ export interface SuspenseBoundary {
namespace: ElementNamespace
container: RendererElement
hiddenContainer: RendererElement
anchor: RendererNode | null
activeBranch: VNode | null
pendingBranch: VNode | null
deps: number
Expand Down Expand Up @@ -473,14 +472,14 @@ function createSuspenseBoundary(
assertNumber(timeout, `Suspense timeout`)
}

const initialAnchor = anchor
const suspense: SuspenseBoundary = {
vnode,
parent: parentSuspense,
parentComponent,
namespace,
container,
hiddenContainer,
anchor,
deps: 0,
pendingId: suspenseId++,
timeout: typeof timeout === 'number' ? timeout : -1,
Expand Down Expand Up @@ -529,20 +528,28 @@ function createSuspenseBoundary(
move(
pendingBranch!,
container,
next(activeBranch!),
anchor === initialAnchor ? next(activeBranch!) : anchor,
MoveType.ENTER,
)
queuePostFlushCb(effects)
}
}
}
// this is initial anchor on mount
let { anchor } = suspense
// unmount current active tree
if (activeBranch) {
// if the fallback tree was mounted, it may have been moved
// as part of a parent suspense. get the latest anchor for insertion
anchor = next(activeBranch)
// #8105 if `delayEnter` is true, it means that the mounting of
// `activeBranch` will be delayed. if the branch switches before
// transition completes, both `activeBranch` and `pendingBranch` may
// coexist in the `hiddenContainer`. This could result in
// `next(activeBranch!)` obtaining an incorrect anchor
// (got `pendingBranch.el`).
// Therefore, after the mounting of activeBranch is completed,
// it is necessary to get the latest anchor.
if (parentNode(activeBranch.el!) !== suspense.hiddenContainer) {
anchor = next(activeBranch)
}
unmount(activeBranch, parentComponent, suspense, true)
}
if (!delayEnter) {
Expand Down
Loading

0 comments on commit c81cc14

Please sign in to comment.