<script setup>
import { ref, computed } from 'vue'
import Home from './Home.vue'
import About from './About.vue'
import NotFound from './NotFound.vue'
const routes = {
'/': Home,
'/about': About
}
const currentPath = ref(window.location.hash)
window.addEventListener('hashchange', () => {
currentPath.value = window.location.hash
})
const currentView = computed(() => {
return routes[currentPath.value.slice(1) || '/'] || NotFound
})
</script>
<template>
<a href="#/">Home</a> |
<a href="#/about">About</a> |
<a href="#/non-existent-path">Broken Link</a>
<component :is="currentView" />
</template>为什么还要重新赋值一遍currentPath.value = window.location.hash原本const currentPath = ref(window.location.hash)不就代表ref括号的参数默认是这个变量的value了吗
这个问题非常好,它触及了 Vue 响应式系统与原生浏览器API(如 window.location.hash
)如何交互的核心,也是很多初学者容易困惑的地方。
简单来说,ref()
只在创建时读取一次 window.location.hash
的值,它并不会持续自动跟踪这个值的变化。
我们来详细分解一下这个过程。
1. ref()
的作用:“一次性快照”,而非“持续绑定”
当您执行这行代码时:
const currentPath = ref(window.location.hash)
背后发生的事情是:
-
JavaScript 首先执行括号里的内容:
window.location.hash
。假设页面刚加载时,URL是...#/about
,那么这个表达式会返回一个普通的字符串"#about"
。 -
然后,Vue 的
ref()
函数接收到这个字符串"#about"
,并用它来创建一个响应式对象。 -
这个响应式对象被赋值给
currentPath
变量。此时,currentPath.value
的值就是字符串"#about"
。
最关键的一点是:从这一刻起,currentPath.value
和 window.location.hash
之间就没有任何关系了。ref()
只是在初始化的那一瞬间,拍了一张 window.location.hash
的“快照”作为自己的初始值。
window.location.hash
是一个原生的浏览器属性,它本身并不是一个 Vue 的响应式源。当浏览器因为用户点击链接而改变了它的值时,Vue 的响应式系统是完全不知情的。
2. addEventListener
的作用:“手动连接的桥梁”
既然 Vue 无法自动知道 window.location.hash
变了,我们就需要一个“信使”来告诉它。这个“信使”就是 window.addEventListener
。
// 当浏览器的 hash 值发生变化时,请执行这个函数
window.addEventListener('hashchange', () => {// 这就是信使要传递的消息:// "嘿,Vue!请把 currentPath 的值更新为浏览器最新的 hash 值!"currentPath.value = window.location.hash
})
-
'hashchange'
事件: 这是浏览器提供的标准事件。每当 URL 的 hash 部分(#
后面的内容)发生改变时,浏览器就会触发这个事件。 -
() => { ... }
: 这是事件触发时要执行的回调函数。 -
currentPath.value = window.location.hash
: 这就是我们手动重新赋值的关键步骤。我们在这里读取最新的window.location.hash
值,并把它赋给currentPath.value
。
因为 currentPath
是一个 ref
,所以当它的 .value
被重新赋值时,Vue 的响应式系统就会被激活。Vue 会注意到 currentPath
变了,然后:
-
重新计算
currentView
这个计算属性。 -
currentView
返回了新的组件(比如从Home
变成了About
)。 -
Vue 自动将模板中的
<component :is="currentView" />
更新为新的组件。
一个生动的比喻
-
const currentPath = ref(window.location.hash)
:你抬头看了一眼公共广场上的大钟 (window.location.hash
),然后把当前时间写在了你自己的个人白板 (currentPath
) 上。 -
之后,你的白板上的时间就不会再动了,即使广场上的大钟在不停地走。你的白板和大钟之间没有自动同步的魔法。
-
window.addEventListener('hashchange', ...)
:你雇了一个人(事件监听器),让他一直盯着广场上的大钟。 -
currentPath.value = window.location.hash
:你告诉这个人,每当大钟的时间变了,就立刻跑过来,擦掉你白板上的旧时间,然后写上大钟的最新时间。
正是这个“手动更新白板”的动作,才让你的 Vue 应用(办公室里的其他人)知道现在是几点了。