diff --git a/packages/global.d.ts b/packages/global.d.ts index f53d7b71acd..65f36d34775 100644 --- a/packages/global.d.ts +++ b/packages/global.d.ts @@ -17,6 +17,7 @@ declare var __COMPAT__: boolean declare var __FEATURE_OPTIONS_API__: boolean declare var __FEATURE_PROD_DEVTOOLS__: boolean declare var __FEATURE_SUSPENSE__: boolean +declare var __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__: boolean // for tests declare namespace jest { diff --git a/packages/runtime-core/src/featureFlags.ts b/packages/runtime-core/src/featureFlags.ts index e878407bcc8..267e69eef5c 100644 --- a/packages/runtime-core/src/featureFlags.ts +++ b/packages/runtime-core/src/featureFlags.ts @@ -20,6 +20,11 @@ export function initFeatureFlags() { getGlobalThis().__VUE_PROD_DEVTOOLS__ = false } + if (typeof __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__ !== 'boolean') { + __DEV__ && needWarn.push(`__VUE_PROD_HYDRATION_MISMATCH_DETAILS__`) + getGlobalThis().__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = false + } + if (__DEV__ && needWarn.length) { const multi = needWarn.length > 1 console.warn( diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 35ab851953a..245d586a2c3 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -81,7 +81,7 @@ export function createHydrationFunctions( const hydrate: RootHydrateFunction = (vnode, container) => { if (!container.hasChildNodes()) { - __DEV__ && + ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && warn( `Attempting to hydrate existing markup but container is empty. ` + `Performing full mount instead.` @@ -159,7 +159,7 @@ export function createHydrationFunctions( } else { if ((node as Text).data !== vnode.children) { hasMismatch = true - __DEV__ && + ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && warn( `Hydration text mismatch in`, node.parentNode, @@ -326,7 +326,7 @@ export function createHydrationFunctions( rendererInternals, hydrateNode ) - } else if (__DEV__) { + } else if (__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) { warn('Invalid HostVNode type:', type, `(${typeof type})`) } } @@ -398,7 +398,10 @@ export function createHydrationFunctions( let hasWarned = false while (next) { hasMismatch = true - if (__DEV__ && !hasWarned) { + if ( + (__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && + !hasWarned + ) { warn( `Hydration children mismatch on`, el, @@ -414,7 +417,7 @@ export function createHydrationFunctions( } else if (shapeFlag & ShapeFlags.TEXT_CHILDREN) { if (el.textContent !== vnode.children) { hasMismatch = true - __DEV__ && + ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && warn( `Hydration text content mismatch on`, el, @@ -525,7 +528,10 @@ export function createHydrationFunctions( continue } else { hasMismatch = true - if (__DEV__ && !hasWarned) { + if ( + (__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && + !hasWarned + ) { warn( `Hydration children mismatch on`, container, @@ -595,7 +601,7 @@ export function createHydrationFunctions( isFragment: boolean ): Node | null => { hasMismatch = true - __DEV__ && + ;(__DEV__ || __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__) && warn( `Hydration node mismatch:\n- Client vnode:`, vnode.type, diff --git a/packages/vue/README.md b/packages/vue/README.md index a98bd997487..c41162cc1c6 100644 --- a/packages/vue/README.md +++ b/packages/vue/README.md @@ -37,6 +37,7 @@ Starting with 3.0.0-rc.3, `esm-bundler` builds now exposes global feature flags - `__VUE_OPTIONS_API__` (enable/disable Options API support, default: `true`) - `__VUE_PROD_DEVTOOLS__` (enable/disable devtools support in production, default: `false`) +- `__VUE_PROD_HYDRATION_MISMATCH_DETAILS__` (enable/disable detailed warnings for hydration mismatches in production, default: `false`) The build will work without configuring these flags, however it is **strongly recommended** to properly configure them in order to get proper tree-shaking in the final bundle. To configure these flags: diff --git a/rollup.config.js b/rollup.config.js index b7d38e4527a..3703faae280 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -180,6 +180,9 @@ function createConfig(format, output, plugins = []) { : `true`, __FEATURE_PROD_DEVTOOLS__: isBundlerESMBuild ? `__VUE_PROD_DEVTOOLS__` + : `false`, + __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__: isBundlerESMBuild + ? `__VUE_PROD_HYDRATION_MISMATCH_DETAILS__` : `false` } diff --git a/scripts/dev.js b/scripts/dev.js index 4820723778d..5f996a29831 100644 --- a/scripts/dev.js +++ b/scripts/dev.js @@ -124,7 +124,8 @@ esbuild __COMPAT__: String(target === 'vue-compat'), __FEATURE_SUSPENSE__: `true`, __FEATURE_OPTIONS_API__: `true`, - __FEATURE_PROD_DEVTOOLS__: `false` + __FEATURE_PROD_DEVTOOLS__: `false`, + __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__: `false` } }) .then(ctx => ctx.watch()) diff --git a/scripts/usage-size.ts b/scripts/usage-size.ts index 1a1013b7847..31ee85daebb 100644 --- a/scripts/usage-size.ts +++ b/scripts/usage-size.ts @@ -71,6 +71,7 @@ async function generateBundle(preset: Preset) { replace({ 'process.env.NODE_ENV': '"production"', __VUE_PROD_DEVTOOLS__: 'false', + __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false', __VUE_OPTIONS_API__: 'true', preventAssignment: true }) diff --git a/vitest.config.ts b/vitest.config.ts index e5d5f59345f..ced861a0373 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -15,6 +15,7 @@ export default defineConfig({ __FEATURE_OPTIONS_API__: true, __FEATURE_SUSPENSE__: true, __FEATURE_PROD_DEVTOOLS__: false, + __FEATURE_PROD_HYDRATION_MISMATCH_DETAILS__: false, __COMPAT__: true }, resolve: {