Skip to content

HTML / CSS 布局体系

🎯 核心问题

网页是怎么做出来的?为什么有的网页只有文字,有的却像应用一样可以交互? 这个问题会引出 Web 开发的三大基石,让你理解每一个网页背后的结构。


1. HTML、CSS、JavaScript 分别是什么?

1.1 从静态网页到动态应用

想象一下你在街上看到的海报。你只能看,不能互动——海报不会因为你看了就改变内容,也不会因为你点了某个地方就弹出更多信息。

早期的网页就是这样的"电子海报":只能看、不能改、内容固定。

但现代网页完全不同了。它们像桌面应用一样:

  • 你可以点击、拖拽、输入、上传
  • 页面会根据你的操作实时变化
  • 可以像软件一样完成复杂任务(比如在线视频剪辑)

这种转变的核心原因,就是网页技术的三大基石:HTML + CSS + JavaScript

1.2 一个比喻:盖房子

技术🏠 房子比喻实际作用具体例子
HTML房子的结构和材料定义网页的内容和层级这是一面墙、这是一扇窗、这是一个房间
CSS房子的装修和外观控制网页的样式和布局墙刷成蓝色、窗户放在东边、地板铺瓷砖
JavaScript房子的电器和智能系统让网页具备交互和逻辑按开关灯亮了、开门窗帘自动拉开

💡 三者的关系

HTML → CSS:先有房子,才能装修。HTML 是基础,CSS 是美化。

HTML + CSS → JavaScript:先有房子和装修,才能装智能系统。JavaScript 会让"死"的页面变"活"。

核心思想:三者各司其职,缺一不可。只有 HTML 的页面很丑,只有 HTML+CSS 的页面不能互动,三者齐全才能做出像微信网页版、淘宝这样的"Web 应用"。

1.3 动手试试看

👇 下面这个演示展示了 HTML/CSS/JavaScript 三者如何协作:

HTML / CSS / JavaScript 协作演示同一段页面,切换查看三者各自的作用

欢迎来到我的网站

这是一段描述文字

HTML 代码
<h1>欢迎来到我的网站</h1>
<p>这是一段描述文字</p>
<button>点我试试</button>
执行过程
  1. 浏览器解析标签,识别内容类型
  2. h1 是标题,p 是段落,button 是按钮
  3. 按默认样式渲染(此时看起来很朴素)
分工原则:HTML 定义结构(是什么),CSS 定义样式(长什么样),JavaScript 定义行为(能做什么)。

2. HTML:网页的骨架

2.1 为什么需要 HTML?

在 HTML 出现之前,互联网上的内容只是纯文本。就像你现在看的这段文字,没有任何格式、没有层级、没有链接。

纯文本的问题是什么?

  • 无法表达层级:分不清哪是标题、哪是正文、哪是注释
  • 机器看不懂:搜索引擎、屏幕阅读器(盲人用)无法理解内容
  • 无法交互:没有链接、没有按钮、没有输入框

HTML (HyperText Markup Language) 就是为了解决这个问题诞生的。它用"标签"(tag)来标记内容的含义,让浏览器知道"这是什么"。

2.2 HTML 代码长什么样?

HTML 的基本单位是"标签"(tag)。标签用尖括号 < > 包裹,成对出现:

html
<h1>这是标题</h1>
<p>这是段落</p>
<a href="url">这是链接</a>

关键概念

概念解释例子
标签用尖括号包裹的标记<h1></h1>
元素标签 + 内容的整体<h1>标题</h1>
属性标签上的附加信息href="url"class="card"
嵌套标签里再放标签<div><p>文字</p></div>

2.3 如何看懂 HTML 代码?

🎯 零基础必读:看代码的方法

很多新手看到一堆 <xxx> 就晕了。其实看 HTML 代码有固定套路

第一步:找"最外层"

html
<div class="card">        ← 这是容器,里面装着内容
  <h2>标题</h2>
  <p>描述文字</p>
</div>

第二步:看标签名猜含义

标签名一眼记住里面放什么
<div>大盒子任何内容,用来分组
<span>小盒子文字片段,用来标记
<p>段落一段文字
<h1>-<h6>标题标题文字,数字越小越重要
<a>锚点/链接可点击跳转的内容
<img>图片不放内容,用 src 指向图片
<button>按钮可点击的文字/图标
<input>输入框不放内容,用户输入的地方

第三步:看 class 和 id

html
<div class="user-card" id="user-123">
  • class="user-card" → 这个元素的"类型",CSS 可以批量选中
  • id="user-123" → 这个元素的"身份证号",唯一标识

第四步:缩进表示层级

html
<body>
  <header>           ← 缩进表示 header 是 body 的孩子
    <nav>            ← nav 是 header 的孩子
      <a>首页</a>    ← a 是 nav 的孩子
    </nav>
  </header>
