简介:《Web界面设计规范(Web Form的UI设计)》是一份面向Web前端、UI设计人员及团队规范制定者的界面规范文档,重点解决Web Form界面风格不统一、控件命名与外观混乱、协作维护成本偏高等问题。压缩包内共1个doc文件,约199KB,以规范条文和章节化目录呈现,便于查阅、打印和团队内部分享。文档从界面设计目的与适用范围讲起,依次覆盖文件命名、控件命名、控件外观和界面设计规范,并对字体、颜色、边距、尺寸单位、表格排版及网站目录结构作出说明;其中表格排版还涉及代码对齐、高宽设置等细节,字体、颜色、布局和图标数量也有统一约束。读者可据此整理UI检查清单,统一控件视觉与命名逻辑,降低返工和沟通成本,也可作为Web Form项目评审、新人培训与规范落地的参考底稿。已有2518人学习。
1. 同一个查询表单三种长相:这套 Web 界面设计规范到底管什么
接手一个多人协作的 Web Form 项目,最先暴露问题的往往不是业务逻辑,而是界面。同一个「客户查询」页面,A 同学写的按钮宽 80px、字号 12px,B 同学的按钮紧贴表格边沿、字号 10pt,C 同学干脆用服务端 Label 去排整段静态文字——三个页面摆在同一个菜单下,用户第一反应是「这不像一个系统」。这套 Web 界面设计规范就是从这个具体场景长出来的:它把界面设计里那些「看着差不多」的口头判断,拆成控件命名、控件外观、字体颜色边距、表格排版、目录结构、脚本与状态管理六类可逐条检查的条目。规范只覆盖所有 Web Form 的 UI 设计,不碰 Windows Form;面向的是三到五人以上、既要并行开发又要交付统一观感的团队。人少时规范是约束,人多了规范就是协作的公共接口。
2. 规范边界与目录骨架:先把「管什么、放哪里」定死
一份规范最容易失效的方式,不是条目写得不细,而是边界模糊——谁都以为归自己管,结果谁都不管。所以在写任何控件细节之前,先把两件事钉死:这套规范管到哪一层,以及文件到底放在哪个目录。前者决定评审时能不能判对错,后者决定三个月后接手的人能不能在十分钟内找到那张图。
2.1 适用范围的分界:Web Form 与 Windows Form 不能共用一套尺子
规范开篇就把适用范围写成两条硬边界:所有 Web Form 的 UI 设计适用本规范,Windows Form 的 UI 设计另有一套自己的规范。这句看似废话,实际是整套文档的地基。两套界面的底层差异太大,共用一套条目必然互相打架。
第一是度量单位不同。Web 页面最终渲染在浏览器里,字体尺寸用 pt、对象和线条尺寸用 px,两者之间有一层浏览器 DPI 换算;桌面客户端的控件尺寸直接跟系统 DPI 走,同一个数字在 125% 缩放下表现完全两样。把桌面端的「按钮高 23」搬进网页,结局就是不同分辨率下按钮高度飘忽。
第二是控件体系不同。Web Form 的服务端控件最终要渲染成 HTML 标签,一个TextBox出来可能是<input>,一个Label出来可能是<span>;桌面控件是原生句柄,属性和行为都是固定的。这意味着 Web 端的「控件外观」必须落到 CSS 上,而桌面端可以落在控件属性上。
第三是布局模型不同。网页是流式布局,表格、浮动、盒模型层层嵌套;桌面端大量使用绝对坐标。网页里写死一个left: 320px,换个分辨率就错位,这在桌面端是常规操作。
判断方法很简单:交付物是用户在浏览器地址栏里打开的页面,就走这套规范;是双击 exe 之后弹出来的窗口,就走另一套。混合场景也有,比如网页里嵌了一个打印报表控件或 ActiveX 控件,这类按宿主页面走——它出现在网页里,视觉上就得服从网页的字体、边距和配色,不能自己长一套皮肤出来。
还有一层边界要说清楚:这套规范只管「长什么样、叫什么名、放哪里」,不管业务流程、权限模型和数据校验规则。校验类控件的前缀归它管,但「金额必须大于零」这条业务规则不归它管。把边界写进文档,评审时才有拒绝越界的依据。
提示:规范文档里那句「适用范围」不是客套话,它是后续所有争议的裁决条款。写方案时把它放在第一章,比放在附录里有用得多。
2.2 目录骨架:images、common、temp、media 四个目录各管一摊
目录结构是规范里最容易被跳过、又最容易在后期爆炸的部分。项目前期大家随手往根目录丢文件,等到图片上百张、CSS 改了三个版本之后,没人说得清哪张图还在用。
这套规范的做法是在网站根目录固定开设四个子目录,职责一次性划清:
# 网站根目录的固定骨架 / # 站点根 ├── images/ # 跨栏目公共图片:公司标志、banner、菜单图标、通用按钮 ├── common/ # 公共文件:css、js、include 等被多个页面引用的资源 ├── temp/ # 客户提供的原始资料:文案、原始图片、素材包 │ ├── 20050531/ # 按接收日期建子目录,避免文件堆成一锅粥 │ └── 20050606/ ├── media/ # 多媒体:flash、avi、quick time 等体积较大的文件 └── <栏目名>/ # 每个一级栏目一个目录,与首页栏目结构一一对应 ├── images/ # 该栏目专用图片 └── media/ # 该栏目专用多媒体images和栏目内images的区别是复用范围:全站都要用的放根目录,只有一个栏目用的放栏目目录。这条规则的价值在于,某天要换公司 logo,只需要改一处,而不是全局搜索替换。
common目录承担的是「公共依赖」的角色。CSS、JS、include 文件集中放这里,页面引用时路径统一写成/common/xxx.css。这样做的好处是缓存友好——所有页面引用同一个 URL,浏览器只下载一次。
temp目录是最容易被忽视的一个。客户陆续发来的文案和图片,如果直接塞进images,几个月后没人敢删。规范要求按日期建子目录归档,等于给原始资料留了一个只进不出的冷藏室。清理的时候整目录删掉即可,不影响线上资源。
media目录单独拆出来,是因为多媒体文件体积大、加载慢,混在图片目录里会让备份和迁移变得很痛苦。栏目内容特别多、又分出下级栏目时,可以在栏目目录下再开子目录,但层级不要超过三层,否则路径长度会失控。
2.3 文件命名:.aspx 与 .ascx 的入口约定
目录定完了,接下来是文件本身。规范给出的约定很克制,只有两条,但都是硬性的。
第一条是扩展名与前缀的对应关系。Web Form 页面用.aspx,Web 用户自定义控件用.ascx,自定义控件在项目里的前缀统一为wuc。这条约定的作用是让文件类型在文件名层面就能被识别,不需要打开看内容。在解决方案资源管理器里看到wucOrderSearch.ascx,一眼就知道这是个可复用的用户控件,不是页面。
第二条是字符集约定:目录名和文件名全部用小写英文字母、数字、下划线的组合,不得包含汉字、空格和特殊字符,除非有特殊情况。
# 符合规范 /customer/query_list.aspx /customer/wuc_customer_card.ascx /images/btn_search.gif # 不符合规范(含汉字、空格、大写混用) /客户/查询 列表.aspx /Images/Btn Search.GIF这条规则看着死板,背后是血泪经验。中文文件名在部分服务器上会因为编码问题变成乱码链接;带空格的文件名在 URL 里要转义成%20,一旦被邮件系统或日志系统截断,链接就失效;大小写混用在 Windows 开发机上是好的,部署到区分大小写的服务器上就直接 404。
文件名长度也建议控制住,一般不超过 20 个字符,并且避免用拼音缩写。khcx.aspx这种名字,除了写它的人,没人猜得出是「客户查询」。
3. 控件命名规范:一张前缀表把协作成本压到最低
命名规范是最枯燥的一章,也是最容易在代码评审时吵起来的一章。有人觉得txtName和TextBoxName没区别,都是文本框。但当一个页面有三十个控件、后置代码有八百行时,前缀就是唯一的视觉锚点——你扫一眼就知道这行代码碰的是输入框还是按钮,不用回头翻设计器生成的代码。
3.1 前缀表怎么分组:展示、输入、动作、容器、验证五类
这套规范列了二十多个控件类型,逐条背不现实,但它们其实可以按「用户在界面上看到的形态」分成五组,分组之后就好记了。
| 分组 | 控件类型 | 前缀 | 示例 |
|---|---|---|---|
| 展示类 | Label | lbl | lblTip |
| 展示类 | Literal | ltl | ltlNotice |
| 展示类 | Image | img | imgLogo |
| 展示类 | Xml | xml | xmlConfig |
| 输入类 | TextBox | txt | txtName |
| 输入类 | DropDownList | ddl | ddlCity |
| 输入类 | ListBox | lst | lstDept |
| 输入类 | CheckBox | chk | chkAgree |
| 输入类 | CheckBoxList | chklst | chklstRole |
| 输入类 | RadioButton | rdo | rdoSex |
| 输入类 | RadioButtonList | rdolst | rdolstLevel |
| 输入类 | Calendar | cld | cldBeginDate |
| 动作类 | Button | btn | btnOK |
| 动作类 | LinkButton | lbtn | lbtnDelete |
| 动作类 | ImageButton | ibtn | ibtnSubmit |
| 动作类 | HyperLink | hlk | hlkDetail |
| 容器类 | Panel | pan | panSearch |
| 容器类 | PlaceHolder | plh | plhContent |
| 容器类 | Table | tbl | tblList |
| 容器类 | DataGrid | grd | grdOrders |
| 容器类 | DataList | dlst | dlstNews |
| 容器类 | Repeater | rep | repItems |
| 验证类 | RequireFieldValidator | rfv | rfvName |
| 验证类 | CompareValidator | cpv | cpvAmount |
| 验证类 | RangeValidator | rgv | rgvAge |
| 验证类 | RegularExpressionValidator | rev | revEmail |
| 验证类 | CustomValidator | cstv | cstvCode |
| 验证类 | ValidationSummary | vls | vlsAll |
| 其他 | AdRotator | adr | adrBanner |
| 其他 | CrystalReportViewer | crv | crvReport |
分组的意义在于降低记忆负担。看到grd就知道是列表类容器,看到rfv就知道是必填校验,看到btn就知道有回发动作。命名一旦有了语义,代码评审时「这个控件是干什么的」这类问题就消失了。
前缀之后的部分,规范没有强制,但通常的做法是「语义名首字母大写」:txtCustomerName、ddlOrderStatus、btnSearch。禁止用单字母(txtA)和纯数字(btn1),也禁止用拼音(txtKehuMingcheng)——拼音的歧义比英文缩写大得多。
3.2 从控件声明到代码审查的完整链路
命名规范如果只写在文档里,落地率大概三成。真正让它生效的是把它嵌进代码模板和后置代码的写法里。下面是一个符合规范的查询页片段:
<!-- 查询区:容器 + 输入 + 动作,前缀各自归位 --> <asp:Panel ID="panSearch" runat="server" CssClass="search-bar"> <asp:Label ID="lblName" runat="server" Text="客户名称"></asp:Label> <asp:TextBox ID="txtName" runat="server" Width="150px"></asp:TextBox> <asp:Label ID="lblStatus" runat="server" Text="订单状态"></asp:Label> <asp:DropDownList ID="ddlStatus" runat="server" Width="150px"> <asp:ListItem Text="全部" Value=""></asp:ListItem> <asp:ListItem Text="已确认" Value="1"></asp:ListItem> </asp:DropDownList> <!-- 必填校验直接跟着被校验的控件走,前缀用 rfv --> <asp:RequiredFieldValidator ID="rfvName" runat="server" ControlToValidate="txtName" ErrorMessage="请输入客户名称"></asp:RequiredFieldValidator> <asp:Button ID="btnSearch" runat="server" Text="查询" OnClick="btnSearch_Click" /> </asp:Panel> <!-- 结果区:DataGrid 一条一条绑,前缀 grd --> <asp:DataGrid ID="grdOrders" runat="server" AutoGenerateColumns="false"> <Columns> <asp:BoundColumn DataField="OrderNo" HeaderText="订单号"></asp:BoundColumn> <asp:BoundColumn DataField="Amount" HeaderText="金额"></asp:BoundColumn> <asp:TemplateColumn HeaderText="操作"> <ItemTemplate> <asp:LinkButton ID="lbtnView" runat="server" Text="查看" CommandArgument='<%# Eval("OrderNo") %>'></asp:LinkButton> </ItemTemplate> </asp:TemplateColumn> </Columns> </asp:DataGrid>这段代码的关键点不在标签本身,而在三处约束。第一,容器控件panSearch用CssClass挂样式,而不是写style="..."内联样式,这样所有查询区的间距、背景色都从同一份 CSS 来,改一处全站生效。
第二,校验控件的前缀与被校验控件保持同名词根:txtName对应rfvName,ddlStatus如果要加必选校验就是rfvStatus。这条约定在排查「哪条错误提示对应哪个输入框」时非常省时间,尤其在表单有十几个字段时。
第三,DataGrid里的模板列控件前缀也要遵守规范。lbtnView出现在ItemTemplate里,运行时会被重命名成类似grdOrders_ctl02_lbtnView,但源码里的前缀保证了你在模板里扫一眼就知道它是链接按钮,而不是普通按钮。
对应的后置代码同样遵守前缀约定:
// 事件处理器命名沿用「控件ID_事件」的格式,方便在代码里反查控件 protected void btnSearch_Click(object sender, EventArgs e) { // 输入值直接从前缀明确的控件取,不用再翻设计器确认类型 string name = txtName.Text.Trim(); string status = ddlStatus.SelectedValue; // 查询结果绑定到 grd,分页参数按需传入 grdOrders.DataSource = OrderService.Query(name, status); grdOrders.DataBind(); }事件处理器名btnSearch_Click直接对应控件 ID,这是 .NET 设计器自动生成的默认格式,也是规范推荐保留的格式。它有一个实际好处:在解决方案里全局搜索btnSearch,控件声明、事件绑定、事件实现三处会同时命中,排查回发问题时不用一层层点进去。
3.3 前缀撞车与缩写可读性的处理办法
前缀表用久了会遇到几个具体麻烦,规范没有全部写死,但项目里得给出一致的处理办法。
第一个麻烦是形近前缀。lst(ListBox)、ltl(Literal)、tbl(Table)、txt(TextBox)四个前缀在等宽字体下只差一两个字母,快速扫代码时容易看混。处理办法是加大前缀与语义名之间的视觉分隔:坚持语义名首字母大写,写成lstDept、ltlNotice、tblHeader、txtName,靠大小写变化把前缀「框」出来。如果团队仍然觉得容易混,可以在代码评审清单里加一条:这四个前缀必须人工复核一次。
第二个麻烦是动作类前缀的区分。btn、lbtn、ibtn分别对应普通按钮、链接按钮、图片按钮,它们的交互行为不同——lbtn在DataGrid模板里用得多,ibtn需要准备图片资源。选型时不要凭喜好,按交付形态定:文字按钮用btn,列表行内操作用lbtn,需要图标且图标本身承载语义时用ibtn。
第三个麻烦是缩写撞上业务缩写。比如「客户」的英文缩写是cust,控件名写成txtCustCode没问题;但如果某个业务模块本身就叫CRM,控件名写成txtCRMNo会让前缀与业务词黏在一起。处理办法是在业务缩写前后加下划线或者用完整单词:txtCrmNo。
第四个麻烦是历史代码。老页面里到处是TextBox1、Button2,全部重命名的成本可能比收益高。一般的原则是:新增和改动过的控件必须符合规范,未改动的老代码不动。规范的作用范围是「增量」,不是「存量清零」,这一点在文档里写明,能省掉大量无谓的争论。
4. 控件外观与界面基线:把「好看」拆成可校验的数字
命名解决的是「代码里怎么认」,外观解决的是「屏幕上怎么排」。外观规范最容易写成形容词——「简洁」「清爽」「协调」,这类词在评审时没法用。这套规范的做法是把形容词翻译成数字和枚举值:宽度要么是具体像素,要么是「适应文本」;边距最小 3px;字号一律 9pt。
4.1 宽高取值表:{50px|文本宽度} 这种写法怎么落地
规范用{50px|文本宽度}这种花括号写法表示「取值可以是列举项之一」。这个记法本身值得学:它把「这个属性有哪些合法值」和「默认用哪个」压在一行里,比写三段文字说明清楚。
| 控件类型 | 宽度 | 高度 | 备注 |
|---|---|---|---|
| Label | 适应文本 | 适应文本 | 不设固定宽高 |
| TextBox | 150px / 100% / 超短 / 超宽 | 默认值 | 按输入内容长度选档 |
| Button | 50px / 文本宽度 | 默认值 | 文字按钮最小 50px |
| LinkButton | 适应文本 | 适应文本 | 跟随文字 |
| HyperLink | 适应文本 | 适应文本 | 跟随文字 |
| DropDownList | 150px / 100% / 适应文本 | 默认值 | 与同行 TextBox 对齐 |
| ListBox | 150px / 100% / 适应文本 | 100px / 适应项目数 / 按需 | 高度按可见项数估 |
| DataGrid | 按需 | 按需 | 由列宽和行数决定 |
| CheckBox | 适应文本 | 默认值 | 不设固定宽度 |
| CheckBoxList | 适应文本 | 适应项目 | 纵向排列时高度随项目数 |
| RadioButtonList | 适应文本 | 适应项目 | 同上 |
| Panel | 适应内部控件 / 按需 | 适应内部控件 / 按需 | 由子控件撑开 |
| 各类 Validator | 适应文本 | 默认 | 错误提示文字长度不定 |
「适应文本」这条最容易被误读成「随便」。它的实际含义是:不写死width,让内容决定宽度,但必须保证同一行内相邻控件的基线对齐。比如lblName后面跟txtName,标签宽度不设,文本框宽 150px,两者的垂直对齐靠父容器的 CSS 处理,不能靠给标签加固定宽度硬凑。
「100%」这一档是为自适应布局准备的。查询区的文本框如果放在一个可伸缩的容器里,宽度写100%比写死 300px 更稳,因为容器变窄时它跟着缩,不会撑破布局。但 100% 的文本框在超宽屏上会变成一条长条,可读性差,所以规范同时保留了「超短」「超宽」两档人工判断。
「默认值」这一档指的是高度不由开发者指定,统一走 CSS 里的控件基线高度。这样所有按钮在同一个页面里高度一致,不会出现「这个按钮 22px、那个按钮 26px」的情况。
4.2 字体、颜色、边距、单位:四条不可商量的基线
外观规范里有四条数值型基线,全部落在 CSS 里,页面中不写内联样式。
/* 全局基线:字体、字号、行距集中在 body 和通用类上 */ body { /* 基准字号 9pt,屏显约等于 12px,兼顾中英文可读性与信息密度 */ font-size: 9pt; /* 字体序列:优先 Verdana,逐级回退到无衬线族 */ font-family: Verdana, Arial, Helvetica, sans-serif; /* 正文行距按百分比定义,120% 紧凑、150% 宽松 */ line-height: 120%; /* 最小边距 3px,避免内容紧贴容器边沿 */ margin: 0; padding: 3px; } /* 表格统一边距与单元格间距,防止不同页面表格贴边程度不一 */ table { margin: 3px; border-collapse: collapse; } td, th { padding: 3px; }四条基线的设计逻辑分别是这样。
字体:基准字号 9pt,字体序列为 Verdana、Arial、Helvetica、Sans-Serif。选 Verdana 打头是因为它在小字号下的字腔开阔,中文回退到系统默认无衬线字体时观感也不会跳。字号用 pt 而不是 px,是因为 pt 是打印单位,报表打印出来的字号和屏幕上一致,这对财务类系统很关键。9pt 在 96 DPI 下约等于 12px,是信息密度和可读性的折中点。
颜色:规范只说「以清爽简洁为准,所有色彩设定在 CSS 中完成」,没有给具体色值。这是克制的写法——色板应该由项目视觉稿定,规范只约束「不许在页面里写死颜色」。这条约束的实际价值是让换肤成为可能:改一份 CSS 就能换整套配色,不用逐个页面找style="color:#333"。
边距:页和表格都必须有边距,最小 3px,默认值在 CSS 里设定。3px 这个数字的来源是「小于 3px 的间隙在 1024 分辨率下会糊成一条线」,属于经验值。规范强调「避免紧贴边沿」,因为紧贴会让表格看起来像溢出,视觉上很廉价。
尺寸单位:字体尺寸一律用 pt,对象和线条的尺寸一律用 px。这条分界的理由前面提过,pt 保证打印一致,px 保证像素级对齐。混用的后果是同一份样式在不同 DPI 下缩放比例不一致,边框和文字对不齐。
注意:规范要求字体、颜色、边距全部在 CSS 中设定,页面里出现
<font>标签或内联style都算违规。这条在评审时最好用工具查,人工翻页面太慢。
4.3 表格排版:嵌套层级、代码对齐与空单元格
表格排版是这套规范里最细的部分,因为它直接决定页面加载时的视觉稳定性。规范给了三条约束:代码对齐、宽高归属唯一、嵌套不超过三层。
先看代码对齐。嵌套表格时,<table>和<tr>左对齐,<td>缩进一个 TAB;<td>内如果还有嵌套表格,那个<table>再缩进一个 TAB;如果<td>里没有嵌套表格,</td>结束标记要和<td>同一行,不能换行。
<!-- 符合规范:外层 table 与 tr 对齐,td 缩进,无嵌套时 td 与 /td 同行 --> <table width="600"> <tr> <td><img src="/images/sample.gif" alt="示例"></td> <td> <table width="300"> <tr> <td>嵌套内容</td> </tr> </table> </td> </tr> </table>这条规则不是洁癖。浏览器解析 HTML 时,标签之间的换行会被当成一个半角空格参与排版。<td>和<img>之间多一个换行,图片左侧就多出约 4px 的空白,而这个空白在不同浏览器里宽度还不一样。解决方案不是到处加负 margin,而是从源码层面不产生多余空白。如果确实需要空格,就显式写一个空格字符,让意图可见。
第二条约束是宽高归属唯一。只有一列的表格,width写在<table>标签上;只有一行的表格,height写在<table>标签上;多行多列的表格,width和height写在第一行或第一列的<td>上。总原则是:任何一个单元格的宽高,只能有一个地方在控制它,改代码里任何一个数值,浏览器里都必须能看到变化。
这条规则是为了对付「改了没反应」这个经典问题。当<table>和<td>同时写了width,浏览器按自己的优先级取一个,开发者改另一个就看不到效果,然后开始怀疑人生。规范把控制点收敛到一处,让每次修改都可验证。
第三条约束是嵌套深度。表格嵌套尽量控制在三层以内,并且尽量避免用一整张大表格把全页内容包起来。
<!-- 不推荐:整页一个大表格,浏览器必须等表格闭合才开始渲染 --> <table width="100%"> <tr> <td>页头</td> </tr> <tr> <td>正文……(几百行内容)</td> </tr> </table> <!-- 推荐:按区块拆分,或者用 tbody 分块,让浏览器能边下边渲染 --> <div class="header">页头</div> <div class="content">正文……</div>原因在浏览器渲染机制:表格是「整体解析」的元素,浏览器要拿到</table>才能确定列宽,然后一次性绘制。整页包在一张大表格里,用户会先面对长时间白屏,然后所有内容同时蹦出来。拆成多个区块后,页头可以先渲染,正文可以边下边显示。如果业务上确实需要大表格,就用<tbody>把表格分块,浏览器可以按块渲染。
空单元格也有专门规定:不允许存在没有任何内容的空单元格,空单元格里要插入一张 1×1 的透明 gif(通常命名为null.gif),高度由它撑住。原因是部分浏览器会认为空单元格非法而直接不渲染,导致表格塌陷、列错位。
4.4 排版细则:首行缩进、图片尺寸、行距与相对路径
剩下的一组条目偏细节,但每条都对应过真实问题,值得逐条过一遍。
首行缩进不要用 或全角空格,规范做法是在样式表里定义p { text-indent: 2em; },然后每段用<p>标记,并且不要省略</p>。用空格凑缩进的后果是:换字体、换字号之后缩进量全变,而且复制粘贴到 Word 里会带出一堆不可见字符。
禁止用<img width="?" height="?">人为干预图片显示尺寸,并且建议<img>标签里干脆不带width和height属性。理由是设计稿改图是常态,写死尺寸意味着每次改图都要同步改代码,漏一次就是拉伸变形。让浏览器按图片原始尺寸渲染,改图不用改代码。
但这条有个副作用:图片没加载完时浏览器不知道该留多大位置,页面会在加载过程中抖动,图片越大抖得越厉害。所以规范补了一句例外——如果预料到会明显抖动,就在最后给<img>补上width和height。这是一个典型的「默认放开、必要时收紧」的设计。
不要用<br>干预分段。<br>是换行不是分段,用它凑出来的段落间距在样式调整时会全乱。不同语种的文字之间应该有一个半角空格,但避头符号之前和避尾符号之后除外;汉字之间的标点用全角,英文字母和数字周围的括号用半角。
字号一律用样式表实现,页面中禁止出现<font size="?">。这条在今天看来是常识,但在规范成文的年代,<font>还是主流写法,禁掉它意味着所有字号调整都集中到 CSS。
不要连续出现多于一个 ,也尽量少用全角空格——英文字符集下全角空格会渲染成乱码方框。需要空白时用text-indent、padding、margin、hspace、vspace或者透明 gif 实现。
行距建议用百分比,常用的两个值是line-height: 120%和150%。百分比的好处是跟着字号自适应,字号改大行距同比放大,不用重新算像素值。
路径全部采用相对路径,链接到目录下的默认文件时不必写全名。写aboutus/index.htm不如写aboutus/,后者在换默认文件名时不用改链接。
最后两条关于脚本和状态。客户端脚本一律用 JavaScript,编码规则参照 C# 规范,这条约束的是风格统一。状态管理这条更硬:所有不需要在 PostBack 之后取值的控件一律关闭 ViewState,能关整个页面就关整个页面;静态文本全部直接用 HTML 标记,不要用服务端控件。
<!-- 不推荐:静态文字用服务端 Label,白白增加 ViewState 和渲染开销 --> <asp:Label ID="lblTitle" runat="server" Text="订单明细"></asp:Label> <!-- 推荐:静态文字直接写 HTML --> <h2 class="section-title">订单明细</h2> <!-- 推荐:不需要回发取值的控件显式关闭 ViewState --> <asp:TextBox ID="txtRemark" runat="server" EnableViewState="false"></asp:TextBox>这里的设计逻辑是:ViewState 会把控件状态序列化进页面的隐藏字段,页面越大、控件越多,这个隐藏字段越大,往返传输越慢。一个只用来显示标题的 Label,参与了 ViewState 却永远不会有值变化,属于纯负担。把这类控件换成静态 HTML,页面体积能明显下降。
5. 常见问题排查:规范落地时最容易翻车的五个点
规范写完之后,真正的工作才开始。下面五条是这类项目里最常出现的翻车点,每条都按「现象、原因、解决」写,可以直接拿去当排查清单。
5.1 关掉 ViewState 之后,回发时下拉框的值没了
现象:为了减小页面体积,把页面级EnableViewState设为false,结果点查询按钮后,DropDownList的选中项回到第一项,DataGrid的分页也乱了。
原因:混淆了「控件状态」和「表单提交值」。TextBox、DropDownList这类输入控件在回发时,值是从 HTTP 表单提交里恢复的,不依赖 ViewState;但DataGrid的分页索引、排序方向、动态生成的列这些「非提交状态」,全靠 ViewState 保存。一刀切关掉,等于把这些状态扔了。
解决:按控件粒度关闭。纯展示的Label、Literal、静态Image设EnableViewState="false";需要维持分页、排序、动态结构的DataGrid、Repeater保留 ViewState。页面级开关只在确认整页无状态依赖时才用,并且要在测试环境把每个按钮点一遍。
5.2 表格在某些浏览器里塌陷,列对不齐
现象:本地开发机上看一切正常,换一台机器或者换一个浏览器,表格的某一列压缩成一条线,或者整行高度变成零。
原因:空单元格被浏览器判定为非法结构而跳过渲染。表格的列宽是靠内容撑的,某一列没有内容,这一列就失去宽度依据。另外<td>与<img>之间的换行产生额外空白,也会让列宽计算偏移。
解决:空单元格统一插入 1×1 透明 gif 撑高,并且按规范让</td>与<td>同行,不产生多余空白。表格宽高只在一处声明,避免浏览器在不同优先级之间摇摆。
5.3 图片加载过程中页面剧烈抖动
现象:网速慢的时候打开列表页,内容先渲染出来,图片加载完成后整页往下跳一大截,用户正要点按钮结果点空了。
原因:<img>标签没有width和height,浏览器在图片下载完成前不知道该预留多大空间,只能先按零高度排版,图片到了再重排。
解决:对尺寸较大的图片补上width和height属性,让浏览器提前占位。规范里「建议不写宽高」是为了方便改图,但这条有前提——图片尺寸小、抖动可忽略。凡是首屏大图、列表缩略图,一律补上宽高。
5.4 整页包在一张大表格里,首屏白屏时间过长
现象:页面结构不算复杂,但打开时先白屏两三秒,然后所有内容一次性出现,中间没有任何渐进过程。
原因:整页内容嵌套在一张大表格里。表格是整体解析元素,浏览器必须等</table>才能确定布局,期间不绘制任何内容。
解决:按页头、导航、正文、页脚拆成独立区块,用div加 CSS 控制布局。如果业务上必须用表格承载数据,就用<tbody>分块,让浏览器能按块渲染。这条改造的收益在弱网环境下特别明显。
5.5 控件前缀形同虚设,代码评审变成猜谜
现象:规范发了三个月,代码里还是能看到TextBox1、Button3,问起来都说「赶进度来不及改」。
原因:规范只写在文档里,没有嵌进工具链。新建页面时设计器默认生成TextBox1,开发者不改也能跑,规范没有任何强制力。
解决:把命名检查放进代码评审清单,作为合并前的必过项;在项目模板里预置好常用控件的命名示例,新建页面直接复制;对改动过的老代码执行「改到哪、规范到哪」,不要求一次性全量重命名。规范的生命力在增量执行,不在一次运动式整改。
6. 验收与答辩:一份能照着走的检查清单
规范类的交付物,验收方式和代码类项目不一样。代码可以跑测试,规范只能靠「抽检 + 对照」。我一般会把验收拆成两步:先做一份可勾选的检查表,随机抽五个页面逐条对照;再做一次反向验证,故意改坏一处,看规范能不能发现问题。
检查表按规范章节一一对应,每行给一个可判定的标准,避免「看起来还行」这种结论:
| 检查项 | 判定标准 | 抽检方式 |
|---|---|---|
| 目录结构 | 根目录存在 images、common、temp、media 四个目录 | 直接看目录树 |
| 文件命名 | 无汉字、无空格、无大写字母混用 | 批量列出文件名扫一遍 |
| 控件前缀 | 页面内所有服务端控件前缀符合前缀表 | 搜ID="逐个核对 |
| 字号 | 全站基准 9pt,且只在 CSS 中定义 | 搜页面内是否有font-size内联 |
| 字体标签 | 页面中无<font>标签 | 全局搜索 |
| 边距 | 页与表格边距不小于 3px | 抽查三个页面的计算样式 |
| 表格嵌套 | 嵌套层级不超过三层 | 看源码缩进层级 |
| 空单元格 | 无空<td>,或已插入透明 gif | 搜<td></td> |
| 静态文本 | 无服务端 Label 承载纯静态文字 | 搜asp:Label看 Text 是否写死 |
| ViewState | 展示型控件已关闭 | 搜EnableViewState |
| 路径 | 全部为相对路径 | 搜http://和盘符路径 |
批量检查用命令行比人工翻快得多,下面这几条在项目根目录执行,能筛出大部分问题:
# 找出含汉字或空格的文件名(规范禁止) find . -type f | grep -P '[\x{4e00}-\x{9fa5}\s]' # 找出页面里残留的 font 标签 grep -rn "<font" --include="*.aspx" --include="*.ascx" . # 找出空单元格 grep -rn "<td></td>" --include="*.aspx" . # 找出写死的绝对路径 grep -rn "http://\|C:\\\\" --include="*.aspx" --include="*.css" .这几条命令的判定口径要提前和团队对齐,比如grep命中的结果里,注释掉的代码算不算违规,得有个说法,否则每次跑出来的数字都对不上。我一般约定:注释掉的旧代码不算,但要在一个迭代内清掉。
反向验证这一步很多人会省,其实最有价值。随机挑一个页面,把txtName改成TextBox1,把<td>里的透明 gif 删掉,把width同时写在<table>和<td>上,然后让另一个没参与改造的同事按检查表走一遍。如果他能把三处问题都找出来,说明这份规范是可执行的;如果他看不出来,问题不在他,在检查表的表述还不够具体。
答辩或者交付评审时,讲法也有讲究。不要从「规范有哪些章节」开始讲,那是目录朗读。从一个具体页面切入:改造前是什么样、哪三处不符合规范、改完之后页面体积降了多少、首屏快了多久。用改造前后的对比截图和一组实测数字支撑,评审的人不需要听懂 ViewState 的原理,他只要能看出「这套东西确实让页面变快、变一致」就够了。规范文档本身的价值不在厚度,在于它能不能被下一个人直接拿去用。
我以前做这类规范交付,习惯把文档写完就发出去,觉得条理清楚就算完成。后来有一次,一个页面的表格在客户现场莫名错位,翻了半天才发现是<td>后面多了一个换行——规范里明明写了这一条,但没人真的去看。从那以后我每次交付规范,都强制走一遍「抽五个页面 + 反向验证」的流程,宁可文档薄一点,也要保证每条规则都有人实际对着屏幕验证过。希望这套结构和清单能帮到你。
本文还有配套的精品资源,点击获取