做前端这几年,我养成了一个职业病:看到一个行业标杆站,第一反应不是看内容,而是按 F12 把样式表翻个底朝天。京东、天猫、王者荣耀这三个 PC 端站点,算是让我反复琢磨过好几轮的样本。它们的 CSS 初始化文件放到一起看,套路并不复杂,甚至你会觉得"这不就是 reset 吗"。但真正有价值的是,每一行初始化背后,都是团队跟浏览器默认行为、兼容性历史、工程维护性扯出来的账本。这篇文章不打算空谈理论,我把自己复盘到的源码细节、工程取舍和实战踩坑一次性讲透。如果你是刚入门前端,会知道这些大站到底在"洗"什么;如果你是在搭团队规范,可以直接把后面的模板和思路拿走。
1. 先搞清楚:CSS 初始化到底在“洗”什么
1.1 浏览器默认样式真实存在,而且每个都不一样
很多人写页面久了会忽略一个事实:你什么都不写,浏览器也会给 HTML 元素一套默认样式。段落有 margin,标题有加粗和不同的字号,ul 有缩进和 project, button 有内边距和边框,input 有默认的边框和背景——这套东西叫 user agent stylesheet。问题在于,每个浏览器、每个版本的默认值都不完全一样。
举个最常见的例子:Chrome 里一个普通的 button,内边距是 6px 左右,四周边框是 2px outset;但在老版本的某些国产浏览器的兼容模式下,按钮的内边距可能完全变样。再比如 body 的 margin,IE 默认 8px,大部分现代浏览器也是 8px,可只要触发怪异模式,这个值就说不准了。还有 dl、dd、figure 这类元素,不同 UA 给的 margin 差异更大。你如果不做初始化,写出来的页面在不同设备上会有肉眼可见的间距差,这还没算行高和字体渲染差异。
CSS 初始化干的事情,就是把这一堆"浏览器出厂自带"的样式抹平,让页面从同一起跑线开始渲染。它解决的三个核心问题分别是:跨浏览器一致性、盒子模型统一、以及默认间距对设计稿还原的污染。
1.2 一家公司为什么要把初始化当成前端规范的一部分
单个开发者写 reset,是为了省事;一个团队把初始化代码固定下来,是因为它直接决定了后续所有组件、基础样式、甚至 UI 规范的稳定性。复盘京东、天猫、王者荣耀线上源码的时候你会发现,它们页面里的初始化部分并不是今天才加的,而是经过了多年迭代沉淀出来的固定资产。
这里有个很容易被忽略的点:初始化不是越干净越好。真正上线的大项目,reset 的目标不是消灭所有样式,而是把手动控制权兜住,同时保留那些"反而不想动"的默认行为。比如你清掉所有 h1~h6 的字号和加粗,后面每个页面都要手动补一层标题样式,这反而提高了出错率。所以大站初始化文件往往分成两层:第一层是全局抹平,第二层是基础元素降级重置。这两层拆开来看,才是理解这三个站点初始化源码的正确姿势。
2. 京东、天猫、王者荣耀三个 PC 站的源码复盘
2.1 京东 PC 端:老成稳重的电商风格
京东 PC 端的初始化源码,整体给人的感觉就是"务实"。它没有特别激进的通配符清零,而是一部分走元素选择器、一部分走类名追加。核心部分基本是:html, body, div, p, ul, li, dl, dt, dd这类块级盒子的 margin 和 padding 归零,ul, ol把 list-style 去掉,a去掉下划线,img去掉边框并且把默认的垂直对齐方式改成 top。
有个细节值得单独说:它给body设置了明确的font-size和行高,而不像很多现代框架那样把字号留到根元素上统一处理。京东这套初始化里的字体栈看起来像是"中文优先、英文兜底",同时把\5b8b\4f53这类转义符写在后面,很典型的老电商写法。这么做的好处是页面在未完全加载 CSS 时,文本渲染就已经是接近设计稿的状态,减少 FOUC(样式闪烁)。
2.2 天猫 PC 端:同类基础上做减法,重表达层
天猫 PC 端的初始化跟京东属于同一条技术血脉,但它在细节上更克制。比如它不会把所有标签都塞进通配符选择器,而是选择性地去重置,尤其对 form 系列控件、表格单元格做了更细致的处理。它保留了部分元素的默认 display 属性,因为一旦你手动把table或td的 display 重置成 block,表格布局就彻底废了。
天猫这边更关注的是"表达层"。它的初始化里能看到对line-height、color、font-family的统一定义,这三样东西放在初始化文件里,本质上是给整个品牌站定了一个文本基调。即使你后来加载了成百上千行业务样式,只要没有显式覆盖,所有页面文字都会回到这个基调上,视觉是统一的。
2.3 王者荣耀 PC 端:游戏站点更彻底的“清场”
游戏官网的初始化思路跟电商不太一样。电商页面组件多、密度高,初始化必须谨慎,怕误伤布局;而游戏 PC 站大量使用全屏视觉、沉浸式背景和自定义组件,它需要"清场",把浏览器那套默认行为尽可能压到最低。
王者荣耀 PC 站点的初始化源码里,通配符选择器出现的频率明显更高,* { margin: 0; padding: 0; }这类做法在电商老站里已经很少用了,但在游戏站还是能看到。它还把box-sizing: border-box全局铺开,这样后续所有切图和间距计算都采用内减模式,开发时大脑负担少很多。另外对input、textarea、button的默认边框、背景、字体处理也做得比较狠,甚至把select的下拉箭头都手动重置了一遍,因为游戏页面经常要做自定义下拉框。
2.4 三站初始化代码的对照明细
我复盘时简单整理了一张对照表,列一下它们各自最典型的处理方向,方便你直观感受差异。
| 对比项 | 京东 PC 端 | 天猫 PC 端 | 王者荣耀 PC 端 |
|---|---|---|---|
| 初始化的载体 | 独立 reset 文件,按需拆分 | 基础 reset 与全局变量混合 | 通配符重置 + 现代 CSS 变量 |
| 通配符使用 | 少用,靠元素选择器逐个列 | 基本不用通配符 | 明显使用通配符清场 |
| box-sizing | 部分模块单独设置 | 全局统一 border-box | 全局统一 border-box |
| 字体栈 | 中英字体栈,老牌写法 | 中英字体栈,偏精确控制 | 文艺感字体栈,配合品牌视觉 |
| 表单控件 | 重置 border、padding | 重置 font-family、outline | 彻底清样式再自定义 |
| 浮动清除 | 单独提供 clearfix 类 | 单独提供 clearfix 类 | 采用 flex 布局较少依赖清除浮动 |
这个表格不是源码逐字的百分百还原,毕竟线上页面不同版本都有微调,但方向上就是这么三派。看完你基本能得出结论:初始化没有绝对标准,它和你的交互复杂度、视觉诉求、历史包袱牢牢绑定。
3. 初始化代码每一行的工程原理
3.1 用通配选择器还是逐元素设置
* { margin: 0; padding: 0; }是最常见的 reset 写法,优点是一行顶十行,缺点也很明显:通配选择器的匹配成本高,尤其在电商这种动辄几千个 DOM 节点的页面上,性能损耗会被放大。京东、天猫 PC 站点早期页面 DOM 规模很大,所以它们的初始化选择器基本都是"点名式"的,直接把常见标签列出来,而不是扫全部元素。
这里补充一个容易忽略的知识点:通配选择器的性能问题在今天的普通页面上几乎可以忽略,但在大型 PC 端电商页面里,光是一个商品列表就可能几千个节点,全局通配会造成不必要的样式匹配开销。而且*重置还会把html、body本身就合理的外边距一起抹掉,后续还得补回来,属于多此一举。所以团队规范里,我通常建议:能用元素选择器就别用*,除非你确定页面规模很小。
3.2 box-sizing:border-box 的演进
如果让我选整个初始化里最值钱的一条,那一定是box-sizing: border-box。没有它,你在写一个宽度占 100% 的分栏布局时,再塞 padding 和 border,实际渲染宽度会超过父容器,然后被迫用calc()去减,减一两次还好,减多了绝对崩溃。
京东、天猫 和王者荣耀 PC 站在这一项上的处理略有不同,京东很多老页面还是局部设置,模块内部约定用content-box计算,因为历史业务样式太多,动全局会引发大面积位移。天猫和新版王者荣耀官网就激进多了,直接在根元素写:
*, *::before, *::after { box-sizing: border-box; }一旦全局 border-box,就可放心地写width: 25%,再往里加 padding 也不会把最后一个盒子挤到下一行。这是自从 IE8 支持该属性之后,前端领域最值得写进初始化的一行代码。
3.3 字体栈与中文场景的行高
大站的初始化里,字体栈从来不是随便抄的。京东、天猫 PC 端常见的字体栈大概长这样:
body { font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans CJK SC', sans-serif; }这里面的门道比你想象得多。-apple-system优先是为了吃满苹果设备的系统字体渲染,BlinkMacSystemFont给 Chrome 用,然后一路退回 Windows 的雅黑和 Linux 的文泉驿。把中文字体放在后面,是因为中文字体渲染成本高,如果能让系统优先用英文字体渲染英文和数字,会明显减少页面在滚动和动画时的不流畅感。
行高也是初始化里的重头戏。老电商站喜欢在 body 上写line-height: 1.5或1.5em,因为那时屏幕小、字号小,1.5 倍行高能保证长文本阅读舒适。游戏站则常把 body 行高设为 1.2、1.3,为了视觉更紧凑。行高的单位也有讲究,写在 body 上用无单位数字最稳,它能被子元素继承后按各自字号重新计算,不至于出现 em 单位那种连锁放大问题。
3.4 表单、列表、图片样式处理细节
表单控件如果不重置,几乎是前端最常见的一个"丑点"。同一个 input,在 Chrome 和 Safari 下高度不同,padding 不同,focus 时的 outline 也可能不一致。因此初始化文件里通常会有类似这样的内容:
input, textarea, select, button { font-family: inherit; font-size: 100%; margin: 0; vertical-align: baseline; } button, input[type='button'], input[type='submit'] { cursor: pointer; }其中font-family: inherit这行特别重要。浏览器的button默认不会继承 body 的字体设置,如果你不显式继承,按钮上的中文会突然换成另一种字体,看起来很突兀。font-size: 100%则是让按钮和输入框的字号跟随上下文,不至于因为表单控件默认 13.3px 而跟周围文字对不上。
列表样式、图片边框这种常规操作我就不细说了,但img { vertical-align: middle }或top这一条值得展开。图片默认的 vertical-align 是 baseline,它在文字行里会导致图片下方多出几像素的间隙,这是很多人图片底部对不齐的根源。初始化时统一成 middle 或 top,可以瞬间减少大量幽灵间隙。
3.5 兼容旧浏览器时的取舍
现在讨论初始化,很容易忘了当年还有一批"老顽固"。京东、天猫之所以保留了大量逐元素重置,而不完全倒向现代 reset 方案,是因为它们还要兼容一些老旧内核的浏览器。如果不兼容那些环境,很多事情都可以做减法;一旦要兼容,像display: inline-block在 IE8 下只对 inline 元素生效这类问题,就要靠初始化的 hack 去兜。
复盘王者荣耀 PC 端的时候你会发现,它的初始化对兼容层压力小很多,因为它主要面向现代浏览器,可以大胆用box-sizing、CSS 变量、rem 布局。这背后的逻辑是:初始化策略不是纯技术问题,而是由用户群的浏览器分布决定的。如果你们产品的后台用户还大量停留在老版本浏览器,就别照搬游戏站那种激进 reset,不然上线后会有排不完的兼容性 bug。
4. 给你一份可直接落地的初始化方案
4.1 可复制的 PC 端初始化模板
下面是结合三个站点里最好的处理方式,整理出来的一份可以抄走的 PC 端初始化模板。它兼顾了性能、兼容和日常开发体验,不会像老式 reset 那样把所有样式都清空,也不会像纯 normalize 那样留着太多默认值让你猜不透。
/* 基础盒模型与边距清空 */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } /* 文档级盒模型统一 */ *, *::before, *::after { box-sizing: border-box; } /* 文本与页面基色 */ body { line-height: 1.5; color: #222; background-color: #fff; -webkit-font-smoothing: antialiased; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, 'Microsoft YaHei', 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans CJK SC', sans-serif; } /* 列表与链接降级 */ ul, ol { list-style: none; } a { text-decoration: none; color: inherit; } a:hover, a:focus { text-decoration: underline; } /* 图片与媒体 */ img, picture, video, canvas, svg { display: block; max-width: 100%; } img { vertical-align: middle; border: 0; } /* 表单统一 */ button, input, select, textarea { margin: 0; font-family: inherit; font-size: 100%; line-height: inherit; color: inherit; background: transparent; border: none; outline: none; } button { cursor: pointer; } /* 表格 */ table { border-collapse: collapse; border-spacing: 0; } /* 清除浮动工具类 */ .clearfix::after { content: ""; display: table; clear: both; }这份模板可以直接作为reset.css或base.css放到项目里。没有为它加太多花哨的动画、变量,原因是初始化文件越安静,后续维护成本越低。
4.2 不同项目类型的初始化调整要点
不同场景用这份模板时,要按项目类型做三处调整。
第一处是字体栈。如果你是电商、社区、资讯站,中英文混排频率很高,字体栈就按模板里这种"英文先行、中文兜底"的顺序写,并把font-size设置在html上用 rem 体系。如果你是游戏、品牌展示站,字体栈可以更聚焦在品牌字上,允许部分文字使用指定字体,但初始化主体还是建议保留通用字体。
第二处是默认颜色。模板里 body 的color: #222适合中性内容平台;电商站更多用#333;偏年轻化的品牌站会用#1f1f1f这类高级黑。颜色放初始化里不代表不能覆盖,但它能保证在没有业务样式时页面也有一个稳定的文本基调。
第三处是是否需要兼容旧浏览器。如果你们还要兼容 IE8、IE9,box-sizing: border-box的全局写法要改成逐元素设置,通配选择器和 HTML5 标签重置也要拆开,甚至要去引入一个专门做重置的兼容包。如果不需要兼容旧内核,那上面的模板直接用,完全没问题。
4.3 初始化文件的组织方式与加载顺序
初始化文件写好后,加载顺序很关键。现代构建工具里,样式会最终打包进一个文件,但开发环境下通常拆成多个:reset.css、variables.css、common.css、components.css。初始化一定要排最前面,其次是设计变量和工具类,最后才是业务组件样式。
为什么要强调顺序?因为 CSS 的层叠机制里,后加载的同优先级规则会覆盖先加载的。如果初始化放后面,等于把别人组件的样式又踩了一脚,这是绝对要避免的。另外,线上环境为了性能会把 CSS 都合并压缩,这时初始化代码的注释和空行会被压缩掉,顺序仍然保持原样,层级关系不会被破坏。
5. 我实际踩过的几个坑
5.1 重置按钮后,垂直居中不好使
某次做一个营销页时,我直接把按钮的padding和border全部清零,结果按钮文字总是不居中,往下偏一两像素。原因很细:button默认的line-height跟普通文本不一样,而且不同浏览器对button内部的文本基线处理有差异。后来我在初始化里加了一行button { line-height: normal; },再统一设置 padding,问题立刻消失。这里提醒一下,清掉按钮默认样式后,一定要手动把它的行高、高度、padding 这三个属性一起定义,只清不补必踩坑。
5.2 全局通配重置带来的无语事件
有一次我为了省事,在某个后台项目用了* { margin: 0; padding: 0; },结果在渲染一个表格组件时,表格单元格之间的间距全部消失,视觉上变成挤成一团。查了半天才发现,table的默认border-spacing也被通配选择器重置掉了。这个案例说明:通配符重置看似一行秒杀,但它会连那些"你觉得不该动"的属性一起清掉。表格的border-collapse、border-spacing,还有input的size属性、select的下拉样式,都是高风险区。这也是大站宁愿写一长串元素选择器,也不用通配符的根本原因之一。
5.3 font-family 覆盖不彻底导致图标字体失效
做某个游戏门户站的时候,我用到了图标字体,但页面里的某些<i>标签怎么都不显示图标,只显示一个方框。排查到最后发现,项目里自动生成的某些组件样式给i元素单独指定了font-family: sans-serif,覆盖了图标字体的声明。从那个项目以后,我把初始化里的图标基础类写清楚了,专门给图标预留一个font-family,并且尽量防范全局字体设置污染到图标渲染。
5.4 rem 没生效,是因为 body 和 html 在打架
另一个经典场景是:html 上设了 100px 基准字号,body 又单独设了 12px,结果 rem 单位在部分老旧继承下表现混乱。特别是在京东、天猫这类代码层级很深的老项目里,业务样式可能在某个业务组件里重新定义了html的字号,导致设置 rem 的值突然变化。你现在写初始化模板,建议把font-size只放在html上:
html { font-size: 16px; }业务上要适配不同屏幕,可以通过等比例缩放根字号来控制全局 rem。不要既在html又在body上同时设置font-size,这等于埋了一个随时会被触发的地雷。
6. 收个尾巴:从这三个站学到最值钱的一件事
反复复盘京东、天猫、王者荣耀 PC 端的初始化源码,我最深的体会是:初始化代码没有标准答案,每个文件都是团队对自己"历史问题清单"的回应。京东、天猫的老代码里那些逐元素重置,不是他们不够现代,而是电商页面模块体积太大,不能拿通配符冒险;王者荣耀官网里更彻底的重置,则是因为游戏页面视觉更统一,可以承受更大的样式覆盖成本。
对我个人而言,真正值钱的收获是一句话:初始化文件最大的价值不在于把样子变干净,而在于把不确定性降到一个可控范围内。你不需要每次接手项目都从零开始重写 reset,而是可以基于团队业务画像做取舍。如果你现在正在做自己的个人项目,建议直接拿出第四节那份模板跑一遍,遇到跟默认样式打架的地方,再按项目的实际情况去修改。多做两三个项目以后,你会真正理解为什么这些大站宁可保留一份看起来很"笨"的初始化文件,也不愿意在页面里到处补 hack。