</body>

2.4 常用 HTML 标签速查

结构标签(定义页面骨架):

html
<h1>这是一级标题</h1>
<h2>这是二级标题</h2>
<p>这是一个段落</p>
<div>这是一个容器(用来分组)</div>
<span>这是行内容器(用来标记文字)</span>

链接与媒体(让页面丰富):

html
<a href="https://example.com">点击这里跳转</a>
<img src="photo.jpg" alt="照片描述" />
<video src="movie.mp4" controls></video>

表单(收集用户输入):

html
<form>
  <input type="text" placeholder="请输入用户名" />
  <input type="password" placeholder="请输入密码" />
  <button type="submit">登录</button>
</form>

语义化标签(HTML5 新增,让页面含义更明确):

html
<header>页面头部</header>
<nav>导航栏</nav>
<main>主要内容区</main>
<article>一篇文章</article>
<aside>侧边栏</aside>
<footer>页脚</footer>

💡 为什么要用语义化标签?

<div class="header"><header> 看起来效果一样,为什么要用后者?

  1. SEO 友好:搜索引擎能更好理解页面结构
  2. 可访问性:屏幕阅读器能快速定位"导航""主要内容"等区域
  3. 代码可读性:看到 <header> 一眼就知道是头部

什么时候用 div? 当没有合适的语义标签时。比如一个纯装饰性的容器。

2.5 如何记住这么多 HTML 标签?

🎯 新手困惑

"HTML 标签有一百多个,怎么记得住?"

答案是:不需要全部记住。 实际开发中,90% 的情况只用 20 个左右的标签。

按用途分类记忆

一、页面结构类(画骨架)

标签记忆口诀用途
<header>页面或区块的头部
<nav>导航导航链接区域
<main>主体页面主要内容(每页只有一个)
<article>文章独立的内容块(可以单独拿走还有意义)
<section>章节有主题的内容分组
<aside>旁边侧边栏、补充内容
<footer>页面或区块的底部

记忆方法:想象一张报纸——有报头(header)、目录(nav)、正文(main/article)、专栏(aside)、报脚(footer)。

二、内容标记类(说清楚是什么)

标签记忆口诀用途
<h1>-<h6>标题1-6标题层级,h1 最大最重要
<p>段落一段文字
<ul>/<ol>/<li>无序/有序/列表项列表
<a>锚点链接,跳转用
<img>图片图片
<video>/<audio>视频/音频多媒体
<strong>/<em>强调/斜体强调语义化的强调

记忆方法<a> 是 anchor(锚)的缩写,想象船抛锚停在一个地方,链接就是"停"到另一个页面。

三、表单交互类(收集用户输入)

标签记忆口诀用途
<form>表单表单容器
<input>输入各种输入框(type 决定类型)
<textarea>文本区域多行文本输入
<select>/<option>选择/选项下拉选择
<button>按钮按钮
<label>标签输入框的说明文字

记忆方法<input> 的 type 属性决定它长什么样:

  • type="text" → 文本框
  • type="password" → 密码框
  • type="email" → 邮箱框
  • type="checkbox" → 复选框
  • type="radio" → 单选框

四、容器类(分组用)

标签记忆口诀用途
<div>大盒子块级容器,独占一行
<span>小盒子行内容器,只占内容宽度

记忆方法:div = division(分区),span = span(跨度)。div 用来划分大区域,span 用来标记文字片段。

遇到不认识的标签怎么办?

方法一:猜英文单词

很多标签是英文单词的缩写:

  • <abbr> = abbreviation(缩写)
  • <blockquote> = block quote(块引用)
  • <caption> = caption(标题/说明)
  • <figcaption> = figure caption(图片说明)

方法二:查 MDN

MDN HTML 元素参考 有所有标签的详细说明。

方法三:问 AI

"HTML 中的 <dl> 标签是什么意思?什么时候用?"

不用刻意背标签

真正的工作流程是这样的

  1. 你知道要用一个"容器" → 写 <div>
  2. 后来发现这是"导航区域" → 改成 <nav>
  3. 后来发现这是"独立文章" → 改成 <article>

先写出来,再优化语义。标签可以随时改,不用一开始就纠结用哪个。


3. CSS:网页的皮肤

3.1 为什么需要 CSS?

想象你住进了一个毛坯房:有墙、有窗、有门,能住人,但是:

  • 墙是灰色的水泥,不好看
  • 插座和开关随便装,不美观
  • 没有家具,生活不方便

只有 HTML 的网页就是这样:有内容、有结构,但不友好

CSS (Cascading Style Sheets) 就是网页的"装修队"。它不改变 HTML 的结构(不拆墙、不改门),只负责:

  • 🎨 刷墙:改变颜色、背景
  • 🖼️ 挂画:添加边框、阴影、圆角
  • 🪑 摆家具:调整布局、间距、对齐

