Subscribe on changes!

关于隐式的动态节点被当做静态标记提升

avatar
Jul 14th 2022

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>

img

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值解决打包后的缓存问题。

avatar
Jul 19th 2022

建议还是用英文吧。。。这里的维护者基本上看不懂中文的。

I recommend using English.

avatar
Aug 1st 2022

感觉挺有意思的,翻了翻源码,问题应该出在 这里

看样子是有意为之要重用之前的 el,具体原因不知。

开发环境和线上环境表现不同是因为开发环境克隆 dom 不能 hmr

不过自己操作 dom 的行为严格来说就不应该归 vue 管了,在组件卸载时可以自己手动卸载

onBeforeUnmount(() => {
  (document.querySelector('#map') as HTMLInputElement).innerHTML = ''
});
avatar
Aug 2nd 2022

image 可以在配置里手动指定是否需要静态提升优化

avatar
Sep 8th 2022

自己去修改创建的dom,vue不会去感知,应该遵循数据驱动的开发模式来创建dom

avatar
Sep 17th 2022

This is expected: you are responsible for cleaning up manually added DOM