Nuxt.js中如何执行DOM完全加载后运行的函数?
问题分析
核心问题在于SPA页面间导航时,表格列宽调整函数无法触发,仅刷新页面(SSR场景)时正常。原因是SSR模式下页面DOM在JavaScript执行前已完全构建,而SPA导航是客户端动态渲染DOM,onMounted+nextTick的时机可能赶不上表格DOM的实际渲染完成时间(比如表格依赖异步数据加载)。
解决方案
以下是针对性修复方案,按优先级排序:
方案1:监听表格数据变化(适用于异步加载表格数据的场景)
如果表格基于异步数据渲染,onMounted时数据可能未返回、表格DOM尚未生成,此时需监听数据加载完成后再执行列宽调整:
<script setup> import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'; // 假设表格数据为异步获取的变量 const tableData = ref([]); const adjustTableColumnWidths = () => { const tables = document.querySelectorAll('.prose table'); if (!tables.length) return; // 无表格时直接返回 tables.forEach((table) => { const totalWidth = table.offsetWidth; const firstColWidth = totalWidth / 3; const colCount = table.rows[0]?.cells.length || 1; const otherColWidth = colCount > 1 ? (totalWidth - firstColWidth) / (colCount - 1) : totalWidth; Array.from(table.rows).forEach(row => { const firstCell = row.cells[0]; if (firstCell) { firstCell.style.width = `${firstColWidth}px`; } for (let i = 1; i < row.cells.length; i++) { row.cells[i].style.width = `${otherColWidth}px`; } }); }); }; // 监听表格数据变化,数据更新后执行调整 watch(tableData, async () => { await nextTick(); adjustTableColumnWidths(); }, { deep: true }); onMounted(async () => { await nextTick(); adjustTableColumnWidths(); window.addEventListener('resize', adjustTableColumnWidths); }); onUnmounted(() => { window.removeEventListener('resize', adjustTableColumnWidths); }); </script>
方案2:利用路由切换事件(适用于页面级导航场景)
如果是Vue Router页面间导航,可在路由全局后置钩子中触发列宽调整,确保页面DOM完全渲染:
在router/index.js中添加:
import { nextTick } from 'vue'; router.afterEach(async () => { await nextTick(); const adjustTableColumnWidths = () => { const tables = document.querySelectorAll('.prose table'); if (!tables.length) return; tables.forEach((table) => { const totalWidth = table.offsetWidth; const firstColWidth = totalWidth / 3; const colCount = table.rows[0]?.cells.length || 1; const otherColWidth = colCount > 1 ? (totalWidth - firstColWidth) / (colCount - 1) : totalWidth; Array.from(table.rows).forEach(row => { const firstCell = row.cells[0]; if (firstCell) { firstCell.style.width = `${firstColWidth}px`; } for (let i = 1; i < row.cells.length; i++) { row.cells[i].style.width = `${otherColWidth}px`; } }); }); }; adjustTableColumnWidths(); });
若组件被keep-alive缓存,可在页面组件中使用onActivated钩子:
<script setup> import { onActivated, nextTick } from 'vue'; // ... 其他代码 onActivated(async () => { await nextTick(); adjustTableColumnWidths(); }); </script>
方案3:用MutationObserver监听DOM变化(通用兜底方案)
若不确定表格渲染时机,可监听.prose容器的DOM变化,当表格被添加时自动执行调整:
<script setup> import { onMounted, onUnmounted, nextTick } from 'vue'; const adjustTableColumnWidths = () => { // 你的列宽调整函数逻辑 const tables = document.querySelectorAll('.prose table'); if (!tables.length) return; tables.forEach((table) => { const totalWidth = table.offsetWidth; const firstColWidth = totalWidth / 3; const colCount = table.rows[0]?.cells.length || 1; const otherColWidth = colCount > 1 ? (totalWidth - firstColWidth) / (colCount - 1) : totalWidth; Array.from(table.rows).forEach(row => { const firstCell = row.cells[0]; if (firstCell) { firstCell.style.width = `${firstColWidth}px`; } for (let i = 1; i < row.cells.length; i++) { row.cells[i].style.width = `${otherColWidth}px`; } }); }); }; let observer = null; onMounted(async () => { await nextTick(); adjustTableColumnWidths(); window.addEventListener('resize', adjustTableColumnWidths); const proseContainer = document.querySelector('.prose'); if (proseContainer) { observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.addedNodes.length) { const hasTable = Array.from(mutation.addedNodes).some(node => node.tagName === 'TABLE' || node.querySelector?.('table')); if (hasTable) { adjustTableColumnWidths(); } } }); }); observer.observe(proseContainer, { childList: true, subtree: true }); } }); onUnmounted(() => { window.removeEventListener('resize', adjustTableColumnWidths); if (observer) observer.disconnect(); }); </script>
关键优化点
- 给函数添加空值判断,避免无效执行
- 处理表格行/列数量异常的情况(比如仅一列的表格)
- 根据实际场景选择匹配的触发时机,优先监听数据变化或路由事件,MutationObserver作为兜底
内容的提问来源于stack exchange,提问作者Jeremy
相关产品推荐
相关产品推荐