3.2 CSS 代码长什么样?

CSS 代码有固定格式:

css
选择器 {
  属性名: 属性值;
  属性名: 属性值;
}

三种写法

html
<!-- 方式一:行内样式(临时测试用) -->
<div style="color: red;">红色文字</div>

<!-- 方式二:内部样式(写在 HTML 文件里) -->
<style>
  .red-text { color: red; }
</style>

<!-- 方式三:外部样式(独立 CSS 文件,推荐) -->
<link rel="stylesheet" href="styles.css" />

3.3 如何看懂 CSS 代码?

🎯 零基础必读:看 CSS 的方法

第一步:看选择器——"给谁装修?"

选择器写法含义
标签选择器p { }所有 <p> 标签
类选择器.card { }所有 class="card" 的元素
ID 选择器#header { }唯一的 id="header" 元素
后代选择器.card h2 { }.card 里面的所有 <h2>
组合选择器.card, .box { }.card.box 都选中

第二步:看属性——"装修什么?"

属性分类常见属性作用
文字color, font-size, font-weight颜色、大小、粗细
背景background, background-color背景色、背景图
边框border, border-radius边框线、圆角
间距margin, padding外边距、内边距
布局display, flex, grid排列方式

第三步:看值——"装修成什么样?"

css
.card {
  width: 300px;        /* 固定宽度 */
  padding: 16px;       /* 内边距 16 像素 */
  border-radius: 8px;  /* 圆角 8 像素 */
  background: #fff;    /* 白色背景 */
}

常见单位

  • px:像素,固定大小
  • %:百分比,相对于父元素
  • rem:相对于根元素字体大小
  • vw/vh:相对于视口宽度/高度

3.4 选择器优先级

如果一个元素同时被多个选择器选中,谁说了算?

html
<p class="highlight" id="special">这段文字是什么颜色?</p>
css
p { color: red; }             /* 优先级:1 */
.highlight { color: yellow; } /* 优先级:10 */
#special { color: blue; }     /* 优先级:100 */

答案:蓝色。ID 选择器优先级最高,类选择器次之,标签选择器最低。

内联样式(写在 style 属性里)优先级是 1000,最高!

3.5 盒模型:为什么宽度对不上?

🎯 真实场景

你做一个网页,要求三个卡片并排显示,每个卡片宽度 300px,容器总宽度 900px。你写了:

css
.card { width: 300px; }

结果:第三个卡片掉到下一行了!

为什么? 因为 width: 300px 只是内容宽度,你忘了算 padding 和 border。如果卡片有 padding: 20pxborder: 1px,实际宽度是 342px,三个卡片就是 1026px,超出了容器!

每个 HTML 元素在 CSS 中都被看作一个"盒子",由四层组成。想象你在打包快递:内容是商品,padding 是气泡膜,border 是纸箱,margin 是箱子之间的间隔。

👇 动手试试看:拖动滑块调节各层大小,观察盒模型的变化:

CSS 盒模型理解元素实际占用空间的构成
场景:你要做三个并排卡片,容器宽度 900px,每个卡片设 width: 200px。结果第三个掉下去了——为什么?
100px
15px
5px
10px
box-sizing:
margin
border
padding
content
100px
实际占用宽度160px
100 + 15×2 + 5×2 + 10×2 = 160px
三个卡片共 480px,可以放下
CSS
.box {
box-sizing: content-box;
width: 100px;
padding: 15px;
border: 5px solid #ccc;
margin: 10px;
}
关键区别:content-box(默认)的 width 只是内容宽度; border-box 的 width 包含 content + padding + border。推荐全局设置 box-sizing: border-box

解决方案

css
.box {
  box-sizing: border-box;  /* 让 width 包含 padding 和 border */
  width: 200px;
  padding: 10px;
  border: 5px;
}

这样,width: 200px 就是最终宽度,padding 和 border 会"挤"在里面。

3.6 Flexbox:怎么让元素自动对齐?

Flexbox 是现代 CSS 最常用的布局方式。它让元素自动排列对齐,就像书架上的书会自动对齐一样。

👇 动手试试看:切换方向、对齐方式,观察盒子如何排列:

Flexbox 布局通过调整参数观察元素排列方式的变化
主轴
主轴 (Main Axis)元素排列的方向,由 flex-direction 决定
交叉轴
交叉轴 (Cross Axis)垂直于主轴,用于对齐元素
1
2
3
4
5
6
主轴方向: 水平 →交叉轴方向: 垂直 ↓
CSS
display: flex;
记忆方法:justify-content 控制主轴方向的对齐(水平时左右,垂直时上下); align-items 控制交叉轴方向的对齐。

Flex 核心概念

