
做前端这些年被问得最多的问题之一就是零基础想入门HTML第一个页面写什么比较合适。我的答案一直没变过——自我简介网页。原因很朴素它体量小、需求明确、结果肉眼可见而且几乎把HTML结构里最常用的那批标签都用上了。更关键的一点是这个页面写完你能立刻发给朋友看有正反馈学起来不容易半途而废。所以这篇我把当年自己练手、后来带新人时反复在用的那套自我简介网页结构完整拆开讲一遍从第一行doctype到最后一行footer每一块为什么这么写、能不能换个写法、换了会出什么问题都尽量说透。内容适合刚接触网页的朋友也适合写了几年但一直是div堆到底、想回头补一补语义化结构的人。1. 为什么我建议从自我简介网页入手练HTML结构1.1 一个页面就能覆盖大半个基础标签体系别看自我简介网页信息量小它的覆盖面其实一点不小。一个人的信息无非几类姓名、头像、一句话介绍、联系方式、教育或工作经历、技能清单、做过的项目。这几类信息拆到HTML标签上正好对应标题标签h1到h3、段落p、图片img、超链接a、无序列表ul加li、有序列表ol、描述列表dl加dt加dd再往下还能用上table做时间线、用section和article做分区、用footer放联系方式。也就是说认认真真写完一个自我介绍页等于把入门阶段该认识的标签一次性见了个遍。我带新人的时候有个习惯不让他们先去看那套又长又全的标签手册而是直接扔一个需求把你自己的信息组织成一个页面。人在写自己的东西时是有表达欲的遇到技能这块我想列成两栏项目想按时间倒序排这种具体诉求会主动去查该用什么标签记忆比死记硬背牢得多。等你把这个页面写顺了再去看那些社区主页、文档站首页的结构会发现骨架几乎是同一套。还有一层好处是文件体量小。整个页面纯HTML也就一百多行加上样式和脚本顶天两三百行浏览器打开瞬间出结果出错也好定位。比起一上来就啃一个几千行的项目模板挫败感低太多。1.2 结构先行的思路先搭骨架再谈样式很多新手的第一反应是我要先把这个页面弄好看于是一上来就写一堆颜色、圆角、阴影。结果样式越加越乱最后连自己都搞不清哪个div是干什么的。我的做法一向是先把结构写死再考虑视觉。具体讲就是把页面当成一份带层级的文档而不是一堆盒子。先问自己这块内容是页面的头部信息还是主体内容头部信息包括姓名、职位、一句话简介用header包起来主体内容是按经历、技能、项目分块用main包起来块与块之间用section分隔最后的联系方式用footer收尾。层级理清楚了再去看每一块内部标题用h几、正文用p还是li基本就是顺着往下填。这个顺序的好处在于即使你完全不加样式浏览器默认渲染出来的页面也是能读的标题是标题的粗细列表是列表的样子段落之间有间距。结构对了样式只是锦上添花结构错了样式再怎么堆都是在给烂地基刷墙。提示写完结构后先别急着写CSS把浏览器默认样式下的页面截个图。这张图能直接看出你的层级关系对不对也能看出有没有把该用标题的地方错误地用成了加粗段落。1.3 三种常见写法的对比div堆砌、语义化标签、表格布局我刚学那会儿网上还流传用table布局整页的做法现在回头看当然是老黄历了但这个对比对理解结构很有帮助。下面这张表是我总结的三种写法在自我简介网页上的实际差别。写法类型典型结构优点问题适合场景纯div堆砌全部用div加class样式控制自由无层级语义屏幕阅读器读不出结构后期维护靠猜class名复杂组件内部布局语义化标签header/main/section/footer/p/ul结构自解释利于无障碍与检索需要多想一步这块是什么内容型页面首选表格布局整页包在table里早期兼容性好语义完全错位响应式极难处理真正需要行列数据时这三种我在不同阶段都用过。现在的默认选择是第二种但在某些具体场景下第一种依然合理——比如一个卡片内部为了做视觉对齐那层纯粹是布局容器用div反而比硬塞一个section更诚实。关键判断标准是这块内容本身是不是一个独立的语义单元。是就用语义标签不是纯粹为了排版用div。至于表格布局整页只在一个地方还能看到它的合理性——就是真的在展示行列数据比如技能名称 / 熟练程度 / 使用年限这种结构。这时候用table不是妥协是正确选择。2. 文档骨架从doctype到body的每一行在干什么2.1 第一行doctype不是标签但它决定了后续一切很多教程会一笔带过地说第一行照抄就行然后新手就真的照抄抄了好几年也不知道那行在干什么。我说清楚一点!doctype html不是一个HTML标签它是给浏览器的一句声明意思是请用标准模式来渲染我这个页面。那不用标准模式会怎样浏览器会进入一种叫怪异模式的兼容状态此时它对盒模型的宽高计算、行内元素垂直对齐这些细节的处理和现代标准是不一样的。你照着教程写的两栏布局在别人电脑上莫名错位很可能就是这一行漏了或者写错了。以前那串长得吓人的!DOCTYPE HTML PUBLIC -//W3C//DTD HTML 4.01//EN ...就是旧标准的写法现在统一用这个简短版本大小写不敏感但习惯上写成小写。这条声明必须放在所有内容之前连注释都不能在它前面。我踩过的坑是某个编辑器模板会自动在文件头插一段版权注释结果doctype被挤到第二行页面在某些情况下渲染异常查了半天才发现是这个原因。2.2 html标签与langzh-cn到底影响了什么html是整个文档的根元素所有内容都在它里面这一点没什么争议。容易被忽略的是它上面的langzh-cn。这个属性和视觉效果没有任何关系但它影响三件事。第一屏幕阅读器会据此选择合适的语音引擎读中文和读英文的发音规则完全不同不标的话读出来会很怪。第二浏览器在决定用哪种字体渲染、什么时候触发中日韩标点换行规则时会参考这个值。第三页面翻译类功能靠它判断原文语言比如判断该不该弹是否翻译成英文的提示。zh-cn表示简体中文。如果你写的是繁体内容习惯上用zh-hant或者zh-tw。这个属性写错了不会报错页面照样显示所以很多项目里它是错的也没人发现。我自己的检查习惯是新建文件时第一件事就把lang写上别等到最后补。2.3 head里的三件套charset、viewport、titlehead里可以塞很多东西但对一个自我简介网页来说下面三个是必须有的。meta charsetutf-8决定了浏览器用什么编码去解析文件。写utf-8能覆盖绝大多数场景包括中文、日文、各种符号。这个标签必须放在head里尽可能靠前的位置一般紧跟在head开始标签之后。原因是浏览器在读到它之前如果已经拿到了一段字节流可能会先用默认编码猜一次放得越靠前猜错的机会越小。乱码问题的九成都出在这里或者文件本身的实际保存编码上两个要一致。meta nameviewport contentwidthdevice-width, initial-scale1是针对移动端的。不写这一行手机浏览器会默认按一个虚拟的宽屏常见是980像素左右来渲染页面然后整体缩小给你看结果就是字小得要点两下放大。写上去之后页面宽度就等于设备实际宽度布局才是你预期的样子。initial-scale1表示初始不缩放一般和widthdevice-width配合使用。title是标签页上显示的文字也是收藏夹里的名字还是搜索结果的标题。新手最常犯的错误是写个我的网页或者干脆不写。写成张明的个人主页 - 前端方向这种信息量立刻不一样。2.4 一份可以直接抄走的head模板把上面说的几块拼起来就是下面这个样子。我这几年的个人小页面基本都从这个模板改。!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 title张明 - 个人简介/title meta namedescription content前端方向从业者主要做界面结构与交互实现。 meta nameauthor content张明 link relstylesheet hrefstyle.css /head body !-- 页面内容 -- /body /html多说一句description和author这两个meta。它们不影响页面显示但description的内容有可能被搜索结果引用author是给文档留个署名信息。写不写都行但既然成本是一行字顺手写上没坏处。link relstylesheet那行是引入外部样式表下面第4章会细说样式该放哪。3. 页面主体的语义化结构拆解3.1 header区名字、职位、一句话介绍header这块要解决的是你是谁。最小的写法是h1加一行p。header h1张明/h1 p前端方向 · 擅长把设计稿变成能跑的界面/p img srcavatar.jpg alt张明的头像 width120 height120 /header这里有几个细节值得说。h1在一个页面里原则上只用一次代表这个页面的主标题自我简介页里最合适的就是你的名字。别为了好看把名字写成h3然后用CSS调大字号——那是拿样式去掩盖结构问题。头像那个img标签alt属性一定要写它不只是给图片加载失败时显示的文字也是屏幕阅读器读出来的描述。width和height写上可以在图片还没加载完时先把位置占住避免页面内容跳动。注意alt不是可有可无的装饰属性。纯装饰性图片可以写alt空字符串表示这张图没有信息量跳过但绝不能直接省略alt省略会让辅助工具去猜甚至读出文件名。一句话介绍我建议手写别用本人性格开朗、乐于助人这种模板句。写你做过的具体事情比如给公司内网做过一个数据看板看的人立刻有画面。3.2 main区经历、技能、项目怎么分区main表示这个页面的核心内容一个页面理论上只有一个main且它不应该包含那些在每个页面都重复的部分比如全站导航、页脚版权。这一点很多人会搞错把导航也塞进main里。main内部按内容主题切section每个section自己带一个h2标题。main section h2工作经历/h2 ul li h3某公司 · 前端开发/h3 p2021年至今负责后台管理系统的界面结构与组件维护。/p /li li h3某工作室 · 实习/h3 p2020年到2021年参与活动页面的静态页面制作。/p /li /ul /section section h2技能清单/h2 dl dtHTML/dt dd能独立完成语义化结构熟悉表单与无障碍基础属性。/dd dtCSS/dt dd熟悉常用布局方式能处理常见的移动端适配问题。/dd /dl /section /main这里我故意用了两种不同的标签经历用无序列表因为每条经历是并列关系没有先后顺序的含义技能用描述列表因为技能名和说明之间是术语—解释的对应关系dl正好表达这个。标题层级也要留意h1是名字所以section的标题从h2开始section内部的小标题用h3。跳级比如h1直接到h3在视觉上可能看不出问题但会让依赖层级导航的工具读起来很混乱。3.3 列表、表格、段落到底该选哪个这是新手最纠结的部分我给一个判断链条按顺序问自己就行。第一步这些内容是不是并列的条目是用列表。列表内部条目之间如果有明确的先后比如第一步、第二步用ol没有顺序含义的用ul。第二步这些内容是不是名称—描述的配对是用dl。第三步这些内容是不是二维的行列数据比如年份 / 项目 / 角色是用table表头用thead包th内容用tbody包td。第四步以上都不是就是一段叙述文字用p。自我简介页里最典型的需要table的地方是做一条时间线table thead tr th scopecol时间/th th scopecol项目/th th scopecol我的角色/th /tr /thead tbody tr td2023/td td数据看板改版/td td结构与样式实现/td /tr tr td2022/td td活动落地页/td td整页制作/td /tr /tbody /tablescopecol这个属性是告诉辅助工具这个表头管的是下面这一列不是必填但加了没坏处。3.4 footer区联系方式与版权footer放的是页面收尾信息。对个人简介页来说通常是联系方式、社交账号链接、版权说明。footer h2联系我/h2 ul li邮箱a hrefmailto:zhangmingexample.comzhangmingexample.com/a/li li主页a hrefhttps://example.com target_blank relnoopener个人作品集/a/li /ul psmall本页内容为个人介绍转载请注明出处。/small/p /footer邮箱链接用mailto:前缀点击后会直接调起邮件的撰写界面比让用户手动复制粘贴友好。外部链接加target_blank会在新标签页打开配合relnoopener是个稳妥习惯——不加的话新打开的页面在某些情况下能通过脚本访问原页面的引用属于可以避免的小隐患。底部那行版权我用small标签包了一下它的语义是附加说明、小字和视觉上的字小一点正好对上比用p加个class再调字号要正经。4. 完整的自我简介网页代码实操4.1 完整结构代码可以直接拿去改把前面几块拼起来就是一份完整的页面骨架。我在关键位置都加了注释方便你对照着改。!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 title张明 - 个人简介/title meta namedescription content前端方向的个人简介页面。 link relstylesheet hrefstyle.css /head body header h1张明/h1 p前端方向 · 把设计稿变成能跑的界面/p img srcavatar.jpg alt张明的头像 width120 height120 nav a href#exp经历/a a href#skill技能/a a href#contact联系/a /nav /header main section idexp h2工作经历/h2 ul li h3某公司 · 前端开发/h3 p2021年至今负责后台管理系统的界面结构与组件维护。/p /li li h3某工作室 · 实习/h3 p2020年到2021年参与活动页面的静态页面制作。/p /li /ul /section section idskill h2技能清单/h2 dl dtHTML/dt dd独立完成语义化结构熟悉表单与无障碍基础属性。/dd dtCSS/dt dd熟悉常用布局方式能处理常见移动端适配问题。/dd dtJavaScript/dt dd能做基础交互例如页内平滑跳转、回到顶部。/dd /dl /section section h2项目时间线/h2 table thead tr th scopecol时间/th th scopecol项目/th th scopecol我的角色/th /tr /thead tbody tr td2023/td td数据看板改版/td td结构与样式实现/td /tr tr td2022/td td活动落地页/td td整页制作/td /tr /tbody /table /section /main footer idcontact h2联系我/h2 ul li邮箱a hrefmailto:zhangmingexample.comzhangmingexample.com/a/li li作品集a hrefhttps://example.com target_blank relnoopenerexample.com/a/li /ul psmall本页为个人介绍内容仅供参考。/small/p /footer script srcmain.js/script /body /html有几个地方我特意做了示范。header里放了个nav做页内锚点跳转href的值对应各个section的id这是最省力的导航实现方式不用写任何脚本就能点。script标签放在body最后原因在4.3节细说。4.2 CSS放哪里外链、内联、行内的取舍样式有三种放法各有各的适用场合我按推荐顺序排一下。外部样式表是首选。在head里用link relstylesheet hrefstyle.css引入一个单独的文件。它的好处是这个文件会被浏览器缓存页面多了以后不用重复下载改样式也只改一个地方。个人简介页虽然只有一个页面我依然推荐外链因为这是养成好习惯的起点。head里的style标签适合临时调试或者单页小项目。写起来方便但页面结构和样式混在一个文件里行数一多就难找。行内style属性也就是直接写在标签上的stylecolor:red我的建议是尽量别用。它优先级高、不好覆盖还会让结构代码变得又长又乱。唯一合适的场景是那种由脚本动态计算出来的、每秒钟都在变的值。/* style.css */ body { font-family: system-ui, -apple-system, Segoe UI, sans-serif; line-height: 1.7; max-width: 720px; margin: 0 auto; padding: 24px 16px; color: #222; } h1 { font-size: 1.8rem; margin-bottom: 4px; } h2 { font-size: 1.25rem; margin-top: 36px; border-bottom: 1px solid #ddd; padding-bottom: 6px; } h3 { font-size: 1rem; margin-bottom: 4px; } img { border-radius: 50%; display: block; } nav a { margin-right: 12px; }那段font-family里的system-ui是个省事的写法让页面直接用系统自带的界面字体中文环境下会落到系统默认的中文字体上比自己硬指定一款字体要稳。max-width加margin auto这个组合是经典的居中方式比用定位写要简单得多。4.3 JS什么时候需要什么时候不需要自我简介页本质上是个内容页绝大部分需求用HTML加CSS就够了。真正需要脚本的场景我数了数大概只有三类。第一类是回到顶部。页面长了之后用户滚到底部想回去有个按钮会舒服很多。实现思路是监听滚动距离超过一定高度就把按钮显示出来点击时让页面平滑滚回顶部。核心就两行用window.scrollY判断距离用window.scrollTo({top: 0, behavior: smooth})做平滑滚动。这里behavior: smooth是关键参数不加它就是瞬间跳回去体验差不少。第二类是导航高亮。滚到某个section时上方对应的导航项变色。这个用现代浏览器提供的观察接口来做会比监听滚动事件干净代码十来行。第三类是数据驱动的部分。比如你的项目列表想从一份数据文件里读出来渲染而不是写死在HTML里。这种场景在个人页上不多见真需要的时候再上脚本也不迟。对应的取舍原则是能用HTML和CSS表达的就不要用JavaScript。锚点跳转能用href解决的别写scrollTo纯展示的折叠内容能用details标签解决的别写点击展开的逻辑。脚本引入的方式上我在页面底部用script srcmain.js/script好处是脚本执行时DOM已经解析完毕不需要再包一层等页面加载完成的回调。4.4 几个容易被忽略但很实用的属性细节第一锚点跳转的平滑效果现在可以纯CSS实现。给html标签加一句scroll-behavior: smooth;所有页内锚点跳转都会变成平滑滚动连JS都不用写。这是我这几年最喜欢的一个小改动一行顶十几行。第二链接的relnoopener前面提过值得再说一遍。凡是target_blank的外部链接加上它。第三表单类的联系方式。如果你想让访客直接留言可以放一个简单的表单注意input都要有关联的label。form action/message methodpost label forname你的称呼/label input idname namename typetext required label formsg留言内容/label textarea idmsg namemsg rows4 required/textarea button typesubmit提交/button /formlabel里的for要等于input的id点击文字就能聚焦到输入框这个细节对手机用户尤其友好。required是浏览器自带的必填校验省得自己写脚本。5. 常见问题与排查技巧实录5.1 中文乱码八成是这两个原因乱码是新手遇到的第一个拦路虎我把它拆成两种情况。第一种meta charset写了utf-8但文件本身是按别的编码保存的。比如在某个编辑器里默认保存成了GBK浏览器却按utf-8解析中文就变成方块或问号。解决办法是在编辑器的保存设置里确认编码是UTF-8注意有些编辑器区分UTF-8和UTF-8 with BOM网页文件建议用不带BOM的那个。第二种文件编码是对的但meta标签位置太靠后或者干脆忘了写。浏览器在没拿到编码声明之前可能已经按默认编码解了前面一小段。所以这个标签要放在head的最前面。排查方法很直接右键查看页面源码如果源码里中文显示正常但页面上是乱码说明文件编码没问题是声明的问题如果源码里就已经是乱码那就是文件本身的编码存错了。5.2 标签没闭合浏览器为什么不报错HTML和编程语言不太一样它对错误的容忍度非常高。你漏掉一个/p浏览器会自动帮你补上页面看起来一切正常。这种宽容是好事也是坑。好事在于新手不会因为一个笔误就白屏。坑在于错误被掩盖了等到页面变复杂某天突然布局全乱排查起来毫无头绪。我的建议是养成两个习惯。一是写完一段就缩进对齐标签成对出现开一个立刻补上闭合的。二是把浏览器的开发者工具用起来按F12打开看元素面板里的实际结构——浏览器解析后的真实结构和你写的文件可能有差别差别的地方往往就是问题所在。一个高频错误是块级元素嵌套不当比如把div直接放在ul里面合法位置是li里或者p里面套p浏览器会自动断开前一个p结构就和你预期的不一样了。这类问题在元素面板里一眼就能看出来。5.3 移动端显示不对先检查这三处手机上打开自己写的页面最常出现的两个症状是字太小要放大和有横向滚动条。前者基本是漏了viewport那行meta。后者通常是某个元素宽度超出了屏幕。超出屏幕的元素排查有个小技巧在开发者工具里给所有元素临时加一句outline: 1px solid red;然后一层层找哪个红框超出了边界。常见元凶是固定宽度的图片、加了padding但没做宽度修正的块、以及表格内容太宽。图片可以加max-width: 100%;表格可以套一层可横向滚动的容器。5.4 常见问题速查表下面这张表是我这些年被问得最多的几个问题直接按症状找原因。症状最可能的原因处理方式中文显示为乱码文件编码与charset声明不一致或meta位置靠后统一保存为UTF-8meta charset放head首位手机上看字很小漏写viewport meta补上widthdevice-width, initial-scale1页面出现横向滚动条某元素宽度超出视口图片加max-width:100%定位元素检查偏移量图片位置在加载时跳动img没写宽高补上width和height属性点击锚点跳转很生硬没有平滑滚动设置html加scroll-behavior: smooth新标签页打开有隐患外部链接缺rel加relnoopener标题层级被工具读乱跳级用h标签从h1顺延不跳级样式改了没生效缓存或选择器优先级强制刷新检查选择器权重提示这个表建议打印出来贴在手边。新手阶段八成的报错都在这八条里遇到问题先扫一遍表比漫无目的地搜要快得多。我在实际带人的过程中发现最容易反复踩的其实是最后一条——样式改了没生效。十次里有七次是浏览器缓存了旧的CSS文件强制刷新Windows上是CtrlF5就好剩下三次是选择器优先级问题比如想覆盖一个行内样式却发现怎么写都不生效。这种情况下与其一路加!important不如回头把样式的位置理顺。