关于隐式的动态节点被当做静态标记提升
Vue version
v3.2.37
Link to minimal reproduction
https://stackblitz.com/edit/vitejs-vite-nasrdq?file=vite.config.ts
Steps to reproduce
场景叙述
在app组件中定义一个<router-view>
,定义一个Login页面级组件,路由为/
,定义一个Regist页面级组件,路由为/reg
,在Regist组件中定义一个<div id="map"></div>
DOM元素,在onMounted生命周期中向该元素动态创建一个宽高为100px的div。
操作
测试开发环境下:设置两个按钮使两个路由界面来回切换。
重点观察测试生产环境下
:设置两个按钮使两个路由界面来回切换。
regist.vue
<script setup lang="ts">
import { onMounted } from 'vue';
onMounted(() => {
const div = document.createElement('div');
div.style.cssText = `
width: 100px;
height: 100px;
background: red;
`;
(document.querySelector('#map') as HTMLInputElement).appendChild(div);
});
</script>
<template>
<div class="regist">
register
<div id="map"></div>
</div>
</template>
<style scoped>
.regist {
width: 400px;
height: 400px;
background: #999;
}
</style>
login.vue
<script setup lang="ts"></script>
<template>
<div class="login">Login</div>
</template>
<style scoped>
.login {
width: 400px;
height: 400px;
background: #666;
}
</style>
router.ts
import { createRouter, createWebHashHistory } from 'vue-router';
const routes = [
{
path: '/',
name: 'AppLogin',
component: () => import('../components/AppLogin.vue'),
},
{
path: '/reg',
name: 'AppRegist',
component: () => import('../components/AppRegist.vue'),
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
app.vue
<script setup lang="ts">
import { useRouter } from 'vue-router';
const router = useRouter();
</script>
<template>
<h1>App</h1>
<div>
<button @click="router.push('/')">Login</button>
<button @click="router.push('/reg')">Reg</button>
</div>
<hr />
<router-view></router-view>
</template>
<style scoped></style>
What is expected?
最后打包出来的生产环境代码跟开发环境运行结果保持一致。
What is actually happening?
- 在开发模式下没有任何问题,每次切换到
/reg
路由时,始终只有一个宽高100px的div。 - 经过打包过后,运行
dist
目录下的生产环境代码,来回切换路由时发现Regist页面中的div会一直叠加。
System Info
System:
OS: Windows 10 10.0.19044
CPU: (8) x64 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
Memory: 2.04 GB / 7.90 GB
Binaries:
Node: 16.13.1 - E:\Nodejs\node.EXE
Yarn: 1.22.17 - ~\AppData\Roaming\npm\yarn.CMD
npm: 8.12.1 - E:\Nodejs\npm.CMD
Browsers:
Edge: Spartan (44.19041.1266.0), Chromium (103.0.1264.49)
Internet Explorer: 11.0.19041.1566
npmPackages:
vue: ^3.2.37 => 3.2.37
Any additional comments?
疑问点
怀疑是静态标记出的问题,不清楚静态标记提升这一优化是只会在打包阶段发生还是?
目前只能通过给该隐式的动态节点显式地绑定一个变量key
值解决打包后的缓存问题。
感觉挺有意思的,翻了翻源码,问题应该出在 这里
看样子是有意为之要重用之前的 el
,具体原因不知。
开发环境和线上环境表现不同是因为开发环境克隆 dom
不能 hmr
。
不过自己操作 dom
的行为严格来说就不应该归 vue
管了,在组件卸载时可以自己手动卸载
onBeforeUnmount(() => {
(document.querySelector('#map') as HTMLInputElement).innerHTML = ''
});