属性作用常用值
display: flex开启 Flex 布局-
flex-direction主轴方向row(水平)、column(垂直)
justify-content主轴对齐flex-startcenterspace-between
align-items交叉轴对齐stretchcenterflex-start
flex-wrap是否换行nowrapwrap
gap元素间距10px1rem

3.7 SCSS:CSS 的"升级版"

🎯 真实场景

你写了一个项目,CSS 文件有 2000 行。后来要改主题色,你发现:

  • 主色调 #3b82f6 出现了 50 次
  • 改一个颜色要全局搜索替换
  • 还要担心漏改了某个地方

SCSS 解决的问题:变量、嵌套、混入、模块化

SCSS 示例

scss
// 1. 变量:定义主题色
$primary-color: #3b82f6;

// 2. 嵌套:父子关系一目了然
.card {
  background: white;
  
  h2 {
    color: $primary-color;
  }
  
  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  }
}

编译后变成普通 CSS

css
.card {
  background: white;
}
.card h2 {
  color: #3b82f6;
}
.card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

SCSS vs Less vs 原生 CSS

特性原生 CSSSCSSLess
变量--var$var@var
嵌套
混入@mixin.mixin()
学习曲线简单中等中等

💡 新手建议

  1. 先学好原生 CSS:预处理器只是"语法糖",本质还是 CSS
  2. 项目大了再用 SCSS:小项目直接写 CSS 更简单
  3. 现代 CSS 已经支持变量--primary-color: #3b82f6; 原生就能用

3.8 如何记住这么多 CSS 属性?

🎯 新手困惑

"CSS 属性有好几百个,怎么记得住?"

答案是:按用途分类,记住核心属性,其他的用到再查。

按用途分类记忆

一、文字排版类(管文字长什么样)

属性记忆口诀常用值
color颜色red#fffrgb(0,0,0)
font-size字号16px1rem1.5em
font-weight字重normalbold100-900
font-family字体"微软雅黑"sans-serif
line-height行高1.524px
text-align文字对齐leftcenterright
text-decoration文字装饰noneunderlineline-through

记忆方法:想象你在 Word 里排版——改颜色、改大小、加粗、改字体、调行距、对齐、加下划线。

二、盒模型类(管元素占多大空间)

属性记忆口诀常用值
width/height宽/高100px50%100vw
padding内边距10px10px 20px
margin外边距10pxauto(居中用)
border边框1px solid #ccc
border-radius圆角4px50%(圆形)
box-sizing盒模型border-box(推荐)

记忆方法:padding 是"内"边距(内容到边框的距离),margin 是"外"边距(边框到其他元素的距离)。

简写规则

css
/* 四个值:上 右 下 左(顺时针) */
padding: 10px 20px 15px 25px;

/* 两个值:上下 左右 */
padding: 10px 20px;

/* 一个值:四个方向都一样 */
padding: 10px;

三、背景与边框类(管元素长什么样)

属性记忆口诀常用值
background背景#fffurl(bg.jpg)linear-gradient(...)
background-color背景色#fffrgba(0,0,0,0.5)
background-image背景图url(photo.jpg)
background-size背景大小covercontain100%
background-position背景位置centertop left
box-shadow盒阴影0 2px 10px rgba(0,0,0,0.1)
opacity透明度0-1(0 完全透明)

记忆方法background 是简写,可以一次设置多个值:

css
background: #fff url(bg.jpg) no-repeat center/cover;
/*          颜色  图片      是否重复   位置/大小 */

四、布局类(管元素怎么排列)

属性记忆口诀常用值
display显示方式blockinlineflexgridnone
position定位staticrelativeabsolutefixedsticky
top/right/bottom/left四个方向10px50%(配合 position 使用)
z-index层级数字越大越在上层
float浮动leftright(老方法,不推荐)
overflow溢出处理visiblehiddenscrollauto

position 记忆方法

  • static:默认,正常流
  • relative:相对自己原来的位置偏移
  • absolute:相对最近的定位祖先元素定位
  • fixed:相对视口定位(滚动也不动)
  • sticky:滚动到一定位置后固定

五、Flexbox 布局类(一维布局神器)

属性记忆口诀作用
display: flex开启 Flex容器变成 Flex 容器
flex-direction方向row(横向)、column(纵向)
justify-content主轴对齐元素在主轴上怎么排
align-items交叉轴对齐元素在交叉轴上怎么对齐
flex-wrap换行nowrapwrap
gap间隙元素之间的间距
flex弹性子元素的伸缩比例

记忆方法

  • justify = 证明/对齐 → 主轴对齐
  • align = 排列/对齐 → 交叉轴对齐

六、动画过渡类(管元素怎么动)

属性记忆口诀常用值
transition过渡all 0.3s ease
transform变换translate(10px)rotate(45deg)scale(1.1)
animation动画fadeIn 1s ease forwards

简写规则

css
/* transition: 属性 时长 缓动函数 延迟 */
transition: all 0.3s ease 0s;

