mirror of https://github.com/vuejs/core.git
Merge branch 'minor' into edison/feat/fowardedSlots
This commit is contained in:
commit
f5a8fc50aa
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,3 +1,20 @@
|
|||
# [3.6.0-alpha.2](https://github.com/vuejs/core/compare/v3.6.0-alpha.1...v3.6.0-alpha.2) (2025-07-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-vapor:** handle empty interpolation ([#13592](https://github.com/vuejs/core/issues/13592)) ([d1f2915](https://github.com/vuejs/core/commit/d1f2915cfe7915fa73624485ff3dd443176a31a9))
|
||||
* **compiler-vapor:** handle special characters in cached variable names ([#13626](https://github.com/vuejs/core/issues/13626)) ([a5e106d](https://github.com/vuejs/core/commit/a5e106d96eb17d73c8673e826393c910d5594a2f))
|
||||
* **compiler-vapor:** selectors was not initialized in time when the initial value of createFor source was not empty ([#13642](https://github.com/vuejs/core/issues/13642)) ([f04c9c3](https://github.com/vuejs/core/commit/f04c9c342d398c11111c873143dc437f588578ee))
|
||||
* **reactivity:** allow collect effects in EffectScope ([#13657](https://github.com/vuejs/core/issues/13657)) ([b9fb79a](https://github.com/vuejs/core/commit/b9fb79a1fd099b67e01c5fe5941551c0da3a0cae)), closes [#13656](https://github.com/vuejs/core/issues/13656)
|
||||
* **reactivity:** remove link check to align with 3.5 ([#13654](https://github.com/vuejs/core/issues/13654)) ([3cb27d1](https://github.com/vuejs/core/commit/3cb27d156f6a30e8f950616a53a3726519eaf216)), closes [#13620](https://github.com/vuejs/core/issues/13620)
|
||||
* **runtime-core:** use __vapor instead of vapor to identify Vapor components ([#13652](https://github.com/vuejs/core/issues/13652)) ([ad21b1b](https://github.com/vuejs/core/commit/ad21b1b7e96bc894f5df0d95fbd77c9ba6b15c2e))
|
||||
* **runtime-vapor:** component emits vdom interop ([#13498](https://github.com/vuejs/core/issues/13498)) ([d95fc18](https://github.com/vuejs/core/commit/d95fc186c26e81345cd75037c3c1304b0eae13b4))
|
||||
* **runtime-vapor:** handle v-model vdom interop error ([#13643](https://github.com/vuejs/core/issues/13643)) ([2be828a](https://github.com/vuejs/core/commit/2be828a0c165c7f1533ace0bd81fba43a2af16d6))
|
||||
* **runtime-vapor:** remove access globalProperties warning ([#13609](https://github.com/vuejs/core/issues/13609)) ([fca74b0](https://github.com/vuejs/core/commit/fca74b00a86c6039aa05591618539a77aaa72daf))
|
||||
|
||||
|
||||
|
||||
# [3.6.0-alpha.1](https://github.com/vuejs/core/compare/v3.5.17...v3.6.0-alpha.1) (2025-07-12)
|
||||
|
||||
### Features
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"private": true,
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"packageManager": "pnpm@10.12.4",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-core",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/compiler-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-core.esm-bundler.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-dom",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/compiler-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/compiler-dom.esm-bundler.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-sfc",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/compiler-sfc",
|
||||
"main": "dist/compiler-sfc.cjs.js",
|
||||
"module": "dist/compiler-sfc.esm-browser.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-ssr",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/compiler-ssr",
|
||||
"main": "dist/compiler-ssr.cjs.js",
|
||||
"types": "dist/compiler-ssr.d.ts",
|
||||
|
|
|
@ -11,6 +11,53 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: expression > empty interpolation 1`] = `
|
||||
"import { template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = t0()
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: expression > empty interpolation 2`] = `
|
||||
"import { template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = t0()
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: expression > empty interpolation 3`] = `
|
||||
"import { template as _template } from 'vue';
|
||||
const t0 = _template("<div></div>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
const n0 = t0()
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: expression > empty interpolation 4`] = `
|
||||
"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div> </div>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
const n1 = t0()
|
||||
const n0 = _child(n1)
|
||||
const x1 = _child(n1)
|
||||
_renderEffect(() => {
|
||||
const _foo = _ctx.foo
|
||||
_setText(n0, _toDisplayString(_foo))
|
||||
_setText(x1, _toDisplayString(_foo))
|
||||
})
|
||||
return n1
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: expression > props 1`] = `
|
||||
"import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template(" ")
|
||||
|
|
|
@ -43,7 +43,7 @@ export function render(_ctx) {
|
|||
const n2 = t0()
|
||||
_setTemplateRef(n2, "foo", void 0, true)
|
||||
return n2
|
||||
}, null, 4)
|
||||
}, undefined, 4)
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -93,7 +93,7 @@ export function render(_ctx) {
|
|||
const x4 = _child(n4)
|
||||
_renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value)))
|
||||
return n4
|
||||
}, null, 1)
|
||||
}, undefined, 1)
|
||||
return n5
|
||||
})
|
||||
return n0
|
||||
|
@ -150,6 +150,7 @@ exports[`compiler: v-for > selector pattern 1`] = `
|
|||
const t0 = _template("<tr> </tr>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
let _selector0_0
|
||||
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
|
||||
const n2 = t0()
|
||||
const x2 = _child(n2)
|
||||
|
@ -157,8 +158,9 @@ export function render(_ctx) {
|
|||
_setText(x2, _toDisplayString(_ctx.selected === _for_item0.value.id ? 'danger' : ''))
|
||||
})
|
||||
return n2
|
||||
}, (row) => (row.id))
|
||||
const _selector0_0 = n0.useSelector(() => _ctx.selected)
|
||||
}, (row) => (row.id), undefined, ({ createSelector }) => {
|
||||
_selector0_0 = createSelector(() => _ctx.selected)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
@ -168,14 +170,16 @@ exports[`compiler: v-for > selector pattern 2`] = `
|
|||
const t0 = _template("<tr></tr>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
let _selector0_0
|
||||
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
|
||||
const n2 = t0()
|
||||
_selector0_0(() => {
|
||||
_setClass(n2, _ctx.selected === _for_item0.value.id ? 'danger' : '')
|
||||
})
|
||||
return n2
|
||||
}, (row) => (row.id))
|
||||
const _selector0_0 = n0.useSelector(() => _ctx.selected)
|
||||
}, (row) => (row.id), undefined, ({ createSelector }) => {
|
||||
_selector0_0 = createSelector(() => _ctx.selected)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
@ -202,14 +206,16 @@ exports[`compiler: v-for > selector pattern 4`] = `
|
|||
const t0 = _template("<tr></tr>", true)
|
||||
|
||||
export function render(_ctx) {
|
||||
let _selector0_0
|
||||
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
|
||||
const n2 = t0()
|
||||
_selector0_0(() => {
|
||||
_setClass(n2, { danger: _for_item0.value.id === _ctx.selected })
|
||||
})
|
||||
return n2
|
||||
}, (row) => (row.id))
|
||||
const _selector0_0 = n0.useSelector(() => _ctx.selected)
|
||||
}, (row) => (row.id), undefined, ({ createSelector }) => {
|
||||
_selector0_0 = createSelector(() => _ctx.selected)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -12,6 +12,21 @@ export function render(_ctx) {
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > component event with special characters 1`] = `
|
||||
"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
|
||||
|
||||
export function render(_ctx) {
|
||||
const _component_Foo = _resolveComponent("Foo")
|
||||
const _on_update_model = () => {}
|
||||
const _on_update_model1 = () => {}
|
||||
const n0 = _createComponentWithFallback(_component_Foo, {
|
||||
"onUpdate:model": () => _on_update_model,
|
||||
"onUpdate-model": () => _on_update_model1
|
||||
}, null, true)
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`v-on > dynamic arg 1`] = `
|
||||
"import { on as _on, renderEffect as _renderEffect, template as _template } from 'vue';
|
||||
const t0 = _template("<div></div>", true)
|
||||
|
|
|
@ -68,7 +68,7 @@ export function render(_ctx) {
|
|||
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
|
||||
const n2 = t0()
|
||||
return n2
|
||||
}, null, 4)
|
||||
}, undefined, 4)
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
|
|
@ -47,4 +47,25 @@ describe('compiler: expression', () => {
|
|||
expect(code).toMatchSnapshot()
|
||||
expect(code).contains(`_String(_foo.id++)`)
|
||||
})
|
||||
|
||||
test('empty interpolation', () => {
|
||||
const { code } = compileWithExpression(`{{}}`)
|
||||
const { code: code2 } = compileWithExpression(`{{ }}`)
|
||||
const { code: code3 } = compileWithExpression(`<div>{{ }}</div>`)
|
||||
const { code: code4 } = compileWithExpression(`<div>{{ foo }}{{ }}</div>`)
|
||||
|
||||
expect(code).toMatchSnapshot()
|
||||
expect(code).not.toContain(`_toDisplayString`)
|
||||
expect(code).not.toContain(`_setText`)
|
||||
|
||||
expect(code2).toMatchSnapshot()
|
||||
expect(code2).not.toContain(`_toDisplayString`)
|
||||
expect(code2).not.toContain(`_setText`)
|
||||
|
||||
expect(code3).toMatchSnapshot()
|
||||
expect(code3).not.toContain(`_toDisplayString`)
|
||||
expect(code3).not.toContain(`_setText`)
|
||||
|
||||
expect(code4).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -695,4 +695,16 @@ describe('v-on', () => {
|
|||
expect(code).matchSnapshot()
|
||||
expect(code).include('n0.$evtclick = e => _ctx.handleClick(e)')
|
||||
})
|
||||
|
||||
test('component event with special characters', () => {
|
||||
const { code } = compileWithVOn(
|
||||
`<Foo @update:model="() => {}" @update-model="() => {}" />`,
|
||||
)
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('const _on_update_model = () => {}')
|
||||
expect(code).contains('const _on_update_model1 = () => {}')
|
||||
expect(code).contains('"onUpdate:model": () => _on_update_model')
|
||||
expect(code).contains('"onUpdate-model": () => _on_update_model1')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compiler-vapor",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/compiler-vapor",
|
||||
"main": "dist/compiler-vapor.cjs.js",
|
||||
"module": "dist/compiler-vapor.esm-bundler.js",
|
||||
|
|
|
@ -26,7 +26,7 @@ import {
|
|||
genCall,
|
||||
genMulti,
|
||||
} from './utils'
|
||||
import { genExpression } from './expression'
|
||||
import { genExpression, genVarName } from './expression'
|
||||
import { genPropKey, genPropValue } from './prop'
|
||||
import {
|
||||
type SimpleExpressionNode,
|
||||
|
@ -102,6 +102,7 @@ export function genCreateComponent(
|
|||
|
||||
function getUniqueHandlerName(context: CodegenContext, name: string): string {
|
||||
const { seenInlineHandlerNames } = context
|
||||
name = genVarName(name)
|
||||
const count = seenInlineHandlerNames[name] || 0
|
||||
seenInlineHandlerNames[name] = count + 1
|
||||
return count === 0 ? name : `${name}${count}`
|
||||
|
|
|
@ -647,7 +647,7 @@ function parseExp(context: CodegenContext, content: string): Node {
|
|||
return parseExpression(`(${content})`, options)
|
||||
}
|
||||
|
||||
function genVarName(exp: string): string {
|
||||
export function genVarName(exp: string): string {
|
||||
return `${exp
|
||||
.replace(/[^a-zA-Z0-9]/g, '_')
|
||||
.replace(/_+/g, '_')
|
||||
|
|
|
@ -99,19 +99,27 @@ export function genFor(
|
|||
keyProp,
|
||||
idMap,
|
||||
)
|
||||
const patternFrag: CodeFragment[] = []
|
||||
const selectorDeclarations: CodeFragment[] = []
|
||||
const selectorSetup: CodeFragment[] = []
|
||||
|
||||
for (let i = 0; i < selectorPatterns.length; i++) {
|
||||
const { selector } = selectorPatterns[i]
|
||||
const selectorName = `_selector${id}_${i}`
|
||||
patternFrag.push(
|
||||
selectorDeclarations.push(`let ${selectorName}`, NEWLINE)
|
||||
if (i === 0) {
|
||||
selectorSetup.push(`({ createSelector }) => {`, INDENT_START)
|
||||
}
|
||||
selectorSetup.push(
|
||||
NEWLINE,
|
||||
`const ${selectorName} = `,
|
||||
...genCall(`n${id}.useSelector`, [
|
||||
`${selectorName} = `,
|
||||
...genCall(`createSelector`, [
|
||||
`() => `,
|
||||
...genExpression(selector, context),
|
||||
]),
|
||||
)
|
||||
if (i === selectorPatterns.length - 1) {
|
||||
selectorSetup.push(INDENT_END, NEWLINE, '}')
|
||||
}
|
||||
}
|
||||
|
||||
const blockFn = context.withId(() => {
|
||||
|
@ -165,16 +173,17 @@ export function genFor(
|
|||
|
||||
return [
|
||||
NEWLINE,
|
||||
...selectorDeclarations,
|
||||
`const n${id} = `,
|
||||
...genCall(
|
||||
helper('createFor'),
|
||||
[helper('createFor'), 'undefined'],
|
||||
sourceExpr,
|
||||
blockFn,
|
||||
genCallback(keyProp),
|
||||
flags ? String(flags) : undefined,
|
||||
selectorSetup.length ? selectorSetup : undefined,
|
||||
// todo: hydrationNode
|
||||
),
|
||||
...patternFrag,
|
||||
]
|
||||
|
||||
// construct a id -> accessor path map.
|
||||
|
|
|
@ -87,7 +87,8 @@ export const transformText: NodeTransform = (node, context) => {
|
|||
}
|
||||
|
||||
function processInterpolation(context: TransformContext<InterpolationNode>) {
|
||||
const children = context.parent!.node.children
|
||||
const parentNode = context.parent!.node
|
||||
const children = parentNode.children
|
||||
const nexts = children.slice(context.index)
|
||||
const idx = nexts.findIndex(n => !isTextLike(n))
|
||||
const nodes = (idx > -1 ? nexts.slice(0, idx) : nexts) as Array<TextLike>
|
||||
|
@ -97,10 +98,18 @@ function processInterpolation(context: TransformContext<InterpolationNode>) {
|
|||
if (prev && prev.type === NodeTypes.TEXT) {
|
||||
nodes.unshift(prev)
|
||||
}
|
||||
const values = processTextLikeChildren(nodes, context)
|
||||
|
||||
if (values.length === 0 && parentNode.type !== NodeTypes.ROOT) {
|
||||
return
|
||||
}
|
||||
|
||||
context.template += ' '
|
||||
const id = context.reference()
|
||||
const values = nodes.map(node => createTextLikeExpression(node, context))
|
||||
|
||||
if (values.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const nonConstantExps = values.filter(v => !isConstantExpression(v))
|
||||
const isStatic =
|
||||
|
@ -129,8 +138,10 @@ function processTextContainer(
|
|||
children: TextLike[],
|
||||
context: TransformContext<ElementNode>,
|
||||
) {
|
||||
const values = children.map(child => createTextLikeExpression(child, context))
|
||||
const values = processTextLikeChildren(children, context)
|
||||
|
||||
const literals = values.map(getLiteralExpressionValue)
|
||||
|
||||
if (literals.every(l => l != null)) {
|
||||
context.childrenTemplate = literals.map(l => String(l))
|
||||
} else {
|
||||
|
@ -149,13 +160,22 @@ function processTextContainer(
|
|||
}
|
||||
}
|
||||
|
||||
function createTextLikeExpression(node: TextLike, context: TransformContext) {
|
||||
markNonTemplate(node, context)
|
||||
if (node.type === NodeTypes.TEXT) {
|
||||
return createSimpleExpression(node.content, true, node.loc)
|
||||
} else {
|
||||
return node.content as SimpleExpressionNode
|
||||
function processTextLikeChildren(nodes: TextLike[], context: TransformContext) {
|
||||
const exps: SimpleExpressionNode[] = []
|
||||
for (const node of nodes) {
|
||||
let exp: SimpleExpressionNode
|
||||
markNonTemplate(node, context)
|
||||
|
||||
if (node.type === NodeTypes.TEXT) {
|
||||
exp = createSimpleExpression(node.content, true, node.loc)
|
||||
} else {
|
||||
exp = node.content as SimpleExpressionNode
|
||||
}
|
||||
|
||||
if (exp.content) exps.push(exp)
|
||||
}
|
||||
|
||||
return exps
|
||||
}
|
||||
|
||||
function isTextLike(node: TemplateChildNode): node is TextLike {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/reactivity",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/reactivity",
|
||||
"main": "index.js",
|
||||
"module": "dist/reactivity.esm-bundler.js",
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
import { EffectFlags, cleanup } from './effect'
|
||||
import {
|
||||
type Link,
|
||||
type ReactiveNode,
|
||||
link,
|
||||
setActiveSub,
|
||||
unlink,
|
||||
} from './system'
|
||||
import { type Link, type ReactiveNode, link, unlink } from './system'
|
||||
import { warn } from './warning'
|
||||
|
||||
export let activeEffectScope: EffectScope | undefined
|
||||
|
@ -65,14 +59,12 @@ export class EffectScope implements ReactiveNode {
|
|||
}
|
||||
|
||||
run<T>(fn: () => T): T | undefined {
|
||||
const prevSub = setActiveSub()
|
||||
const prevScope = activeEffectScope
|
||||
try {
|
||||
activeEffectScope = this
|
||||
return fn()
|
||||
} finally {
|
||||
activeEffectScope = prevScope
|
||||
setActiveSub(prevSub)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -77,14 +77,8 @@ export function link(dep: ReactiveNode, sub: ReactiveNode): void {
|
|||
return
|
||||
}
|
||||
}
|
||||
// TODO: maybe can find a good way to check duplicate link
|
||||
const prevSub = dep.subsTail
|
||||
if (
|
||||
prevSub !== undefined &&
|
||||
prevSub.sub === sub &&
|
||||
(!recursedCheck || isValidLink(prevSub, sub))
|
||||
) {
|
||||
return
|
||||
}
|
||||
const newLink =
|
||||
(sub.depsTail =
|
||||
dep.subsTail =
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/runtime-core",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/runtime-core",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-core.esm-bundler.js",
|
||||
|
|
|
@ -119,7 +119,7 @@ function reload(id: string, newComp: HMRComponent): void {
|
|||
// create a snapshot which avoids the set being mutated during updates
|
||||
const instances = [...record.instances]
|
||||
|
||||
if (newComp.vapor) {
|
||||
if (newComp.__vapor) {
|
||||
for (const instance of instances) {
|
||||
instance.hmrReload!(newComp)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/runtime-dom",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/runtime-dom",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-dom.esm-bundler.js",
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { ref } from '@vue/reactivity'
|
||||
import { makeRender } from './_utils'
|
||||
// @ts-expect-error
|
||||
import { createFor, createSelector, renderEffect } from '../src'
|
||||
import { createFor } from '../src'
|
||||
import { nextTick } from '@vue/runtime-dom'
|
||||
|
||||
const define = makeRender()
|
||||
|
||||
describe.todo('api: createSelector', () => {
|
||||
describe('api: createSelector', () => {
|
||||
test('basic', async () => {
|
||||
let calledTimes = 0
|
||||
let expectedCalledTimes = 0
|
||||
|
@ -15,19 +14,23 @@ describe.todo('api: createSelector', () => {
|
|||
const index = ref(0)
|
||||
|
||||
const { host } = define(() => {
|
||||
const isSleected = createSelector(index)
|
||||
let selector: (cb: () => void) => void
|
||||
return createFor(
|
||||
() => list.value,
|
||||
item => {
|
||||
const span = document.createElement('li')
|
||||
renderEffect(() => {
|
||||
selector(() => {
|
||||
calledTimes += 1
|
||||
const { id } = item.value
|
||||
span.textContent = `${id}.${isSleected(id) ? 't' : 'f'}`
|
||||
span.textContent = `${id}.${id === index.value ? 't' : 'f'}`
|
||||
})
|
||||
return span
|
||||
},
|
||||
item => item.id,
|
||||
undefined,
|
||||
({ createSelector }) => {
|
||||
selector = createSelector(() => index.value)
|
||||
},
|
||||
)
|
||||
}).render()
|
||||
|
||||
|
@ -50,66 +53,11 @@ describe.todo('api: createSelector', () => {
|
|||
)
|
||||
expect(calledTimes).toBe((expectedCalledTimes += 2))
|
||||
|
||||
list.value[2].id = 3
|
||||
await nextTick()
|
||||
expect(host.innerHTML).toBe(
|
||||
'<li>0.f</li><li>1.f</li><li>3.f</li><!--for-->',
|
||||
)
|
||||
expect(calledTimes).toBe((expectedCalledTimes += 1))
|
||||
})
|
||||
|
||||
test('custom compare', async () => {
|
||||
let calledTimes = 0
|
||||
let expectedCalledTimes = 0
|
||||
|
||||
const list = ref([{ id: 1 }, { id: 2 }, { id: 3 }])
|
||||
const index = ref(0)
|
||||
|
||||
const { host } = define(() => {
|
||||
const isSleected = createSelector(
|
||||
index,
|
||||
// @ts-expect-error
|
||||
(key, value) => key === value + 1,
|
||||
)
|
||||
return createFor(
|
||||
() => list.value,
|
||||
item => {
|
||||
const span = document.createElement('li')
|
||||
renderEffect(() => {
|
||||
calledTimes += 1
|
||||
const { id } = item.value
|
||||
span.textContent = `${id}.${isSleected(id) ? 't' : 'f'}`
|
||||
})
|
||||
return span
|
||||
},
|
||||
item => item.id,
|
||||
)
|
||||
}).render()
|
||||
|
||||
expect(host.innerHTML).toBe(
|
||||
'<li>1.t</li><li>2.f</li><li>3.f</li><!--for-->',
|
||||
)
|
||||
expect(calledTimes).toBe((expectedCalledTimes += 3))
|
||||
|
||||
index.value = 1
|
||||
await nextTick()
|
||||
expect(host.innerHTML).toBe(
|
||||
'<li>1.f</li><li>2.t</li><li>3.f</li><!--for-->',
|
||||
)
|
||||
expect(calledTimes).toBe((expectedCalledTimes += 2))
|
||||
|
||||
index.value = 2
|
||||
await nextTick()
|
||||
expect(host.innerHTML).toBe(
|
||||
'<li>1.f</li><li>2.f</li><li>3.t</li><!--for-->',
|
||||
)
|
||||
expect(calledTimes).toBe((expectedCalledTimes += 2))
|
||||
|
||||
list.value[2].id = 4
|
||||
await nextTick()
|
||||
expect(host.innerHTML).toBe(
|
||||
'<li>1.f</li><li>2.f</li><li>4.f</li><!--for-->',
|
||||
)
|
||||
expect(calledTimes).toBe((expectedCalledTimes += 1))
|
||||
// list.value[2].id = 3
|
||||
// await nextTick()
|
||||
// expect(host.innerHTML).toBe(
|
||||
// '<li>0.f</li><li>1.f</li><li>3.f</li><!--for-->',
|
||||
// )
|
||||
// expect(calledTimes).toBe((expectedCalledTimes += 1))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -341,7 +341,7 @@ describe('api: createVaporApp', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('config.globalProperty', () => {
|
||||
test.todo('config.globalProperty', () => {
|
||||
const { app } = define({
|
||||
setup() {
|
||||
return []
|
||||
|
@ -351,7 +351,7 @@ describe('api: createVaporApp', () => {
|
|||
app.config.globalProperties.msg = 'hello world'
|
||||
} catch (e) {}
|
||||
expect(
|
||||
`app.config.globalProperties is not supported in vapor mode`,
|
||||
`app.config.globalProperties is not supported in vapor mode components`,
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,23 @@
|
|||
import { createVNode, defineComponent, h, renderSlot } from '@vue/runtime-dom'
|
||||
import {
|
||||
createVNode,
|
||||
defineComponent,
|
||||
h,
|
||||
nextTick,
|
||||
ref,
|
||||
renderSlot,
|
||||
toDisplayString,
|
||||
useModel,
|
||||
} from '@vue/runtime-dom'
|
||||
import { makeInteropRender } from './_utils'
|
||||
import { createComponent, defineVaporComponent } from '../src'
|
||||
import {
|
||||
applyTextModel,
|
||||
child,
|
||||
createComponent,
|
||||
defineVaporComponent,
|
||||
renderEffect,
|
||||
setText,
|
||||
template,
|
||||
} from '../src'
|
||||
|
||||
const define = makeInteropRender()
|
||||
|
||||
|
@ -26,6 +43,54 @@ describe('vdomInterop', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('v-model', () => {
|
||||
test('basic work', async () => {
|
||||
const VaporChild = defineVaporComponent({
|
||||
props: {
|
||||
modelValue: {},
|
||||
modelModifiers: {},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(__props) {
|
||||
const modelValue = useModel(__props, 'modelValue')
|
||||
|
||||
const n0 = template('<h1> </h1>')() as any
|
||||
const n1 = template('<input>')() as any
|
||||
const x0 = child(n0) as any
|
||||
applyTextModel(
|
||||
n1,
|
||||
() => modelValue.value,
|
||||
_value => (modelValue.value = _value),
|
||||
)
|
||||
renderEffect(() => setText(x0, toDisplayString(modelValue.value)))
|
||||
return [n0, n1]
|
||||
},
|
||||
})
|
||||
|
||||
const { html, host } = define({
|
||||
setup() {
|
||||
const msg = ref('foo')
|
||||
return () =>
|
||||
h(VaporChild as any, {
|
||||
modelValue: msg.value,
|
||||
'onUpdate:modelValue': (value: string) => {
|
||||
msg.value = value
|
||||
},
|
||||
})
|
||||
},
|
||||
}).render()
|
||||
|
||||
expect(html()).toBe('<h1>foo</h1><input>')
|
||||
|
||||
const inputEl = host.querySelector('input')!
|
||||
inputEl.value = 'bar'
|
||||
inputEl.dispatchEvent(new Event('input'))
|
||||
|
||||
await nextTick()
|
||||
expect(html()).toBe('<h1>bar</h1><input>')
|
||||
})
|
||||
})
|
||||
|
||||
describe('emit', () => {
|
||||
test('emit from vapor child to vdom parent', () => {
|
||||
const VaporChild = defineVaporComponent({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/runtime-vapor",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/runtime-vapor",
|
||||
"main": "index.js",
|
||||
"module": "dist/runtime-vapor.esm-bundler.js",
|
||||
|
|
|
@ -88,18 +88,6 @@ function prepareApp() {
|
|||
}
|
||||
|
||||
function postPrepareApp(app: App) {
|
||||
if (__DEV__) {
|
||||
app.config.globalProperties = new Proxy(
|
||||
{},
|
||||
{
|
||||
set() {
|
||||
warn(`app.config.globalProperties is not supported in vapor mode.`)
|
||||
return false
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
app.vapor = true
|
||||
const mount = app.mount
|
||||
app.mount = (container, ...args: any[]) => {
|
||||
|
|
|
@ -74,6 +74,9 @@ export const createFor = (
|
|||
) => Block,
|
||||
getKey?: (item: any, key: any, index?: number) => any,
|
||||
flags = 0,
|
||||
setup?: (_: {
|
||||
createSelector: (source: () => any) => (cb: () => void) => void
|
||||
}) => void,
|
||||
): VaporFragment => {
|
||||
const _insertionParent = insertionParent
|
||||
const _insertionAnchor = insertionAnchor
|
||||
|
@ -402,6 +405,10 @@ export const createFor = (
|
|||
}
|
||||
}
|
||||
|
||||
if (setup) {
|
||||
setup({ createSelector })
|
||||
}
|
||||
|
||||
if (flags & VaporVForFlags.ONCE) {
|
||||
renderList()
|
||||
} else {
|
||||
|
@ -412,12 +419,9 @@ export const createFor = (
|
|||
insert(frag, _insertionParent, _insertionAnchor)
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
frag.useSelector = useSelector
|
||||
|
||||
return frag
|
||||
|
||||
function useSelector(source: () => any): (key: any, cb: () => void) => void {
|
||||
function createSelector(source: () => any): (cb: () => void) => void {
|
||||
let operMap = new Map<any, (() => void)[]>()
|
||||
let activeKey = source()
|
||||
let activeOpers: (() => void)[] | undefined
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/server-renderer",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "@vue/server-renderer",
|
||||
"main": "index.js",
|
||||
"module": "dist/server-renderer.esm-bundler.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/shared",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "internal utils shared across @vue packages",
|
||||
"main": "index.js",
|
||||
"module": "dist/shared.esm-bundler.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@vue/compat",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "Vue 3 compatibility build for Vue 2",
|
||||
"main": "index.js",
|
||||
"module": "dist/vue.runtime.esm-bundler.js",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "vue",
|
||||
"version": "3.6.0-alpha.1",
|
||||
"version": "3.6.0-alpha.2",
|
||||
"description": "The progressive JavaScript framework for building modern web UI.",
|
||||
"main": "index.js",
|
||||
"module": "dist/vue.runtime.esm-bundler.js",
|
||||
|
|
Loading…
Reference in New Issue