From 8ebb5bdf09dfd6c5dd2a0b99f6b4e0ead92960a4 Mon Sep 17 00:00:00 2001 From: CHOYSEN Date: Sun, 21 Feb 2021 12:57:04 +0800 Subject: [PATCH 1/3] fix(plugin-vue): return rerender function when user empty the template --- packages/plugin-vue/src/handleHotUpdate.ts | 19 +++-------------- packages/plugin-vue/src/main.ts | 21 +++++++++++++++---- .../plugin-vue/src/utils/descriptorCache.ts | 16 +++++++++++++- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/packages/plugin-vue/src/handleHotUpdate.ts b/packages/plugin-vue/src/handleHotUpdate.ts index 734c6d4a8d604d..22892e01457108 100644 --- a/packages/plugin-vue/src/handleHotUpdate.ts +++ b/packages/plugin-vue/src/handleHotUpdate.ts @@ -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' @@ -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 diff --git a/packages/plugin-vue/src/main.ts b/packages/plugin-vue/src/main.ts index d1996b087cde06..f2517f189ab4a0 100644 --- a/packages/plugin-vue/src/main.ts +++ b/packages/plugin-vue/src/main.ts @@ -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' @@ -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) diff --git a/packages/plugin-vue/src/utils/descriptorCache.ts b/packages/plugin-vue/src/utils/descriptorCache.ts index dc1cc7fc69d3f7..813d8d10200f94 100644 --- a/packages/plugin-vue/src/utils/descriptorCache.ts +++ b/packages/plugin-vue/src/utils/descriptorCache.ts @@ -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() const prevCache = new Map() @@ -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) { + 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]) +} From 7173d90a51426be2dacf4eb4d254a86587c6abfd Mon Sep 17 00:00:00 2001 From: CHOYSEN Date: Sun, 21 Feb 2021 14:50:11 +0800 Subject: [PATCH 2/3] test(vue): should re-render when template is emptied --- packages/playground/vue/Main.vue | 4 +++- packages/playground/vue/__tests__/vue.spec.ts | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/playground/vue/Main.vue b/packages/playground/vue/Main.vue index 37324329d32bef..465c289bc56317 100644 --- a/packages/playground/vue/Main.vue +++ b/packages/playground/vue/Main.vue @@ -2,7 +2,9 @@

Vue SFCs

{{ time }}
- +
+ +
diff --git a/packages/playground/vue/__tests__/vue.spec.ts b/packages/playground/vue/__tests__/vue.spec.ts index ffc11531df4add..c331d61fa52e8f 100644 --- a/packages/playground/vue/__tests__/vue.spec.ts +++ b/packages/playground/vue/__tests__/vue.spec.ts @@ -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', () => { From 57a1f05622276ebc67cccc1fca2fd5743c3dd9d1 Mon Sep 17 00:00:00 2001 From: CHOYSEN Date: Tue, 23 Feb 2021 09:49:22 +0800 Subject: [PATCH 3/3] refactor: just export isEqualBlock from handleHotUpdate.ts --- packages/plugin-vue/src/handleHotUpdate.ts | 19 ++++++++++++++++--- packages/plugin-vue/src/main.ts | 5 ++--- .../plugin-vue/src/utils/descriptorCache.ts | 16 +--------------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/packages/plugin-vue/src/handleHotUpdate.ts b/packages/plugin-vue/src/handleHotUpdate.ts index 22892e01457108..3b3ec974d1638c 100644 --- a/packages/plugin-vue/src/handleHotUpdate.ts +++ b/packages/plugin-vue/src/handleHotUpdate.ts @@ -1,10 +1,9 @@ import _debug from 'debug' -import { SFCDescriptor } from '@vue/compiler-sfc' +import { SFCBlock, SFCDescriptor } from '@vue/compiler-sfc' import { createDescriptor, getDescriptor, - setPrevDescriptor, - isEqualBlock + setPrevDescriptor } from './utils/descriptorCache' import { getResolvedScript, setResolvedScript } from './script' import { ModuleNode, HmrContext } from 'vite' @@ -145,6 +144,20 @@ export async function handleHotUpdate({ return [...affectedModules].filter(Boolean) as ModuleNode[] } +export 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 diff --git a/packages/plugin-vue/src/main.ts b/packages/plugin-vue/src/main.ts index f2517f189ab4a0..645a5032046c9f 100644 --- a/packages/plugin-vue/src/main.ts +++ b/packages/plugin-vue/src/main.ts @@ -5,13 +5,12 @@ import { ResolvedOptions } from '.' import { createDescriptor, getPrevDescriptor, - setDescriptor, - isEqualBlock + setDescriptor } from './utils/descriptorCache' import { PluginContext, TransformPluginContext } from 'rollup' import { resolveScript } from './script' import { transformTemplateInMain } from './template' -import { isOnlyTemplateChanged } from './handleHotUpdate' +import { isOnlyTemplateChanged, isEqualBlock } from './handleHotUpdate' import { RawSourceMap, SourceMapConsumer, SourceMapGenerator } from 'source-map' import { createRollupError } from './utils/error' diff --git a/packages/plugin-vue/src/utils/descriptorCache.ts b/packages/plugin-vue/src/utils/descriptorCache.ts index 813d8d10200f94..dc1cc7fc69d3f7 100644 --- a/packages/plugin-vue/src/utils/descriptorCache.ts +++ b/packages/plugin-vue/src/utils/descriptorCache.ts @@ -1,7 +1,7 @@ import path from 'path' import slash from 'slash' import hash from 'hash-sum' -import { parse, SFCDescriptor, SFCBlock } from '@vue/compiler-sfc' +import { parse, SFCDescriptor } from '@vue/compiler-sfc' const cache = new Map() const prevCache = new Map() @@ -49,17 +49,3 @@ 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) { - 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]) -}