/* transform 可以组合多个变换 */
transform: translateX(10px) rotate(45deg) scale(1.1);

遇到不认识的属性怎么办?

方法一:猜英文单词

很多属性是英文单词或缩写:

  • margin = 边缘、余地
  • padding = 填充
  • border = 边界
  • visibility = 可见性
  • cursor = 光标

方法二:按场景联想

当你想实现某个效果时,想想"关键词":

我想...可能的属性
改颜色colorbackground-colorborder-color
改大小widthheightfont-size
改位置marginpositiontop/left
改间距paddingmargingap
隐藏元素display: nonevisibility: hiddenopacity: 0
居中margin: autotext-align: centerjustify-content: center
加圆角border-radius
加阴影box-shadowtext-shadow
加动画transitionanimation

方法三:查 MDN 或问 AI

MDN CSS 属性参考 有所有属性的详细说明。

"CSS 中如何让文字只显示一行,超出部分用省略号?"

方法四:用开发者工具"偷师"

看到喜欢的网页效果:

  1. 右键 → "检查"
  2. 选中元素,看 Styles 面板
  3. 直接复制 CSS 属性

不用刻意背属性

真正的工作流程是这样的

  1. 你知道要"居中" → 搜索"CSS 居中"
  2. 复制代码,改改数值
  3. 用多了就记住了

推荐的学习路径

  1. 先掌握盒模型widthheightpaddingmarginborder
  2. 再掌握 Flexboxdisplay: flexjustify-contentalign-items
  3. 然后掌握定位positiontop/leftz-index
  4. 最后学动画transitiontransformanimation

其他属性用到再查,用多了自然就记住了。


4. JavaScript:网页的大脑

4.1 为什么需要 JavaScript?

只有 HTML + CSS 的网页,就像商店橱窗里的模特

  • ✅ 看起来很漂亮(CSS)
  • ✅ 结构很清晰(HTML)
  • ❌ 但你跟它说话,它不会回应
  • ❌ 你按了按钮,什么也不会发生

JavaScript 让网页从"橱窗模特"变成"真人":

  • ✅ 点击按钮,会弹出提示
  • ✅ 输入文字,会实时检查格式
  • ✅ 滚动页面,会加载更多内容
  • ✅ 提交表单,会显示"正在提交..."

4.2 JavaScript 代码长什么样?

能力一:记住数据(变量)

javascript
let userName = '张三'
let isLoggedIn = true
let cartCount = 5

能力二:重复做事(函数)

javascript
function sayHello(name) {
  return '你好,' + name + '!'
}

console.log(sayHello('张三'))  // 输出:你好,张三!

能力三:响应事件(事件监听)

javascript
button.addEventListener('click', function() {
  alert('按钮被点击了!')
})

能力四:修改页面(DOM 操作)

javascript
document.getElementById('title').textContent = '新标题'
document.getElementById('box').style.background = 'red'

4.3 如何看懂 JavaScript 代码?

🎯 零基础必读:看 JS 代码的方法

第一步:找变量——"记住了什么?"

javascript
const API_URL = 'https://api.example.com'  // 常量,不会变
let count = 0                                // 变量,会变
const user = { name: '张三', age: 25 }       // 对象,多个数据
const items = ['苹果', '香蕉', '橙子']        // 数组,列表数据

第二步:找函数——"能做什么?"

javascript
// 函数名通常能猜出用途
function handleClick() { }      // 处理点击
function fetchData() { }        // 获取数据
function validateForm() { }     // 验证表单

第三步:找事件——"什么时候触发?"

javascript
button.addEventListener('click', handleClick)     // 点击时
input.addEventListener('input', validateForm)     // 输入时
window.addEventListener('scroll', loadMore)       // 滚动时

第四步:找 DOM 操作——"改了什么?"

javascript
element.textContent = '新内容'     // 改文字
element.classList.add('active')    // 加样式类
element.style.display = 'none'     // 隐藏元素
parent.appendChild(child)          // 添加元素

4.4 DOM:JavaScript 如何操作页面?

浏览器读取 HTML 代码后,不会把它们当成一堆字符串,而是在内存里把它们画成一棵"树":

Document (文档)

<html>
    ├─<head>
    │   └─<title>我的网页</title>
    └─<body>
        ├─<h1>欢迎</h1>
        └─<div class="card">
            ├─<img src="photo.jpg">
            └─<p>一段文字</p>

这棵树就叫 DOM 树。每个 HTML 标签都是这棵树上的一个"节点"。

怎么找到节点?

javascript
// 按 ID 找(最快,唯一)
const element = document.getElementById('header')

// 按选择器找(最常用)
const element = document.querySelector('.card h2')    // 找第一个
const elements = document.querySelectorAll('button')  // 找所有

