Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update #165

Merged
merged 12 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## [3.5.9](https://github.com/vuejs/core/compare/v3.5.8...v3.5.9) (2024-09-26)


### Bug Fixes

* **reactivity:** fix property dep removal regression ([6001e5c](https://github.com/vuejs/core/commit/6001e5c81a05c894586f9287fbd991677bdd0455)), closes [#12020](https://github.com/vuejs/core/issues/12020) [#12021](https://github.com/vuejs/core/issues/12021)
* **reactivity:** fix recursive sync watcher on computed edge case ([10ff159](https://github.com/vuejs/core/commit/10ff15924053d9bd95ad706f78ce09e288213fcf)), closes [#12033](https://github.com/vuejs/core/issues/12033) [#12037](https://github.com/vuejs/core/issues/12037)
* **runtime-core:** avoid rendering plain object as VNode ([#12038](https://github.com/vuejs/core/issues/12038)) ([cb34b28](https://github.com/vuejs/core/commit/cb34b28a4a9bf868be4785b001c526163eda342e)), closes [#12035](https://github.com/vuejs/core/issues/12035) [vitejs/vite-plugin-vue#353](https://github.com/vitejs/vite-plugin-vue/issues/353)
* **runtime-core:** make useId() always return a string ([a177092](https://github.com/vuejs/core/commit/a177092754642af2f98c33a4feffe8f198c3c950))
* **types:** correct type inference of union event names ([#12022](https://github.com/vuejs/core/issues/12022)) ([4da6881](https://github.com/vuejs/core/commit/4da688141d9e7c15b622c289deaa81b11845b2c7))
* **vue:** properly cache runtime compilation ([#12019](https://github.com/vuejs/core/issues/12019)) ([fa0ba24](https://github.com/vuejs/core/commit/fa0ba24b3ace02d7ecab65e57c2bea89a2550dcb))



## [3.5.8](https://github.com/vuejs/core/compare/v3.5.7...v3.5.8) (2024-09-22)


Expand Down
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"private": true,
"version": "3.5.8",
"version": "3.5.9",
"packageManager": "pnpm@9.10.0",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -61,12 +61,12 @@
"devDependencies": {
"@babel/parser": "catalog:",
"@babel/types": "catalog:",
"@rollup/plugin-alias": "^5.1.0",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-commonjs": "^26.0.3",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-replace": "5.0.4",
"@swc/core": "^1.7.26",
"@swc/core": "^1.7.28",
"@types/hash-sum": "^1.0.2",
"@types/node": "^20.16.5",
"@types/semver": "^7.5.8",
Expand All @@ -75,7 +75,7 @@
"@vue/consolidate": "1.0.0",
"conventional-changelog-cli": "^5.0.0",
"enquirer": "^2.4.1",
"esbuild": "^0.23.1",
"esbuild": "^0.24.0",
"esbuild-plugin-polyfill-node": "^0.3.0",
"eslint": "^9.10.0",
"eslint-plugin-import-x": "^4.2.1",
Expand All @@ -94,7 +94,7 @@
"pug": "^3.0.3",
"puppeteer": "~23.3.0",
"rimraf": "^6.0.1",
"rollup": "^4.21.3",
"rollup": "^4.22.4",
"rollup-plugin-dts": "^6.1.1",
"rollup-plugin-esbuild": "^6.1.1",
"rollup-plugin-polyfill-node": "^0.13.0",
Expand Down
18 changes: 18 additions & 0 deletions packages-private/dts-test/ref.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,24 @@ describe('allow getter and setter types to be unrelated', <T>() => {
f.value = ref(1)
})

describe('correctly unwraps nested refs', () => {
const obj = {
n: 24,
ref: ref(24),
nestedRef: ref({ n: ref(0) }),
}

const a = ref(obj)
expectType<number>(a.value.n)
expectType<number>(a.value.ref)
expectType<number>(a.value.nestedRef.n)

const b = reactive({ a })
expectType<number>(b.a.n)
expectType<number>(b.a.ref)
expectType<number>(b.a.nestedRef.n)
})

// computed
describe('allow computed getter and setter types to be unrelated', () => {
const obj = ref({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"vue": "^3.4.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.1.3",
"vite": "^5.4.5"
"@vitejs/plugin-vue": "^5.1.4",
"vite": "^5.4.8"
}
}
2 changes: 1 addition & 1 deletion packages/compiler-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/compiler-core",
"version": "3.5.8",
"version": "3.5.9",
"description": "@vue/compiler-core",
"main": "index.js",
"module": "dist/compiler-core.esm-bundler.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-dom/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/compiler-dom",
"version": "3.5.8",
"version": "3.5.9",
"description": "@vue/compiler-dom",
"main": "index.js",
"module": "dist/compiler-dom.esm-bundler.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-sfc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/compiler-sfc",
"version": "3.5.8",
"version": "3.5.9",
"description": "@vue/compiler-sfc",
"main": "dist/compiler-sfc.cjs.js",
"module": "dist/compiler-sfc.esm-browser.js",
Expand Down
19 changes: 5 additions & 14 deletions packages/compiler-sfc/src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { createCache } from './cache'
import type { ImportBinding } from './compileScript'
import { isImportUsed } from './script/importUsageCheck'
import type { LRUCache } from 'lru-cache'
import { genCacheKey } from '@vue/shared'

export const DEFAULT_FILENAME = 'anonymous.vue'

Expand Down Expand Up @@ -103,24 +104,14 @@ export const parseCache:
| Map<string, SFCParseResult>
| LRUCache<string, SFCParseResult> = createCache<SFCParseResult>()

function genCacheKey(source: string, options: SFCParseOptions): string {
return (
source +
JSON.stringify(
{
...options,
compiler: { parse: options.compiler?.parse },
},
(_, val) => (typeof val === 'function' ? val.toString() : val),
)
)
}

export function parse(
source: string,
options: SFCParseOptions = {},
): SFCParseResult {
const sourceKey = genCacheKey(source, options)
const sourceKey = genCacheKey(source, {
...options,
compiler: { parse: options.compiler?.parse },
})
const cache = parseCache.get(sourceKey)
if (cache) {
return cache
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-ssr/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/compiler-ssr",
"version": "3.5.8",
"version": "3.5.9",
"description": "@vue/compiler-ssr",
"main": "dist/compiler-ssr.cjs.js",
"types": "dist/compiler-ssr.d.ts",
Expand Down
70 changes: 70 additions & 0 deletions packages/reactivity/__tests__/computed.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1023,6 +1023,7 @@ describe('reactivity/computed', () => {
expect(p.value).toBe(3)
})

// #11995
test('computed dep cleanup should not cause property dep to be deleted', () => {
const toggle = ref(true)
const state = reactive({ a: 1 })
Expand All @@ -1037,4 +1038,73 @@ describe('reactivity/computed', () => {
state.a++
expect(pp.value).toBe(2)
})

// #12020
test('computed value updates correctly after dep cleanup', () => {
const obj = reactive({ foo: 1, flag: 1 })
const c1 = computed(() => obj.foo)

let foo
effect(() => {
foo = obj.flag ? (obj.foo, c1.value) : 0
})
expect(foo).toBe(1)

obj.flag = 0
expect(foo).toBe(0)

obj.foo = 2
obj.flag = 1
expect(foo).toBe(2)
})

// #11928
test('should not lead to exponential perf cost with deeply chained computed', () => {
const start = {
prop1: shallowRef(1),
prop2: shallowRef(2),
prop3: shallowRef(3),
prop4: shallowRef(4),
}

let layer = start

const LAYERS = 1000

for (let i = LAYERS; i > 0; i--) {
const m = layer
const s = {
prop1: computed(() => m.prop2.value),
prop2: computed(() => m.prop1.value - m.prop3.value),
prop3: computed(() => m.prop2.value + m.prop4.value),
prop4: computed(() => m.prop3.value),
}
effect(() => s.prop1.value)
effect(() => s.prop2.value)
effect(() => s.prop3.value)
effect(() => s.prop4.value)

s.prop1.value
s.prop2.value
s.prop3.value
s.prop4.value

layer = s
}

const t = performance.now()
start.prop1.value = 4
start.prop2.value = 3
start.prop3.value = 2
start.prop4.value = 1
expect(performance.now() - t).toBeLessThan(process.env.CI ? 100 : 30)

const end = layer
expect([
end.prop1.value,
end.prop2.value,
end.prop3.value,
end.prop4.value,
]).toMatchObject([-2, -4, 2, 3])
})
})
11 changes: 11 additions & 0 deletions packages/reactivity/__tests__/reactive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '../src/reactive'
import { computed } from '../src/computed'
import { effect } from '../src/effect'
import { targetMap } from '../src/dep'

describe('reactivity/reactive', () => {
test('Object', () => {
Expand Down Expand Up @@ -398,4 +399,14 @@ describe('reactivity/reactive', () => {
a.value++
}).not.toThrow()
})

// #11979
test('should release property Dep instance if it no longer has subscribers', () => {
let obj = { x: 1 }
let a = reactive(obj)
const e = effect(() => a.x)
expect(targetMap.get(obj)?.get('x')).toBeTruthy()
e.effect.stop()
expect(targetMap.get(obj)?.get('x')).toBeFalsy()
})
})
20 changes: 20 additions & 0 deletions packages/reactivity/__tests__/watch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
WatchErrorCodes,
type WatchOptions,
type WatchScheduler,
computed,
onWatcherCleanup,
ref,
watch,
Expand Down Expand Up @@ -209,4 +210,23 @@ describe('watch', () => {
source.value++
expect(dummy).toBe(1)
})

// #12033
test('recursive sync watcher on computed', () => {
const r = ref(0)
const c = computed(() => r.value)

watch(c, v => {
if (v > 1) {
r.value--
}
})

expect(r.value).toBe(0)
expect(c.value).toBe(0)

r.value = 10
expect(r.value).toBe(1)
expect(c.value).toBe(1)
})
})
2 changes: 1 addition & 1 deletion packages/reactivity/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/reactivity",
"version": "3.5.8",
"version": "3.5.9",
"description": "@vue/reactivity",
"main": "index.js",
"module": "dist/reactivity.esm-bundler.js",
Expand Down
6 changes: 5 additions & 1 deletion packages/reactivity/src/computed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,13 @@ export class ComputedRefImpl<T = any> implements Subscriber {
* @internal
*/
isSSR: boolean
/**
* @internal
*/
next?: Subscriber = undefined

// for backwards compat
effect: this = this

// dev only
onTrack?: (event: DebuggerEvent) => void
// dev only
Expand Down
46 changes: 26 additions & 20 deletions packages/reactivity/src/dep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ export class Dep {
map?: KeyToDepMap = undefined
key?: unknown = undefined

/**
* Subscriber counter
*/
sc: number = 0

constructor(public computed?: ComputedRefImpl | undefined) {
if (__DEV__) {
this.subsHead = undefined
Expand All @@ -113,9 +118,7 @@ export class Dep {
activeSub.depsTail = link
}

if (activeSub.flags & EffectFlags.TRACKING) {
addSub(link)
}
addSub(link)
} else if (link.version === -1) {
// reused from last run - already a sub, just sync version
link.version = this.version
Expand Down Expand Up @@ -197,27 +200,30 @@ export class Dep {
}

function addSub(link: Link) {
const computed = link.dep.computed
// computed getting its first subscriber
// enable tracking + lazily subscribe to all its deps
if (computed && !link.dep.subs) {
computed.flags |= EffectFlags.TRACKING | EffectFlags.DIRTY
for (let l = computed.deps; l; l = l.nextDep) {
addSub(l)
link.dep.sc++
if (link.sub.flags & EffectFlags.TRACKING) {
const computed = link.dep.computed
// computed getting its first subscriber
// enable tracking + lazily subscribe to all its deps
if (computed && !link.dep.subs) {
computed.flags |= EffectFlags.TRACKING | EffectFlags.DIRTY
for (let l = computed.deps; l; l = l.nextDep) {
addSub(l)
}
}
}

const currentTail = link.dep.subs
if (currentTail !== link) {
link.prevSub = currentTail
if (currentTail) currentTail.nextSub = link
}
const currentTail = link.dep.subs
if (currentTail !== link) {
link.prevSub = currentTail
if (currentTail) currentTail.nextSub = link
}

if (__DEV__ && link.dep.subsHead === undefined) {
link.dep.subsHead = link
}
if (__DEV__ && link.dep.subsHead === undefined) {
link.dep.subsHead = link
}

link.dep.subs = link
link.dep.subs = link
}
}

// The main WeakMap that stores {target -> key -> dep} connections.
Expand Down
Loading