写前端三件套(HTML+CSS+JS)的文章很多,但大多数要么太理论,读完还是写不出页面;要么太零散,今天学个标签明天学个选择器,学完就忘。这篇文章想换一种讲法:不做大而全的百科式罗列,而是用 3 小时带你走完一条“能动手、能验证、能看懂自己代码”的最小路径。
先说判断:零基础学前端,最忌讳的不是笨,而是顺序错了。很多人一上来就啃厚厚的参考书,或者直接扎进 Vue、React 框架里,结果 HTML 语义还没搞清,就被组件、状态管理砸晕,最后心态崩了。正确顺序应该是什么?先用 HTML 搭骨架,再用 CSS 做样式,最后用 JS 加交互——这也是为什么业界把这三种语言叫做“前端三件套”。
如果你正处在这些阶段,这篇文章非常适合你:
- 完全零基础,想转行或自学前端,但不知道从哪里下手;
- 学过一点 HTML 和 CSS,但 JS 一直没入门,写不出交互效果;
- 想快速了解网页从“静态”到“动态”的完整过程,再决定要不要深入。
读完这篇文章,你会亲手做出一个可交互的待办事项页面,并理解背后每一行代码的作用。这个项目很小,但它把 HTML 结构、CSS 布局、JS 事件处理、DOM 操作全部串起来了。跑通它,你就等于掌握了一套“前端思考方式”。
下面是 3 小时的学习路线:
| 时间段 | 学习内容 | 产出 |
|---|---|---|
| 第 1 小时 | HTML 核心标签与页面结构 | 一个语义清晰的页面骨架 |
| 第 2 小时 | CSS 选择器、盒模型、Flex 布局 | 一个整洁、具备现代感的页面 |
| 第 3 小时 | JS 变量、事件、DOM 操作 | 一个能增删数据的待办清单 |
1. 这篇文章真正要解决的问题
很多人问“前端难不难”,这个问题很难一句话回答。更准确的问法是:“你希望用前端解决什么问题?”
如果你只是想看懂网页、改改页面样式、给自己做个简单工具页,那么前端三件套的入门成本非常低。低到什么程度呢?你只需要一个浏览器,再加一个文本编辑器(比如 VS Code),不需要安装复杂的开发环境,不需要配置服务器,写完 HTML 文件直接用浏览器打开就能看到效果。这种“所见即所得”的反馈速度,是很多人愿意学前端的重要原因。
但如果你是想马上找一份前端开发的工作,那就需要清醒一点:3 小时只能帮你建立完整的概念框架和动手能力基础,距离企业用人要求还有不小的距离。本文的目标是把地基打牢,而不是给你造一栋精装房。
前端这个领域,表面上是三个技术,底层其实是两种思维:
- 结构思维:用 HTML 把内容组织成树状结构,让浏览器和搜索引擎都能理解;
- 变化思维:用 CSS 控制视觉表现,用 JS 响应用户行为,让页面从“死”的变成“活”的。
很多初学者写页面乱,本质是结构思维没建立起来;写交互总报错,本质是变化思维没建立起来。这两种思维,恰恰是这三小时里最值得你花精力体会的东西。
具体到本文,你会看到:
- HTML 不是“背标签”,而是“设计内容层级”;
- CSS 不是“背属性”,而是“理解布局规则”;
- JS 不是“背语法”,而是“理解数据和事件”。
为了避免你学完就忘,整个项目的核心主线是**“做一个待办事项页面”**。你没有看错,就是很多人瞧不上眼的 Todo List。因为这个项目天然包含了前端最核心的几件事:用户输入、列表渲染、数据增删、界面状态变化。能把这个项目做明白,你已经超过了一半的“只看不练型选手”。
2. HTML:网页的结构骨架与核心标签
2.1 HTML 是什么,不是什么
HTML 全称是 HyperText Markup Language(超文本标记语言)。注意,它叫“标记语言”,不是“编程语言”。它没有变量、没有逻辑判断、没有循环,它的工作只有一件事:用标签描述内容的结构。
标签的书写形式非常简单:由尖括号包裹的标签名组成,通常有开始标签和结束标签。
<p>这是一段文本</p>也有少数自闭合标签,比如换行标签:
<br>你要把 HTML 理解成一份“文档大纲”,它告诉浏览器:这里有标题,这里是段落,这里是一个链接,那里是一张图片。至于标题用什么颜色、段落显示多大字号、图片显示多宽,这些都不归 HTML 管,而是 CSS 的工作。
很多初学者刚学 HTML,特别喜欢背标签,什么article、section、aside、footer,背得头大。这里给个建议:先记住使用频率最高的 20 个标签,其余的等用到再查。前端是一个极其依赖查文档的领域,这不是丢人的事,反而是从业常态。
2.2 一份 HTML 文档的标准结构
任何 HTML 文件,打开后都是下面这个模板。它不需要你记,VS Code 里输入!再按 Tab 键就能自动生成。
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>我的第一个页面</title> </head> <body> <h1>你好,前端</h1> <p>这是我的第一个页面。</p> </body> </html>几个关键点解释一下:
<!DOCTYPE html>:告诉浏览器当前文档使用的是 HTML5 标准。不要省略,否则浏览器会进入“怪异模式”,导致样式解析异常。<html lang="zh-CN">:声明页面语言为中文,这对无障碍访问和 SEO 都有影响。<head>:存放页面的元信息,比如字符集、视口配置、标题、外部样式表和脚本的引入。这里的 content 不会直接显示在页面上。<meta charset="UTF-8">:指定字符编码为 UTF-8。如果不写,页面上中文很可能变成乱码。<meta name="viewport" ...>:移动端适配必备,控制页面在手机上的显示宽度。不写的话,手机浏览器会把桌面端页面整体缩小显示,体验很差。<body>:所有用户能看到的页面内容,都必须放进这里。
记住一个习惯:先写结构,再填内容。不要一上来就急着打字,先在脑子里想清楚,这个页面由哪些模块组成,每个模块是什么关系。
2.3 常用的 HTML 标签与语义化
下面这张表覆盖了日常开发中最常用的一批标签,建议你先按这个范围学,不要贪多。
| 标签 | 作用 | 典型使用场景 |
|---|---|---|
h1~h6 | 标题,一级到六级 | 页面主标题、章节标题 |
p | 段落 | 正文文本 |
a | 超链接 | 跳转其他页面或锚点 |
img | 图片 | 展示图片素材 |
ul/ol/li | 无序列表 / 有序列表 / 列表项 | 导航菜单、商品列表 |
div | 无语义的分块容器 | 页面布局分区 |
span | 无语义的行内容器 | 对一段文本的部分内容做样式 |
input | 输入框 | 表单、搜索框 |
button | 按钮 | 提交、触发交互 |
form | 表单容器 | 收集用户输入 |
nav | 导航区域 | 顶部菜单栏 |
header | 页头 | 页面顶部区域 |
main | 页面主要内容区 | 主体内容容器 |
footer | 页脚 | 版权信息、底部链接 |
那么“语义化”是什么意思?现在很多表格布局时代的老前端喜欢全用div一把梭,但语义化标签的初衷是让“代码自己说明自己”。你可以想象自己是一名阅读你代码的同事,如果他打开文件第一眼就看到header、nav、main、footer,会比看到一堆div class="top"、div class="mid"、div id="bottom"轻松得多。更重要的是,搜索引擎爬虫和屏幕阅读器都会根据语义标签来理解页面结构,这对 SEO 和无障碍体验都有实实在在的好处。
2.4 实战:搭建待办页面的 HTML 骨架
现在开始动手。创建一个新文件夹,命名为todo-app,在里面新建一个文件index.html,写入下面的代码:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>我的待办清单</title> <link rel="stylesheet" href="style.css"> </head> <body> <div class="app"> <h1>📝 我的待办清单</h1> <div class="input-area"> <input type="text" id="todoInput" placeholder="输入新的待办事项..."> <button id="addBtn">添加</button> </div> <ul id="todoList"> <li class="todo-item"> <input type="checkbox" class="todo-checkbox"> <span>学习 HTML 标签</span> <button class="delete-btn">删除</button> </li> <li class="todo-item"> <input type="checkbox" class="todo-checkbox"> <span>学习 CSS 布局</span> <button class="delete-btn">删除</button> </li> </ul> </div> <script src="script.js"></script> </body> </html>注意几个细节:
<link rel="stylesheet" href="style.css">放在head中,表示引入外部 CSS 文件;<script src="script.js">放在body的末尾。因为 JS 要操作 DOM,而 DOM 必须等页面加载完才能操作。放在末尾可以避免“还没渲染完就开始找节点”的问题。- 我用了两个
input,一个是文本框,一个是复选框,它们都是普通的输入控件,不要混淆。
现在就差 CSS 和 JS 文件了。别急着关编辑器,我们继续。
3. CSS:从“白底黑字”到“现代界面”
3.1 CSS 到底是干什么的
CSS 全称是 Cascading Style Sheets(层叠样式表)。它的核心价值在于:把“内容”和“表现”分离。同样一份 HTML,引用不同的 CSS,可以呈现出完全不同的视觉风格。这就像同一件白 T 恤,搭休闲裤是日常风,搭半身裙是通勤风,搭牛仔裤是街头风——衣服是 HTML,搭配方案是 CSS。
初学者最容易在 CSS 里遇到的困惑是:为什么我设的颜色不生效?为什么这个元素不居中?要解决这些问题,不能只背属性,必须理解 CSS 的两个底层机制:
- 选择器:决定“样式作用于哪个元素”;
- 盒模型:决定“元素在页面中占多大空间、怎么排列”。
3.2 选择器:精准定位要修饰的元素
CSS 选择器种类很多,但入门阶段掌握下面五类就够用。
| 选择器 | 写法 | 示例 | 作用 |
|---|---|---|---|
| 标签选择器 | 标签名 | p { color: red; } | 选择所有p标签 |
| 类选择器 | .类名 | .app { width: 500px; } | 选择所有class="app"的元素 |
| ID 选择器 | #id名 | #addBtn { padding: 8px; } | 选择id="addBtn"的元素 |
| 后代选择器 | 父 子 | .input-area input { } | 选择父元素内部的所有匹配子元素 |
| 伪类选择器 | 元素:hover | button:hover { } | 元素处于某个状态时应用样式 |
这里特别提醒两个高频误区:
第一,ID 在同一个页面里只能出现一次。它是元素的“身份证号”,不能重复使用。如果需要为多个元素设置相同样式,应该使用类选择器。
第二,后代选择器并不要求必须是父子关系,只要是祖先和后代即可。比如.app input会选择.app内部所有层级的input,即使中间还包裹了div。
关于伪类选择器,很多初学者被“伪”字吓到,其实它不过是指“元素在某一种特殊状态下的样式”。最常见的:hover是鼠标悬停。此外你还会经常见到:first-child、:last-child、:nth-child()这类用于选位置的伪类,它们在做列表样式时非常有用。
3.3 盒模型:理解空间的底层逻辑
在 CSS 中,每一个元素都可以看作一个盒子。从内到外分别是:
- Content(内容区):文本或图片显示的区域;
- Padding(内边距):内容与边框之间的距离;
- Border(边框):盒子的边框;
- Margin(外边距):盒子与其他元素之间的间距。
一个盒子的实际宽度公式,是面试题也爱考的经典问题:
实际宽度 = width + padding-left + padding-right + border-left + border-right这意味着,如果你给一个元素设置了width: 200px和padding: 20px,它最终的视觉宽度不是 200px,而是 240px。这个问题困扰过无数新手,因为它直接导致“明明设置了宽度,却还是溢出”的现象。
解决办法有两个:
一是记住公式,计算时手动扣除。
二是使用box-sizing属性,把宽度计算方式从content-box改成border-box:
* { box-sizing: border-box; }个人强烈建议你在所有新项目里都加上这一行。border-box的意思是,你设置的width就是元素最终占据的宽度,padding 和 border 都在这 200px 内部消化。这样一来,心智负担会小很多。
3.4 布局方式:别急着学 Grid,先把 Flex 用熟
CSS 发展到今天,主流的布局方式已经不再是float,更不是position一点点拼凑。你要学的下一个重点就是Flexbox(弹性布局)。
Flex 最核心的一句话是:让容器里的子元素按照主轴方向排列,并自动处理空间分配与对齐。使用方式很简单,给父容器写上display: flex,它的直接子元素就会变成“弹性项”,沿水平方向排成一行。
.input-area { display: flex; gap: 8px; }这是 Flex 布局中最高频、最容易见效的写法之一。gap用于控制子元素之间的间距。再用justify-content控制水平方向的对齐,比如center居中、space-between两端对齐;用align-items控制垂直方向的对齐,比如center居中对齐。
初学者常犯的错是把 Flex 属性写在子元素上。记住,display: flex、justify-content、align-items都是写在父容器上的。子元素只需要通过flex: 1这类属性告诉浏览器“我要占据剩余空间”。
3.5 完整 CSS:让待办页面“活”起来
现在在todo-app文件夹下新建一个style.css,写入下面的代码:
/* 文件路径:todo-app/style.css */ * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; display: flex; align-items: center; justify-content: center; } .app { width: 480px; max-width: 90%; background: #ffffff; border-radius: 16px; padding: 24px; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2); } .app h1 { text-align: center; font-size: 24px; color: #333; margin-bottom: 16px; } .input-area { display: flex; gap: 8px; margin-bottom: 16px; } #todoInput { flex: 1; padding: 10px 14px; border: 1px solid #ddd; border-radius: 8px; font-size: 14px; outline: none; transition: border-color 0.2s; } #todoInput:focus { border-color: #667eea; } #addBtn { padding: 10px 18px; background-color: #667eea; color: #fff; border: none; border-radius: 8px; font-size: 14px; cursor: pointer; transition: background-color 0.2s; } #addBtn:hover { background-color: #5a6fd6; } #todoList { list-style: none; } .todo-item { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid #f0f0f0; } .todo-item:last-child { border-bottom: none; } .todo-item span { flex: 1; font-size: 15px; color: #444; } .todo-item input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; } .delete-btn { padding: 4px 10px; background-color: #ff6b6b; color: #fff; border: none; border-radius: 6px; font-size: 12px; cursor: pointer; } .delete-btn:hover { background-color: #e05555; } .todo-item.completed span { text-decoration: line-through; color: #aaa; }这段 CSS 并不是随便写的,每个块都在解决一个具体问题:
body上的display: flex+align-items: center+justify-content: center,实现整屏水平垂直居中。这个技巧是“页面居中”话题的标配答案;.input-area上的display: flex,让输入框和按钮排在同一行,输入框用flex: 1自动填满剩余宽度;.todo-item用display: flex排列复选框、文本和删除按钮;.todo-item.completed span用text-decoration: line-through实现删除线效果,这是后面 JS 联动的基础。
此时你在浏览器里打开index.html,页面已经不再是白底黑字,而是一个带渐变色背景、居中卡片、态度鲜明的现代化小应用了。
4. JavaScript:让页面拥有“生命”
4.1 为什么需要 JS
HTML 负责内容,CSS 负责样式,但它们都改变不了页面的行为。你不能在纯 HTML 里实现“点击按钮新增一行”,也不能在纯 CSS 里实现“输入内容后按下回车自动提交”。而 JS 的存在,就是为网页提供动态行为。
所谓“动态”,其实就两件事:响应用户操作、更新页面内容。
不过,JS 和传统编程语言不太一样的地方在于,它从诞生起就绑定在浏览器环境里。在浏览器里,JS 通过document对象来操作 DOM(Document Object Model,文档对象模型)。
理解 DOM 是理解前端交互的关键。浏览器在加载 HTML 后,会把标签解析成一棵倒挂的树,我们称它为“DOM 树”。每一个标签都变成了这棵树上的一只“节点对象”。JS 能通过操作这些节点,来改变网页上用户看到的任何一个元素——改文字、加样式、增节点、删节点。
简单说:HTML 是静态的买房合同,DOM 是那套能随时重新装修的房子,JS 就是你的装修队。没有 JS,房子再好也就那样;有了 JS,你才能按需改造。
4.2 变量、数据类型与常用语法
JavaScript 的语法面非常广,但一个“能写交互”的入门者,真正需要掌握的语法其实很少。
变量声明:可以使用let和const。const用于不会被重新赋值的变量,let用于会被重新赋值的变量。var是老写法,现在推荐尽量避免。
const appTitle = '我的待办清单'; let count = 0;数据类型:入门阶段你最需要关注的是字符串(string)、数字(number)、布尔值(boolean)、对象(object)、数组(array)。
函数:把一段逻辑封装起来。
function addTodo() { // 具体逻辑 }事件监听:JS 最核心的交互方式。
const btn = document.getElementById('addBtn'); btn.addEventListener('click', addTodo);模板字符串:字符串拼接的现代化写法。
const text = `当前有 ${count} 条待办`;4.3 获取 DOM 元素与操作属性
JS 操作 DOM,第一步永远是“找到要操作的元素”。最常用的三种 API 是:
| API | 返回值 | 使用场景 |
|---|---|---|
document.getElementById('id') | 单个元素 | 操作唯一元素 |
document.querySelector('选择器') | 第一个匹配元素 | 任意选择器 |
document.querySelectorAll('选择器') | NodeList(类似数组) | 操作一组元素 |
找到元素后,你能做什么?最常用的有三个维度:
- 改文本:
element.textContent = '新内容' - 改样式:
element.style.color = 'red' - 改类名:
element.classList.add('completed')
4.4 常用的 DOM 操作:创建、追加、删除
动态列表的基础操作,就是这三板斧:
// 创建元素 const li = document.createElement('li'); // 给元素添加类名和内容 li.classList.add('todo-item'); li.innerHTML = `<input type="checkbox" class="todo-checkbox"><span>${text}</span><button class="delete-btn">删除</button>`; // 追加到父元素中 document.getElementById('todoList').appendChild(li);这里有一个经验之谈:尽量使用textContent去设置用户输入的内容,不要直接拼 HTML。因为如果用户输入的是<img src=x onerror=alert(1)>这类字符串,直接拼到 HTML 里就触发 XSS 攻击。虽然我们这是个学习项目,但从第一天开始养成安全的习惯很重要。
4.5 完整的 JS 逻辑实现
现在在todo-app文件夹下新建script.js,写入下面的完整逻辑:
// 文件路径:todo-app/script.js const todoInput = document.getElementById('todoInput'); const addBtn = document.getElementById('addBtn'); const todoList = document.getElementById('todoList'); // 新增待办事项 function addTodo() { const text = todoInput.value.trim(); if (text === '') { alert('请输入待办内容'); return; } const li = document.createElement('li'); li.classList.add('todo-item'); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.classList.add('todo-checkbox'); const span = document.createElement('span'); span.textContent = text; const deleteBtn = document.createElement('button'); deleteBtn.textContent = '删除'; deleteBtn.classList.add('delete-btn'); li.appendChild(checkbox); li.appendChild(span); li.appendChild(deleteBtn); // 复选:切换完成状态 checkbox.addEventListener('change', function () { li.classList.toggle('completed'); }); // 删除:从列表中移除 deleteBtn.addEventListener('click', function () { todoList.removeChild(li); }); todoList.appendChild(li); todoInput.value = ''; todoInput.focus(); } // 回车提交:绑定 input 上的 keydown 事件 todoInput.addEventListener('keydown', function (event) { if (event.key === 'Enter') { addTodo(); } }); addBtn.addEventListener('click', addTodo);这段代码里有几个细节需要你品一下:
第一,trim()的作用。用户输入可能带前后空格,如果不处理,会出现“看起来没内容但确实添加了”的 bug。trim()会去掉字符串首尾的空白符。如果去掉后是空字符串,直接提示并返回,不创建任何节点。
第二,为什么用createElement而不是innerHTML。前面提到过安全问题。更重要的,createElement生成的是 DOM 对象,我们需要给它的子元素单独绑定事件。如果用整段字符串插入,后续绑定删除事件会比较绕。
第三,事件的绑定位置。注意,删除按钮和复选框的事件是在li被创建之后就立即绑定的。这也是很多教程里容易忽略的细节:如果你在 HTML 里写死了一个删除按钮,你只需要在全局绑定一次;但这里是动态创建的节点,所以每个新节点都需要自己的事件监听器。
第四,toggle方法。如果类名completed存在则移除,不存在则添加。这样复选框反复勾选时,删除线效果能正常切换,而不会被叠加出来。
5. 完整示例与代码实现:把三件套串起来
现在,三个文件已经全部就绪。整个项目的目录结构如下:
todo-app/ ├── index.html ├── style.css └── script.js各文件作用的对应关系可以用一张图表达:
index.html定义页面结构和初始内容;style.css负责视觉表现,包括居中、卡片、按钮样式、删除线等;script.js负责交互逻辑,包括输入、添加、勾选完成、删除。
这三个文件互相配合,构成了一个最简单也最完整的前端项目。这个“HTML 结构 + CSS 样式 + JS 交互”的协作模式,会一直沿用到你接触任何复杂前端项目。
5.1 如何运行与验证
在浏览器中运行有两种方式:
方式一:直接双击打开 index.html。这是最快的验证方式。双击后浏览器直接渲染页面,不需要任何服务器。适合学习阶段使用。
方式二:使用 VS Code 的 Live Server 插件。如果你用 VS Code,建议安装 Live Server 插件。安装后在index.html上右键选择 “Open with Live Server”,浏览器会自动打开http://127.0.0.1:5500并实时刷新。这个方式更接近真实开发体验,也方便调试。
建议直接用方式二,因为它能让你体会到“改代码、保存、浏览器立即更新”的开发节奏。
5.2 预期效果
完成之后,你应该能看到:
- 一个居中显示的白色卡片,卡片上方是标题“我的待办清单”;
- 输入框 + 按钮在同一行,输入框有圆角边框,按钮是蓝紫色;
- 列表里默认有两条待办;
- 在输入框里输入文字,点击“添加”或按下回车,新条目会以列表形式插入到最后;
- 勾选复选框后,对应文字出现删除线并变为灰色;
- 点击“删除”按钮,对应条目被移除。
如果你看到以上所有效果,说明你的 HTML、CSS、JS 已经形成了一个完整的闭环。
5.3 如果失败,怎么排查
最可能出问题的地方是文件路径。请仔细检查:
index.html中<link rel="stylesheet" href="style.css">的路径是否与style.css在同一个目录下;<script src="script.js">的路径是否正确;- 文件名的大小写是否一致,比如
style.css不要写成Style.css。
如果点击“添加”没反应,优先打开浏览器开发者工具(F12),切到 Console 标签页,查看是否有红色报错。最常见的报错是Cannot read properties of null,这种提示十有八九是getElementById的 id 名写错了,或者脚本执行时该元素还不存在于 DOM 中。
6. 常见问题与排查思路
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 页面打开后中文显示乱码 | HTML 文件缺少<meta charset="UTF-8"> | 查看<head>中是否声明字符编码 | 在<head>中添加<meta charset="UTF-8"> |
| CSS 样式完全没有生效 | link标签的路径错误或文件名不匹配 | 检查link标签的href与目录结构 | 修正路径并统一文件名大小写 |
| 输入框为空时点击添加也能添加 | 没有做空值校验 | 审查addTodo函数的trim()判断 | 使用text.trim()判断,为空时alert并return |
| 点击删除无效 | 动态生成的元素没有绑定删除事件 | 查看控制台报错和事件绑定代码 | 每个动态节点的删除按钮都要单独绑定事件,或用事件委托 |
| 页面在手机上显示过宽 | 缺少 viewport 配置 | 检查<head>中 meta 标签 | 添加<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 勾选复选框后删除线不出现 | completed类名与 CSS 不匹配 | 检查 JS 中classList.toggle和 CSS 中.completed span选择器 | 统一类名拼写,确保 CSS 写在style.css中 |
| 找不到元素报错 | JS 执行时机早于 DOM 渲染 | 打开 F12 查看 Console 报错位置 | 把<script>放在body底部,或使用DOMContentLoaded事件 |
7. 最佳实践与工程建议
项目虽然小,但一些“工程习惯”可以尽早建立起来。很多人觉得这些习惯是“以后进公司再说”的事,但实际上它们决定了一个人的代码能走多远。
7.1 命名规范
- HTML id 和 class 使用小驼峰或短横线命名。推荐短横线风格,如:
todo-input、todo-list。关键词之间用-分隔,可读性好。 - JS 变量和函数使用小驼峰命名。如:
todoInput、addTodo。常量大写,如:MAX_COUNT。 - CSS 类名做到“见名知义”。
.todo-item一眼就知道是待办事项条目,.red-bg这种描述样式而不是描述语义的类名尽量少用。
7.2 把逻辑拆成独立函数
建议把“添加事件”和“添加逻辑”拆开,而不是把所有代码都塞进一个click回调里。例如把addTodo定义成独立函数,而addBtn.addEventListener('click', addTodo)只负责事件绑定。这样后续如果要加表单校验、数据持久化,不会把同一个函数改得越来越长。
7.3 防止脚本加载顺序的坑
最简单也最稳妥的做法是把<script>标签放在<body>的末尾。因为浏览器解析 HTML 是按顺序从上往下执行的,脚本放在底部时可以确保它执行时,所有 DOM 元素都已经解析完成。如果你非要放在<head>里,也不是不行,但必须把操作 DOM 的代码包在DOMContentLoaded事件里:
document.addEventListener('DOMContentLoaded', function () { // 在这里执行 DOM 操作 });7.4 建议尽早尝试的扩展
只用script.js里的逻辑还不够,学到一个阶段后可以尝试:
- 把待办事项存入
localStorage,刷新页面后数据不丢失。这会顺便让你理解 JSON 的stringify和parse; - 支持“双击编辑”,把静态文本变成可编辑的输入框;
- 增加筛选功能,全部分类、已完成、未完成三个 Tab;
- 增加统计功能,在页面底部显示“剩余 X 项未完成”。
这些扩展并不难,但每一个都会逼着你回头复习三件套,尤其是 DOM 和事件。
7.5 关于学习路径的提醒
到这里,3 小时的内容基本就学完了。但想提醒一句:本文的学习重点是“建立整体认知”,不是“背语法大全”。学完后你应该具备的能力是:
- 看到一个设计稿,能大致画出它由哪些 HTML 块组成;
- 看懂网上 Demo 里的 CSS 在做什么,并能微调样式;
- 能独立给页面添加一个简单的交互功能;
- 遇到问题知道先看控制台、再查 MDN 文档。
这些能力一旦建立,后续无论学 Vue 还是 React,都会顺畅很多。因为它们本质上是在帮你更高效地操作 DOM、管理数据和维护状态,而底层依然是 HTML、CSS 和 JavaScript 这三件套。
8. 总结与下一步
这篇文章用了约 3 小时的阅读和动手时间,带你完成了从零到一的第一个完整前端小项目。此时你手里应该已经有了三个文件:index.html、style.css、script.js,以及一个能输入、能添加、能勾选、能删除的待办页面。
接下来你需要做一件事:不要急着进入新知识,先把这个项目反复“折腾”几遍。改改颜色,调整一下布局,给列表增加一个“全部清空”按钮,把输入框的 placeholder 换成自己的语言。折腾的过程中,你一定会踩到新的坑,而踩坑、查错、修复,才是真正的学习过程。
如果再往后走,推荐按这个顺序继续:
- 把待办数据存入
localStorage,理解前端持久化; - 学习更系统的 CSS 知识,重点掌握 Grid 布局、伪元素、过渡动画;
- 学习 ES6+ 常用语法(解构、模板字符串、箭头函数、模块化);
- 接触 Vue 或 React,用框架重构这个 Todo 项目。
前端入门并没有想象中那么困难,真正困难的是持续练习、在报错中积累经验,并始终保持“把页面做出来”的动力。希望这个 3 小时的小项目,能成为你前端路上第一个真正跑起来的起点。如果文章里有哪个环节还没看懂,建议回到对应章节,把代码再敲一遍——前端最怕“眼睛会了、手不会”,一定要让手先动起来。