// 按关系找
element.parentNode           // 找父节点
element.children             // 找子节点
element.nextElementSibling   // 找下一个兄弟

性能警告:操作 DOM 是很的。每次修改 DOM,浏览器都要重新计算布局、重新绘制。

javascript
// ❌ 低效:循环 1000 次,每次都操作 DOM
for (let i = 0; i < 1000; i++) {
  document.body.appendChild(createDiv())
}

// ✅ 高效:先拼好,一次性插入
const fragment = document.createDocumentFragment()
for (let i = 0; i < 1000; i++) {
  fragment.appendChild(createDiv())
}
document.body.appendChild(fragment)

这也正是 Vue / React 等现代框架诞生的原因:它们在内存里玩"虚拟 DOM",计算好最小修改量,最后才去动真正的 DOM。

👇 动手试试看:DOM 操作的基本方法:

DOM 操作演示通过 JavaScript 动态修改页面内容、样式和结构
修改内容
修改样式
添加/删除元素

欢迎学习 DOM

这是一个演示 DOM 操作的卡片区域。

  • 项目 1
  • 项目 2
对应的 JavaScript 代码
// 点击左侧按钮查看对应代码
常用 DOM 方法
querySelector()按选择器查找元素
textContent获取/设置文本内容
classList操作元素的 CSS 类
createElement()创建新元素
appendChild()添加子元素
remove()删除元素
注意:频繁操作 DOM 会影响性能。现代框架(Vue/React)使用虚拟 DOM 来优化这个过程——先在内存中计算差异,再批量更新真实 DOM。

4.5 ECMAScript:JavaScript 的版本演进

ECMAScript 是 JavaScript 的"标准说明书"。浏览器厂商按照这个标准来实现 JavaScript 引擎。

为什么要有版本号?

JavaScript 不是一成不变的。每年都会新增功能、修复问题。版本号告诉你"这个浏览器支持哪些功能"。

重要版本一览

版本年份核心特性解决了什么问题
ES52009严格模式、forEach/map/filter规范化语言,增加数组方法
ES6/ES20152015let/const、箭头函数、classPromise、模块化最大的更新,现代 JS 的起点
ES20162016includes()** 幂运算小更新
ES20172017async/awaitObject.entries()异步代码更易读
ES20182018... 扩展运算符、Promise.finally()对象和异步增强
ES20202020可选链 ?.、空值合并 ??BigInt安全访问嵌套属性
ES20212021replaceAll()、逻辑赋值 ??=字符串和赋值增强
ES20222022顶层 await.at() 索引模块异步加载更方便

ES6+ 最常用的新语法

1. letconst 替代 var

javascript
// ❌ 旧写法:var 有变量提升,容易出 bug
var name = '张三'
if (true) {
  var name = '李四'  // 覆盖了外面的 name
}
console.log(name)  // '李四',不是预期的结果

// ✅ 新写法:let 有块级作用域
let name = '张三'
if (true) {
  let name = '李四'  // 只在这个 if 里有效
}
console.log(name)  // '张三',符合预期

// ✅ const:声明后不能重新赋值
const PI = 3.14159
PI = 3  // 报错!防止意外修改

2. 箭头函数:更简洁的函数写法

javascript
// ❌ 旧写法
const add = function(a, b) {
  return a + b
}

// ✅ 新写法
const add = (a, b) => a + b

// 箭头函数的 this 绑定外层作用域
const obj = {
  name: '张三',
  // ❌ 普通函数:this 指向调用者
  oldWay: function() {
    setTimeout(function() {
      console.log(this.name)  // undefined
    }, 100)
  },
  // ✅ 箭头函数:this 继承自 obj
  newWay: function() {
    setTimeout(() => {
      console.log(this.name)  // '张三'
    }, 100)
  }
}

3. 解构赋值:从对象/数组中提取数据

javascript
// 对象解构
const user = { name: '张三', age: 25, city: '北京' }
const { name, age } = user  // 直接提取
console.log(name)  // '张三'

// 数组解构
const colors = ['red', 'green', 'blue']
const [first, second] = colors
console.log(first)  // 'red'

// 函数参数解构
function greet({ name, age }) {
  console.log(`${name} 今年 ${age} 岁`)
}
greet(user)  // '张三 今年 25 岁'

4. 模板字符串:字符串拼接不再痛苦

javascript
// ❌ 旧写法:一堆引号和加号
const msg = '用户 ' + name + ' 的年龄是 ' + age + ' 岁'

// ✅ 新写法:反引号 + ${}
const msg = `用户 ${name} 的年龄是 ${age} 岁`

// 还支持多行
const html = `
  <div class="card">
    <h2>${name}</h2>
    <p>年龄:${age}</p>
  </div>
`

5. async/await:异步代码像同步一样写

