Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 3 additions & 1 deletion packages/playground/vue/Main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
<div class="comments"><!--hello--></div>
<h1>Vue SFCs</h1>
<pre>{{ time }}</pre>
<Hmr />
<div class="hmr-block">
<Hmr />
</div>
<Syntax />
<PreProcessors />
<CssModules />
Expand Down
5 changes: 5 additions & 0 deletions packages/playground/vue/__tests__/vue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ describe('hmr', () => {
)
await untilUpdated(() => page.textContent('.hmr-inc'), 'count is 100')
})

test('should re-render when template is emptied', async () => {
editFile('Hmr.vue', () => '')
await untilUpdated(() => page.innerHTML('.hmr-block'), '<!---->')
})
})

describe('src imports', () => {
Expand Down
19 changes: 3 additions & 16 deletions packages/plugin-vue/src/handleHotUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import _debug from 'debug'
import { SFCBlock, SFCDescriptor } from '@vue/compiler-sfc'
import { SFCDescriptor } from '@vue/compiler-sfc'
import {
createDescriptor,
getDescriptor,
setPrevDescriptor
setPrevDescriptor,
isEqualBlock
} from './utils/descriptorCache'
import { getResolvedScript, setResolvedScript } from './script'
import { ModuleNode, HmrContext } from 'vite'
Expand Down Expand Up @@ -144,20 +145,6 @@ export async function handleHotUpdate({
return [...affectedModules].filter(Boolean) as ModuleNode[]
}

function isEqualBlock(a: SFCBlock | null, b: SFCBlock | null) {
if (!a && !b) return true
if (!a || !b) return false
// src imports will trigger their own updates
if (a.src && b.src && a.src === b.src) return true
if (a.content !== b.content) return false
const keysA = Object.keys(a.attrs)
const keysB = Object.keys(b.attrs)
if (keysA.length !== keysB.length) {
return false
}
return keysA.every((key) => a.attrs[key] === b.attrs[key])
}

export function isOnlyTemplateChanged(
prev: SFCDescriptor,
next: SFCDescriptor
Expand Down
21 changes: 17 additions & 4 deletions packages/plugin-vue/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { ResolvedOptions } from '.'
import {
createDescriptor,
getPrevDescriptor,
setDescriptor
setDescriptor,
isEqualBlock
} from './utils/descriptorCache'
import { PluginContext, TransformPluginContext } from 'rollup'
import { resolveScript } from './script'
Expand Down Expand Up @@ -71,11 +72,23 @@ export async function transformMain(
))
}

const renderReplace = hasTemplateImport
? ssr
let renderReplace = ''
if (hasTemplateImport) {
renderReplace = ssr
? `_sfc_main.ssrRender = _sfc_ssrRender`
: `_sfc_main.render = _sfc_render`
: ''
} else {
// #2128
// User may empty the template but we didn't provide rerender function before
if (
prevDescriptor &&
!isEqualBlock(descriptor.template, prevDescriptor.template)
) {
renderReplace = ssr
? `_sfc_main.ssrRender = () => {}`
: `_sfc_main.render = () => {}`
}
}

// styles
const stylesCode = await genStyleCode(descriptor, pluginContext)
Expand Down
16 changes: 15 additions & 1 deletion packages/plugin-vue/src/utils/descriptorCache.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path'
import slash from 'slash'
import hash from 'hash-sum'
import { parse, SFCDescriptor } from '@vue/compiler-sfc'
import { parse, SFCDescriptor, SFCBlock } from '@vue/compiler-sfc'

const cache = new Map<string, SFCDescriptor>()
const prevCache = new Map<string, SFCDescriptor | undefined>()
Expand Down Expand Up @@ -49,3 +49,17 @@ export function getDescriptor(filename: string, errorOnMissing = true) {
export function setDescriptor(filename: string, entry: SFCDescriptor) {
cache.set(filename, entry)
}

export function isEqualBlock(a: SFCBlock | null, b: SFCBlock | null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to move this function. Just export it from handleHotUpdate is fine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, done.

if (!a && !b) return true
if (!a || !b) return false
// src imports will trigger their own updates
if (a.src && b.src && a.src === b.src) return true
if (a.content !== b.content) return false
const keysA = Object.keys(a.attrs)
const keysB = Object.keys(b.attrs)
if (keysA.length !== keysB.length) {
return false
}
return keysA.every((key) => a.attrs[key] === b.attrs[key])
}