Vue Router组件mounted仅首次执行 返回页面D3.js脚本不加载
问题根因
- 动态插入脚本的逻辑存在浏览器缓存机制问题:首次进入页面时插入的D3.js脚本会被浏览器解析执行,同时标签会永久留在
document.head中,后续再次进入组件时,浏览器识别到已存在同src的script标签,不会重复执行脚本解析逻辑,自然不会触发D3相关初始化。 - Vue Router默认配合
<keep-alive>使用时会缓存组件实例,切走页面时组件不会被销毁,回到页面时直接读取缓存实例,mounted钩子仅在组件首次创建时执行一次,后续返回页面不会触发。 - 用固定id
d3-container获取DOM不符合Vue组件化设计,路由切换过程中DOM节点未被正确清理时,D3选择器容易命中已销毁或无效的节点,直接导致渲染失败。
修复方案
方案1:全局引入D3(优先推荐,适合项目多场景使用D3的情况)
直接在项目入口文件提前加载D3,彻底去掉组件内动态插入脚本的逻辑,避免动态插标签带来的时序、缓存问题。
入口文件添加代码:
// 项目入口文件(如main.js) import * as d3 from 'd3' window.d3 = d3
组件侧调整为生命周期触发渲染,用ref获取内部DOM节点,适配keep-alive缓存场景:
const pagina2 = { template: ` <div class="col-12 col-md-8"> <article class="bg-dark text-white border border-dark rounded mt-4 mb-4"> <h2 class="px-5 text-danger">Approfondimento Pagina 2</h2> <div ref="d3Container"></div> <p class="px-5">Approfondimento</p> <p class="px-5">"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."</p> </article> <!----------Page Navigation----------> <nav aria-label="Page navigation"> <ul class="pagination justify-content-center"> <li class="page-item "><router-link class="page-link text-danger bg-dark border-dark" to="/">1</router-link></li> <li class="page-item"><router-link class="page-link text-danger bg-dark border-dark" to="/pagina2">2</router-link></li> </ul> </nav> </div> `, mounted(){ this.renderD3() }, // 适配keep-alive缓存,组件每次激活时触发渲染 activated(){ this.renderD3() }, methods: { renderD3(){ const container = this.$refs.d3Container // 清空容器历史内容,避免重复渲染叠加 container.innerHTML = '' // 此处编写D3渲染逻辑,直接调用window.d3即可 } } }
方案2:单页动态加载D3的修复写法(适合仅当前页面用D3、不想全局引入的场景)
如果必须动态加载脚本,需要补全脚本加载监听、组件销毁时清理脚本标签、DOM校验逻辑,避免浏览器缓存导致的脚本不执行问题:
const pagina2 = { // template部分和方案1一致,将id选择器替换为ref="d3Container" template: `/* 同方案1模板 */`, mounted(){ this.loadD3AndRender() }, activated(){ this.loadD3AndRender() }, beforeUnmount(){ // 组件销毁时移除已插入的D3脚本,避免下次加载时浏览器跳过解析 const existScript = document.querySelector('script[src="./JS/D3.js"]') if(existScript) existScript.remove() // 清空D3绑定的全局事件,避免内存泄漏 }, methods: { loadD3AndRender(){ // D3已加载时直接渲染 if(window.d3){ this.renderD3() return } const D3script = document.createElement('script') D3script.src = './JS/D3.js' // 等脚本加载完成后再执行渲染,解决时序问题 D3script.onload = () => { this.renderD3() } document.head.appendChild(D3script) }, renderD3(){ const container = this.$refs.d3Container container.innerHTML = '' // 此处编写D3渲染逻辑 } } }
补充配置:如果未开启keep-alive但mounted仍只触发一次,可以给路由出口添加fullPath作为key,强制路由切换时重建组件:
<router-view :key="$route.fullPath" />禁止在组件内用全局id选择器获取DOM,组件复用时同id会多次出现在页面中,会直接导致选择器命中错误节点,用
ref获取组件内部DOM是Vue的标准写法。
内容的提问来源于stack exchange,提问作者Shodan4004
相关产品推荐
相关产品推荐