javascript
// ❌ 回调地狱
fetchUser(function(user) {
  fetchOrders(user.id, function(orders) {
    fetchDetails(orders[0].id, function(details) {
      console.log(details)
    })
  })
})

// ✅ async/await
async function getUserData() {
  const user = await fetchUser()
  const orders = await fetchOrders(user.id)
  const details = await fetchDetails(orders[0].id)
  console.log(details)
}

6. 可选链 ?. 和空值合并 ??

javascript
const user = {
  name: '张三',
  address: {
    city: '北京'
  }
}

// ❌ 旧写法:层层判断
const street = user && user.address && user.address.street
const streetName = street !== undefined ? street : '未知'

// ✅ 新写法:可选链 + 空值合并
const streetName = user?.address?.street ?? '未知'

💡 如何知道浏览器支持哪些特性?

  1. 查兼容表caniuse.com 输入特性名
  2. 用构建工具:Babel 可以把新语法转成旧浏览器支持的代码
  3. 看目标用户:如果只支持现代浏览器,大部分 ES6+ 特性都能直接用

4.6 TypeScript:给 JavaScript 加上类型约束

为什么需要 TypeScript?

场景一:函数参数类型不确定

javascript
// JavaScript
function calculateTotal(price, quantity) {
  return price * quantity
}

calculateTotal(100, 5)      // 500 ✅
calculateTotal('100', 5)    // '1005' ❌ 字符串拼接,不是乘法
calculateTotal(100, '5')    // 500 ✅ 但这是运气好

JavaScript 不会告诉你参数类型错了,直到运行时才发现问题。

场景二:对象属性拼写错误

javascript
// JavaScript
const user = {
  name: '张三',
  age: 25
}

console.log(user.nmae)  // undefined,拼写错误但不报错

TypeScript 解决这些问题

typescript
// TypeScript
interface User {
  name: string
  age: number
}

function greet(user: User) {
  console.log(`你好,${user.name}`)
  console.log(user.nmae)  // ❌ 编译时报错:属性 'nmae' 不存在
}

greet({ name: '张三', age: 25 })        // ✅
greet({ name: '张三', age: '25' })      // ❌ 编译时报错:age 应该是 number
greet({ name: '张三' })                 // ❌ 编译时报错:缺少 age

TypeScript 的核心概念

1. 基本类型

typescript
let name: string = '张三'
let age: number = 25
let isActive: boolean = true
let anyValue: any = '可以是任何类型'  // 不推荐,失去类型检查的意义

2. 接口(Interface):定义对象结构

typescript
interface Product {
  id: number
  name: string
  price: number
  discount?: number  // 可选属性
  readonly createdAt: Date  // 只读属性
}

const product: Product = {
  id: 1,
  name: 'iPhone 15',
  price: 6999,
  createdAt: new Date()
}

3. 类型别名(Type)

typescript
type ID = string | number  // 联合类型
type Status = 'pending' | 'approved' | 'rejected'  // 字面量类型

function updateStatus(id: ID, status: Status) {
  // ...
}

updateStatus(1, 'approved')      // ✅
updateStatus('abc', 'pending')   // ✅
updateStatus(1, 'processing')    // ❌ 'processing' 不是有效的 Status

4. 泛型:可复用的类型

typescript
// 不用泛型:每个类型写一遍
function getFirstNumber(arr: number[]): number {
  return arr[0]
}
function getFirstString(arr: string[]): string {
  return arr[0]
}

// 用泛型:一个函数搞定
function getFirst<T>(arr: T[]): T {
  return arr[0]
}

getFirst([1, 2, 3])        // 返回 number
getFirst(['a', 'b', 'c'])  // 返回 string

TypeScript vs JavaScript 对比

特性JavaScriptTypeScript
类型检查运行时才发现错误编译时就发现错误
IDE 支持基础提示智能补全、重构、跳转定义
学习曲线简单需要学习类型系统
适用场景小项目、原型大型项目、团队协作
运行方式浏览器直接运行需要编译成 JavaScript

实际开发中的 TypeScript

typescript
// API 响应类型定义
interface ApiResponse<T> {
  code: number
  message: string
  data: T
}

interface User {
  id: number
  name: string
  email: string
}

// 带类型的 API 请求
async function fetchUser(id: number): Promise<ApiResponse<User>> {
  const response = await fetch(`/api/users/${id}`)
  return response.json()
}

// 使用时 IDE 会提示所有属性
fetchUser(1).then(res => {
  console.log(res.data.name)   // ✅ IDE 自动补全
  console.log(res.data.nmae)   // ❌ 编译时报错
})

💡 新手建议

  1. 先学好 JavaScript:TypeScript 是 JS 的超集,不懂 JS 学 TS 会很痛苦
  2. 小项目不用强上 TS:类型定义会增加代码量,简单项目反而变复杂
  3. 从 JSDoc 开始过渡:在 JS 文件里写 /** @type {User} */ 注释,体验类型提示
  4. any 是妥协,不是解决方案:遇到类型问题先尝试解决,不要直接 any

