Vue.js中document.querySelector返回null但元素已存在问题求助
我在跟着Kevin Powell的教程制作响应式导航栏时遇到问题:代码中的const primary_nav = document.querySelector(".primary-navigation")返回null值,但该类名对应的元素确实存在于模板中,这是什么原因导致的?
相关代码
<script setup lang="ts"> import { useRouter } from 'vue-router'; const router = useRouter(); const navigations = [ { path: '/about', name: 'About' }, { path: '/register', name: 'Create account' }, { path: '/login', name: 'Login' }, ]; const primary_nav = document.querySelector(".primary-navigation"); console.log('primary_nav: ', primary_nav); const nav_toggle = () => { const visibility = primary_nav?.getAttribute("data-visible"); console.log('visibility: ', visibility); } </script> <template> <header class="primary-header flex"> <div> <img class="logo" src="src/assets/Missandei-Logo.png" /> </div> <button @click="nav_toggle" class="mobile-nav-toggle display-none" aria-controls="primary-navigation" aria-expanded="false"> <span class="sr-only"> Menu </span> </button> <nav> <ul id="primary-navigation" data-visible="false" class="primary-navigation flex"> <li class="active" v-for="(navigation, index) in navigations" :key="index"> <a> <span aria-hidden="true">{{ index }}</span>{{ navigation.name }} </a> </li> </ul> </nav> </header> </template>
原因分析
核心问题是Vue组件的生命周期时机:<script setup>中的代码会在组件实例创建后、DOM节点挂载到页面之前执行。此时模板里的元素还未被渲染到真实DOM中,document.querySelector自然找不到目标元素,返回null。
解决办法
方法1:使用onMounted生命周期钩子
将获取DOM的逻辑放到onMounted钩子中,此时组件已完成挂载,DOM元素已存在:
<script setup lang="ts"> import { useRouter, onMounted } from 'vue'; const router = useRouter(); const navigations = [ { path: '/about', name: 'About' }, { path: '/register', name: 'Create account' }, { path: '/login', name: 'Login' }, ]; let primary_nav: HTMLElement | null = null; onMounted(() => { primary_nav = document.querySelector(".primary-navigation"); console.log('primary_nav: ', primary_nav); }); const nav_toggle = () => { const visibility = primary_nav?.getAttribute("data-visible"); console.log('visibility: ', visibility); } </script>
方法2:使用Vue模板引用(推荐)
这是Vue官方推荐的操作DOM方式,更契合Vue的响应式机制:
<script setup lang="ts"> import { useRouter, ref } from 'vue'; const router = useRouter(); const navigations = [ { path: '/about', name: 'About' }, { path: '/register', name: 'Create account' }, { path: '/login', name: 'Login' }, ]; const primaryNavRef = ref<HTMLElement | null>(null); const nav_toggle = () => { const visibility = primaryNavRef.value?.getAttribute("data-visible"); console.log('visibility: ', visibility); } </script> <template> <header class="primary-header flex"> <div> <img class="logo" src="src/assets/Missandei-Logo.png" /> </div> <button @click="nav_toggle" class="mobile-nav-toggle display-none" aria-controls="primary-navigation" aria-expanded="false"> <span class="sr-only">Menu</span> </button> <nav> <!-- 给ul添加ref属性关联脚本中的变量 --> <ul ref="primaryNavRef" id="primary-navigation" data-visible="false" class="primary-navigation flex"> <li class="active" v-for="(navigation, index) in navigations" :key="index"> <a> <span aria-hidden="true">{{ index }}</span>{{ navigation.name }} </a> </li> </ul> </nav> </header> </template>
内容的提问来源于stack exchange,提问作者Gabby
相关产品推荐
相关产品推荐

