前端冷知识:为什么你在 DOM 里看不到你输入的值?—— 揭秘 Input 脏值检查与 XSS 防御

6 阅读3分钟

1. 诡异的“消失”现象

让我们还原一个真实的调试场景:

  1. 你写了一个 <el-input v-model="msg" />
  2. 你在页面输入框里输入了攻击脚本:<script>alert(1)</script>
  3. 你右键点击输入框,选择“检查 (Inspect)”。

你预期的画面:

<input class="el-input__inner" value="&lt;script&gt;alert(1)&lt;/script&gt;">

你实际看到的画面:

<input class="el-input__inner">

这时候你可能会慌: “完了,我的数据去哪了?Vue 没绑上吗?还是浏览器出 Bug 了?”

真相是:你的数据很安全,只是浏览器“懒”得把它写在脸上。

2. 核心机制:Attribute(特性)vs Property(属性)

这是前端面试中最经典,也最容易被忽略的考点。

2.1 Attribute:写在 HTML 上的“身份证”

我们在 HTML 标签里写的 value="hello",是 Attribute

  • 作用:它代表元素的初始默认值 (Default Value)。
  • 特性:除非你显式调用 setAttribute,否则它通常不会随着用户的交互而改变。

2.2 Property:活在 DOM 对象里的“当前状态”

当浏览器加载页面后,会为 <input> 标签创建一个 JS 对象(DOM Node)。这个对象里有一个 value 属性,是 Property

  • 作用:它代表元素的当前实时值 (Current Value)。
  • 特性:用户每敲一个字,内存里的这个 value 就在变。

2.3 为什么 Elements 面板里看不到?

Chrome 的 Elements 面板虽然展示的是“实时 DOM 树”,但对于 <input> 这种高频交互元素,浏览器有一个“单向同步”策略:

  • 从 HTML 到 DOM:页面初始化时,HTML 里的 value 会同步给 DOM。
  • 从 DOM 到 HTMLNO! 用户输入改变了 DOM 里的 value,浏览器不会反向把它写回 HTML 标签的 attribute 上。

理由很简单:如果每输入一个字母都要去更新 HTML 字符串渲染,性能开销太大了,而且没有必要(因为页面显示已经由渲染引擎处理了)。

3. 那 XSS 到底是怎么防住的?

既然 HTML 标签上没显示,那浏览器到底是怎么渲染这段“攻击代码”的?

答案在于 API 的选择

Vue 的 v-model 本质是监听 input 事件并更新变量。当它把变量里的 <script> 渲染回屏幕时,它操作的是 DOM Property

// Vue 底层逻辑(简化版)
inputElement.value = "<script>alert(1)</script>";

关键点来了: 当给 DOM 对象的 value 属性赋值时,浏览器会将其视为纯文本数据

  • 浏览器不会启动 HTML 解析器。
  • 浏览器不会尝试寻找 <> 配对。
  • 浏览器只是单纯地把这串字符“画”在输入框里。

所以,无论你输入多么复杂的 XSS payload,在 input 框里,它永远只是一串没有任何杀伤力的字符

4. 如何逼出“真面目”?(查看转义)

虽然 input 标签上不显示,但数据确实在内存里。如果你想亲眼看到浏览器是如何处理这些特殊字符的序列化(转义),你需要用一点小手段:

打开控制台(Console),输入:

// $0 是你当前选中的 input 元素
console.log($0.value);      // 输出 "<script>..."  <-- 内存里的真值(未转义)
console.log($0.outerHTML);  // 输出 "...value="&lt;script&gt;..." <-- 强制序列化后的 HTML

只有当你强制浏览器把当前状态“打印”成 HTML 字符串(outerHTML)时,浏览器为了保证生成的 HTML 语法正确,才会把 < 变成 &lt; 给你看。

5. 总结

  1. Input 的“懒更新” :Elements 面板里看不到 value 变化是正常的,这是浏览器的性能优化策略。
  2. Vue 的数据流:Vue 绑定的是 DOM Property(内存对象),而不是 HTML Attribute。
  3. 安全的本质:XSS 之所以失效,是因为 DOM Property (element.value) 天然把内容当作纯文本处理,直接绕过了 HTML 解析阶段。

下次再看到空的 input 标签别慌,你的数据正安全地躺在内存里呢。