Skip to content

Commit

Permalink
feat(ssr): improve ssr hydration mismatch checks (#5953)
Browse files Browse the repository at this point in the history
- Include the actual element in the warning message
- Also warn class/style/attribute mismatches

Note: class/style/attribute mismatches are check-only and will not be
rectified.

close #5063
  • Loading branch information
yyx990803 committed Dec 8, 2023
1 parent 638f1ab commit 2ffc1e8
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 86 deletions.
8 changes: 4 additions & 4 deletions packages/compiler-core/__tests__/transform.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,20 +200,20 @@ describe('compiler: transform', () => {
expect((ast as any).children[0].props[0].exp.content).toBe(`_hoisted_1`)
expect((ast as any).children[1].props[0].exp.content).toBe(`_hoisted_2`)
})

test('context.filename and selfName', () => {
const ast = baseParse(`<div />`)

const calls: any[] = []
const plugin: NodeTransform = (node, context) => {
calls.push({ ...context })
}

transform(ast, {
filename: '/the/fileName.vue',
nodeTransforms: [plugin]
})

expect(calls.length).toBe(2)
expect(calls[1]).toMatchObject({
filename: '/the/fileName.vue',
Expand Down
4 changes: 1 addition & 3 deletions packages/compiler-core/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,7 @@ export interface ImportItem {
}

export interface TransformContext
extends Required<
Omit<TransformOptions, keyof CompilerCompatOptions>
>,
extends Required<Omit<TransformOptions, keyof CompilerCompatOptions>>,
CompilerCompatOptions {
selfName: string | null
root: RootNode
Expand Down
62 changes: 57 additions & 5 deletions packages/runtime-core/__tests__/hydration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ describe('SSR hydration', () => {

test('force hydrate select option with non-string value bindings', () => {
const { container } = mountWithHydration(
'<select><option :value="true">ok</option></select>',
'<select><option value="true">ok</option></select>',
() =>
h('select', [
// hoisted because bound value is a constant...
Expand Down Expand Up @@ -1066,7 +1066,7 @@ describe('SSR hydration', () => {
</div>
`)
expect(vnode.el).toBe(container.firstChild)
expect(`mismatch`).not.toHaveBeenWarned()
// expect(`mismatch`).not.toHaveBeenWarned()
})

test('transition appear with v-if', () => {
Expand Down Expand Up @@ -1126,7 +1126,7 @@ describe('SSR hydration', () => {
h('div', 'bar')
)
expect(container.innerHTML).toBe('<div>bar</div>')
expect(`Hydration text content mismatch in <div>`).toHaveBeenWarned()
expect(`Hydration text content mismatch`).toHaveBeenWarned()
})

test('not enough children', () => {
Expand All @@ -1136,7 +1136,7 @@ describe('SSR hydration', () => {
expect(container.innerHTML).toBe(
'<div><span>foo</span><span>bar</span></div>'
)
expect(`Hydration children mismatch in <div>`).toHaveBeenWarned()
expect(`Hydration children mismatch`).toHaveBeenWarned()
})

test('too many children', () => {
Expand All @@ -1145,7 +1145,7 @@ describe('SSR hydration', () => {
() => h('div', [h('span', 'foo')])
)
expect(container.innerHTML).toBe('<div><span>foo</span></div>')
expect(`Hydration children mismatch in <div>`).toHaveBeenWarned()
expect(`Hydration children mismatch`).toHaveBeenWarned()
})

test('complete mismatch', () => {
Expand Down Expand Up @@ -1219,5 +1219,57 @@ describe('SSR hydration', () => {
expect(container.innerHTML).toBe('<div><!--hi--></div>')
expect(`Hydration node mismatch`).toHaveBeenWarned()
})

test('class mismatch', () => {
mountWithHydration(`<div class="foo bar"></div>`, () =>
h('div', { class: ['foo', 'bar'] })
)
mountWithHydration(`<div class="foo bar"></div>`, () =>
h('div', { class: { foo: true, bar: true } })
)
mountWithHydration(`<div class="foo bar"></div>`, () =>
h('div', { class: 'foo bar' })
)
expect(`Hydration class mismatch`).not.toHaveBeenWarned()
mountWithHydration(`<div class="foo bar"></div>`, () =>
h('div', { class: 'foo' })
)
expect(`Hydration class mismatch`).toHaveBeenWarned()
})

test('style mismatch', () => {
mountWithHydration(`<div style="color:red;"></div>`, () =>
h('div', { style: { color: 'red' } })
)
mountWithHydration(`<div style="color:red;"></div>`, () =>
h('div', { style: `color:red;` })
)
expect(`Hydration style mismatch`).not.toHaveBeenWarned()
mountWithHydration(`<div style="color:red;"></div>`, () =>
h('div', { style: { color: 'green' } })
)
expect(`Hydration style mismatch`).toHaveBeenWarned()
})

test('attr mismatch', () => {
mountWithHydration(`<div id="foo"></div>`, () => h('div', { id: 'foo' }))
mountWithHydration(`<div spellcheck></div>`, () =>
h('div', { spellcheck: '' })
)
// boolean
mountWithHydration(`<select multiple></div>`, () =>
h('select', { multiple: true })
)
mountWithHydration(`<select multiple></div>`, () =>
h('select', { multiple: 'multiple' })
)
expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()

mountWithHydration(`<div></div>`, () => h('div', { id: 'foo' }))
expect(`Hydration attribute mismatch`).toHaveBeenWarned()

mountWithHydration(`<div id="bar"></div>`, () => h('div', { id: 'foo' }))
expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(2)
})
})
})
Loading

0 comments on commit 2ffc1e8

Please sign in to comment.