4.7 现代 JavaScript 开发工具链

🎯 为什么需要工具链?

浏览器只认识 HTML/CSS/JS。但现代开发中,我们会用:

  • TypeScript:浏览器不认识,需要编译成 JS
  • SCSS/Less:浏览器不认识,需要编译成 CSS
  • 模块化import/export 需要打包成一个文件
  • 新语法:ES6+ 需要转译成旧浏览器支持的代码

工具链就是把这些"开发时用的代码"转换成"浏览器能运行的代码"。

核心工具

工具作用类比
Node.jsJavaScript 运行环境让 JS 可以脱离浏览器运行
npm/yarn/pnpm包管理器下载别人写好的代码库
Vite/Webpack构建工具把源代码打包成浏览器能运行的代码
Babel编译器把新语法转成旧语法
ESLint代码检查发现代码问题和风格不一致

一个典型的开发流程

bash
# 1. 初始化项目
npm create vite@latest my-app -- --template vue-ts

# 2. 安装依赖
cd my-app
npm install

# 3. 开发模式(热更新)
npm run dev

# 4. 构建生产版本
npm run build

5. 三者的协作关系

5.1 分工对比

角色负责什么不做什么典型示例
HTML定义结构与语义不负责样式/交互<section><h1>标题</h1></section>
CSS控制外观与布局不负责逻辑/数据.card { background: white; }
JavaScript处理交互与逻辑不负责结构定义button.onclick = () => alert()

5.2 一个完整的协作示例

html
<!DOCTYPE html>
<html>
<head>
  <style>
    /* CSS:让卡片好看 */
    .card {
      border: 1px solid #ddd;
      border-radius: 8px;
      padding: 16px;
      max-width: 300px;
    }
    .card button {
      background: #3b82f6;
      color: white;
      border: none;
      padding: 8px 16px;
      border-radius: 4px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <!-- HTML:定义卡片结构 -->
  <div class="card">
    <h2 id="title">点击按钮</h2>
    <button id="btn">点我</button>
  </div>

  <script>
    // JavaScript:让按钮能点击
    const btn = document.getElementById('btn')
    const title = document.getElementById('title')
    
    btn.addEventListener('click', function() {
      title.textContent = '已点击!'
      alert('标题已改变')
    })
  </script>
</body>
</html>

6. 遇到不认识的代码怎么办?

6.1 问 AI

"HTML 中的 <aside> 标签是什么意思?什么时候用?"

"CSS 中的 position: sticky 是什么效果?"

6.2 查 MDN

MDN Web Docs 是最权威的 Web 技术文档。遇到不认识的标签、属性、方法,直接搜索即可。

6.3 浏览器开发者工具

  1. 右键点击页面元素 → "检查"
  2. Elements 面板看到 HTML 结构
  3. Styles 面板看到 CSS 样式
  4. Console 面板可以执行 JS 代码

6.4 常见 CSS 属性速查

看到这个它是干嘛的
display: flex开启弹性布局
position: absolute绝对定位
z-index: 100层级,数字大的在上面
overflow: hidden超出部分隐藏
cursor: pointer鼠标变成手型
transition: all 0.3s动画过渡效果
box-sizing: border-box让 width 包含 padding 和 border

7. 名词速查表

名词英文用人话解释
HTMLHyperText Markup Language超文本标记语言,用标签描述网页结构
CSSCascading Style Sheets层叠样式表,控制颜色、布局、动画
JavaScriptJavaScript网页的编程语言,负责交互和逻辑
DOMDocument Object Model文档对象模型,用对象树表示页面
FlexboxFlexible Box Layout一种一维布局方案,易于对齐与分布
盒模型CSS Box Model元素从内容到外边距的层层盒子
SCSSSassy CSSCSS 预处理器,支持变量、嵌套、混入
TypeScriptTypeScriptJavaScript 的超集,增加了类型系统
ES6ECMAScript 2015JavaScript 的一个重要版本,新增很多语法
语义化Semantic HTML使用有含义的标签(如 header)而不是 div
响应式Responsive Design页面自动适配不同屏幕尺寸的设计

总结

现在你已经知道:HTML 定义骨架,CSS 负责颜值,JavaScript 赋予灵魂

这三者是 Web 开发的基石。理解了它们,你就能:

  • 看懂任何网页的源代码(右键 → "查看网页源代码")
  • 修改别人的网页(浏览器 DevTools → Elements)
  • 开始学习前端框架(Vue/React),它们都是基于这三者的

下一步建议

  • 如果你想快速做出网页,可以学习 VueReact 框架
  • 如果你想深入理解 CSS,可以学习 FlexboxGrid 布局
  • 如果你想提升代码质量,可以学习 TypeScript