我最早学前端的时候,一直以为 HTML、CSS、JS 三件套是“画网页”的,直到有个同学往我的输入框里粘了一小段 <script> 标签,页面瞬间弹窗、DOM 乱掉,我才意识到:只要页面里有用户输入的内容,前端就不只是“画”这么简单。这个标题里的 XSS,不是蹭安全热度的点缀,而是新手学完基础后最容易踩到、也最容易忽视的一道坎。
这篇笔记不是从零开始的语法词典,而是把 HTML 搭结构、CSS 做样式、JS 管交互这条主线走通之后,再顺手带大家认识 XSS 到底是怎么利用这三件套“钻空子”的。适合两类人看:一类是刚学完三件套基础语法、想找个完整学习路径把知识点串起来的新手;另一类是已经能写简单页面、但没认真想过“用户输入的数据为什么不能直接插入页面”的初级前端。作为一篇个人实战学习笔记,我会直接把学习过程中验证过值得记的东西、走过的弯路、还有练习 XSS 时该用的本地环境和思路分享出来,希望对你们有帮助。
1. 这套学习笔记到底在解决什么问题
先搞清楚 HTML、CSS、JS 三者的分工,很多学习笔记其实没把这层讲透。HTML 是页面内容的骨架,它负责“这里有一个标题、那里有一个输入框”;CSS 是皮肤,负责颜色、间距、布局;JS 是肌肉和大脑,负责监听用户操作、改变页面状态、和服务器交换数据。这三者不是一个先学完再学另一个的关系,而是从一开始就要在一个页面里混合着用。国内很多新手把 HTML 和 CSS 当成“静态页面”学完,再开始碰 JS,结果遇到 DOM 操作就懵,就是因为没有建立起“HTML 结构可以被 JS 动态改”的意识。
我建议在学习笔记里把三者的关系始终拉在一起:先写 HTML,再用 CSS 调整布局,最后用 JS 控制结构变化和交互行为。XSS 之所以放在这组话题里,是因为它本质上就是在“结构可以被动态改变”这个环节出的问题——当一段用户输入被当作 HTML 结构直接插入 DOM,页面就会失去对“内容”和“代码”的边界控制。
这套学习内容把 XSS 定位成“前端基础的一部分”,而不是“安全专家的领域”,这也是我在实际学习中体会最深的一点。如今前后端分离的架构越来越多,页面里基于 URL 参数、本地存储、接口返回值动态渲染 DOM 的场景极其普遍,前端代码本身就已经是 XSS 攻击发生和防御的第一道关口。如果只看 HTML/CSS/JS 不管 XSS,学到后面几乎必然要回头补课。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. HTML 与 CSS 打基础阶段的重点拆解
2.1 标准文档骨架:每个标签到底在干什么
很多人第一次搜 HTML 相关代码时,会看到一大段 < !doctype html> ... <meta charset ...> 这样的模板,然后直接复制粘贴,根本不管每一行是什么。直接背这套骨架本身没有意义,但理解它非常重要。
html复制<!doctype html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>页面标题</title>
</head>
<body>
<!-- 页面可见内容 -->
</body>
</html>
<!doctype html> 的作用是告诉浏览器“请用现代标准模式解析”,没有它,老浏览器可能进入怪癖模式,盒模型、布局都会出问题;lang="zh-cn" 影响屏幕阅读器和翻译插件的语言判断;meta charset="utf-8" 防止中文乱码;meta name="viewport" 是移动端页面适配的起点。我建议动手一个字一个字敲一遍这套骨架,而不是依赖编辑器的自动补全,因为工作里你会频繁看到别人写出的不规范骨架,只有基础自己熟,出了问题才能一眼定位。
HTML 里另一个核心概念是“块级元素 vs 行内元素”。简单说,块级元素(div、p、h1、ul 等)默认独占一行,宽度撑满父容器;行内元素(span、a、strong 等)只占内容需要的宽度,多个行内元素可在同一行排列。这个差异直接决定了你用 CSS 写布局时选择什么方式,很多新手把 <a> 写进 <div> 后设宽高没反应,就是把行内元素属性记混了。
2.2 语义化标签与初学者最容易忽略的盒子模型
学习 HTML 时,很多讲义会提“语义化”,但没有强调语义化对 CSS 和 JS 选节点的后续影响。用 <header> <nav> <main> <section> <footer> 这些标签,结构清晰是一回事,更重要的是让 CSS 的标签选择器、JS 的 querySelector 有更明确的锚点。当然,语义化标签要配合标题层级使用,不要为了语义化把每个 div 都改成 section。
CSS 部分,盒子模型是关键。标准盒模型中,width 指 content 的宽度,padding 和 border 是在这个宽度基础上向外扩展的;而 box-sizing: border-box 会让 width 包含 padding 和 border。我学的时候一度不理解为什么移动端适配经常加这么一句:
css复制* {
box-sizing: border-box;
}
原因其实很简单:加了这行以后,你设置一个元素宽度为 50%,再给它加上固定 padding 或 border,它的总宽度不会超过父容器的 50%,布局不会无缘无故溢出。没有这行,50% + padding 就会超宽挤爆。这个理论好像很基础,但实际项目里大量“布局乱了”的报错都是从这里来的。
2.3 flex 布局中“子元素宽度自适应”的几种套路
标题相关热搜里有个很高频的问题:flex 布局子元素宽度自适应。我看到很多新手一上来把 flex: 1 当成万能写法,实际上它只是简写,完整展开是 flex-grow、flex-shrink、flex-basis 三个属性。
核心逻辑记住一句话:flex-grow 控制“剩余空间怎么分配”,flex-shrink 控制“空间不够时谁先缩”,flex-basis 控制“在分配剩余空间之前的主轴基础尺寸”。想让多个子元素在一行内按比例填满宽度,最常用的是:
css复制.item {
flex: 1; /* 等价于 flex: 1 1 0% */
}
但这里有个经典大坑:如果子元素里有一长串英文或一张大图片,即使设置了 flex: 1,内容也可能把元素撑开,导致宽度自适应失败。原因是 min-width 的默认值是 auto,而 flex 项目在主轴方向上的最小尺寸默认由内容决定。解决办法是给子元素补一个 min-width: 0(水平方向布局)或 overflow: hidden,让内容可以溢出或截断,flex 的宽度分配才能真正生效。
另外一个实用的套路是“等宽列表 + 最后一个元素不换行”这种场景:父容器用 flex-wrap: wrap,子元素用 flex: 0 0 calc(25% - 间距) 控制每行显示几个。这里 `flex
