一、开发边界情况

Web页面在客户端中展示时,开发者面对的情况会很复杂,比如数据是动态的,设备是多样的等。

1. 还原 UI

还原UI 界面的开发,写代码前先思考以下问题:

  • 文本超长应该怎么处理?直接裁切,还是省略?
  • 数据为空如何展示?不处理,还是将空白容器隐藏,还是展示占位符?
  • 容器太小应该怎么展示?

    1.1 阻止 input 自动填充

    使用 autocomplete=”new-password” 阻止自动填充

    1. <input type="text" id="cc" name="cc" autocomplete="new-password">

    1.2 阻止 input 拼写检查

    使用 spellcheck = “false”, 设置在可能的情况下关闭对元素内容拼写检查

    1. <p contenteditable spellcheck="false">This exampull.</p>

    2. 防御式 CSS

    2.1 尽量不显示设置元素宽高

    design to code 的过程,尽量不显示设置元素宽高,以保证动态数据不破坏布局

  • 为避免内容超出容器

    • 用 min-width 代替 width,min-height 代替 height
  • 实现平滑的折叠、展开:

    • 用合理的 max-width 代替 width,max-height 代替 height
    • 使用 transform: scaleY()

      2.2 声明Flexbox容器

      如当 Flexbox 容器的父元素 display 的值为以下时:
  • 设置为 flex 容器,它的宽度和父容器等宽,即 width 为 100%,也称块Flexbox容器

  • 设置为 inline-flex容器,它的宽度是由其子元素(后代元素)的内容来决定,相当于 width 为 auto,也称内联Flexbox容器

所以应该根据UI的视觉形态来正确的声明Flexbox容器,不能将所有Flexbox容器都声明为块盒!

2.3 考虑Flexbox中的换行

尽量在Flexbox容器上使用 flex-wrap 来避免意外布局的行为。但 flex-wrap: wrap 只有在Flex项目不能自动收缩扩展状态下有效,换句话说,如果在Flex项目中显式设置了 flex: 1时,即使你在Flexbox容器上显式设置flex-wrap为wrap也不能让Flex项目换行

2.4 注意 flex:1 不一定能均分列

flex:1 相当于:

  • flex-grow: 1;
  • flex-shrink: 1;
  • flex-basis: 0%

如果未显式设置flex(它是flex-grow、flex-shrink 和 flex-basis的简写属性)时,其初始值是:

  • flex-grow: 0;
  • flex-shrink: 1;
  • flex-basis: auto

当flex-basis 为 auto 时,Flex项目的宽度是max-content。也就是说,flex:1时,flex-basis变成了0%,这将覆盖Flex项目的内在尺寸(max-content)。flex-shrink不再做任何事情。

“默认情况下,弹性Flex项目(设置为flex:1的Flex项目)在收缩的时候,其宽度不会小于其最小内容尺寸(即 min-width,也就是max-content或固定尺寸元素的长度)。要改变这一点,需要显式设置min-width或min-height的值”

因此,要真正达到均分列,只显式设置 flex:1 还不行,还需要在Flex项目上显式设置min-width的值为0

  1. .item{
  2. flex:1 1 0%;
  3. min-width:0
  4. }

2.5 图片的宽高比与伸缩问题

当用户上传的图片与页面要展示的图片宽高比一样时,图片就会被拉伸,针对img元素展示的图片,可根据 object-fit 属性来控制展示方式:

  • object-fit: cover
  • object-fit: contain

此外,若图片设置的固定的宽或固定的高,不想要图片展示失去过多重要细节,则可设置图片的宽高比属性:

  • aspect-ratio: 4 / 3

    2.6 图片上的文字

    图片上的文字和图片间通常会有一层蒙层,以增加文本的可读性。如 ```css .card__content { background-image: linear-gradient( to top, hsla(0, 0%, 0%, 0.62) 0%, hsla(0, 0%, 0%, 0.614) 7.5%, hsla(0, 0%, 0%, 0.596) 13.5%, hsla(0, 0%, 0%, 0.569) 18.2%, hsla(0, 0%, 0%, 0.533) 22%, hsla(0, 0%, 0%, 0.49) 25.3%, hsla(0, 0%, 0%, 0.441) 28.3%, hsla(0, 0%, 0%, 0.388) 31.4%, hsla(0, 0%, 0%, 0.333) 35%, hsla(0, 0%, 0%, 0.277) 39.3%, hsla(0, 0%, 0%, 0.221) 44.7%, hsla(0, 0%, 0%, 0.167) 51.6%, hsla(0, 0%, 0%, 0.117) 60.2%, hsla(0, 0%, 0%, 0.071) 70.9%, hsla(0, 0%, 0%, 0.032) 84.1%, hsla(0, 0%, 0%, 0) 100% ); color: #fff; }
  1. 防御式的 CSS 应该是在 <img> 中设置一个与文本颜色具有一定对比度的颜色,以避免图片加载失败影响文本
  2. ```css
  3. img {
  4. background-color:grey
  5. }

2.7 图片的最大宽度

一般来说,不要忘记为所有图片设置max-width: 100%,使图片具有一定的响应式能力:

  1. img{
  2. max-width:100%;
  3. height:auto;
  4. object-fit:cover
  5. }

2.8 暗黑模式下的图片处理

第一种方式,使用 picture 处理,需准备明暗模式下的两种图片

  1. <picture>
  2. <source srcset="settings-dark.png" media="(prefers-color-scheme: dark)">
  3. <source srcset="settings-light.png" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)">
  4. <img src="settings-light.png" id="screenshot" loading="lazy">
  5. </picture>

第二种方式,使用 filter 处理,降低暗黑模式下的图片亮度

  1. @media (prefers-color-scheme: dark) {
  2. :root {
  3. --image-filter: grayscale(50%);
  4. }
  5. img:not([src*=".svg"]) {
  6. filter: var(--image-filter);
  7. }
  8. }

2.9 滚动链的锁定

滚动链(Scroll Chaining)指的是z轴的多个容器都出现了滚动。 现象:一个弹框(Modal)并向下滚动到底部(垂直方向)时,如果继续向下滚动则会引起弹框下方的内容(通常是 body 元素)会继续滚动

解决办法:在顶部的滚动容器中把 CSS 的 overscroll-behavior属性设置为 contain

  1. .modal{
  2. overscroll-behavior-y: contain;
  3. overflow-y:auto
  4. }

2.10 滚动条的空间占用

当内容变长出现了滚动条的时候,会引起布局发生变化,因为滚动条要占用布局元素的空间。对此解决办法:
设置 scrollbar-gutter。另外可使用 scrollbar-behavior : smooth 让滚动行为更顺滑。

  1. .element{
  2. scrollbar-gutter: stable;
  3. scrollbar-behavior: smooth
  4. }

2.11 滚动捕捉

在用户滚动浏览文档时,将其滚动到特定的位置,使用 scroll-snap-type、scroll-snap-align、scroll-snap-stop

  1. .container {
  2. overflow-x: auto;
  3. overflow-y: hidden;
  4. scroll-behavior: smooth;
  5. overscroll-behavior-x: contain;
  6. -webkit-overflow-scrolling: touch;
  7. scroll-snap-type: x mandatory;
  8. }
  9. img {
  10. scroll-snap-align: center;
  11. scroll-snap-stop: always;
  12. }

2.12 避开 100vh 的坑

iOS 上Safari使用 100vh 长期存在Bug。如果你将一个容器的高度设置为 100vh 时,会导致这个元素有点太高。因为移动端上的 Safari 在计算 100vh 时忽略了它的部分用户界面
第一种解决方法:

  1. body {
  2. height: 100vh;
  3. }
  4. @supports (-webkit-touch-callout: none) {
  5. body {
  6. height: -webkit-fill-available;
  7. }
  8. }

第二种解决办法:

  1. const vhCheck = () => {
  2. // 模拟 vh
  3. let vh = window.innerHeight * 0.01;
  4. // 设置 css 自定义属性
  5. document.documentElement.style.setProperty('--vh', `${vh}px`);
  6. };
  7. useEffect(() => {
  8. vhCheck();
  9. window.addEventListener('resize', vhCheck);
  10. return () => {
  11. window.removeEventListener('resize', vhCheck);
  12. };
  13. }, []);
  1. .my-element {
  2. height: calc(var(--vh, 1vh) * 100);
  3. }

2.13 z-index 失效问题

文档中的层叠上下文由满足以下任意一个条件的元素形成:

  • 文档根元素();
  • position 值为 absolute(绝对定位)或 relative(相对定位)且 z-index 值不为 auto 的元素;
  • position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素
  • flex (flexbox (en-US)) 容器的子元素,且 z-index 值不为 auto;
  • grid (grid) 容器的子元素,且 z-index 值不为 auto;
  • opacity 属性值小于 1 的元素
  • mix-blend-mode 属性值不为 normal 的元素;
  • 以下任意属性值不为 none 的元素:
    • transform
    • filter
    • perspective
    • clip-path
    • mask / mask-image / mask-border
    • isolation 属性值为 isolate 的元素;
    • -webkit-overflow-scrolling 属性值为 touch 的元素;
    • will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素;
    • contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。

解决方案:

  • 父级,任意父级,非 body 级别,设置 overflow:hidden
  • 使用3D transform变换,比如 translateZ(0)

    2.14 注意Grid 容器的固定尺寸

    假设我们有一个包含固定宽度 aside 和 自动宽度 main 内容的网格。 CSS 看起来像这样:
    1. wrapper {
    2. display: grid;
    3. grid-template-columns: 250px 1fr;
    4. gap: 1rem;
    5. }
    由于空间不足,这将在较小的视口尺寸上中断。 为避免此类问题,请在使用上述 CSS 网格时始终使用媒体查询。
  1. @media (min-width: 600px) {
  2. .wrapper {
  3. display: grid;
  4. grid-template-columns: 250px 1fr;
  5. gap: 1rem;
  6. }
  7. }

2.15 伪元素 content 中文乱码

关于 unicode 码在html、css、js中如何展示:

html &# + 10进制编码 + ;
css \ + 16进制编码
js \u + 4位16进制编码

解决办法:

  • 将中文转为unicode 编码。工具如:在线工具网,转换后示例:、—>\u3001
  • 再将字母 u 去掉

总结:确保HTML、CSS文件使用 UTF-8 格式,并且HTML文档也使用 UFT-8 的字符编码格式,即HTML文档的meta信息包含

2.16 html 空格实体输入

通常 html 中空格实体使用” ”表示,即 no break space,代表为一个空格实体。下表为Unicode 中的空格字符和“零宽度空格” 。常用的空格实体为” ”、” ”、” ”

Code Name of the character Width of the character
U+0020 SPACE Depends on font, typically 1/4 em, often adjusted
U+00A0 NO-BREAK SPACE As a space, but often not adjusted
U+2002 EN SPACE (nut) 1 en (= 1/2 em)
U+2003 EM SPACE (mutton) 1 em (nominally, the height of the font)

3. 用户输入正则限制

3.1 用户名正则

仅英文、数字、下划线,4-30位长度,不支持下划线开头:

  1. ^(?!_)([A-Za-z0-9_]){4,30}$

3.2 密码正则

  • 至少一位大小写字母、数字、特殊字符,6-20位长度:

    1. ^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[#?!@$ %^&*-]).{6,20}$
  • 至少一位大小写字母,只能包含字母、数字、特殊字符,6-20位长度:

    1. ^(?=.*?[A-Z])(?=.*?[a-z])[A-Za-z0-9#?!@$ %^&*-].{6,20}$
  • 至少包含一位大小写字母,只能输入ASCII!-~ 范围,6-20位长度:

    1. ^(?=.*?[A-Z])(?=.*?[a-z])[!-~]{6,20}$

    3.3 手机号正则

  • 共11位数字,1开头,第二位不能为 2

    1. ^1[3456789]\d{9}$

    二、问题定位

    1. 页面未展示出来?

  • 代码逻辑层面

    • 页面是否存在条件展示
  • 代码兼容性层面
    • 使用的 API 在特定浏览器版本下是否完全支持,部分支持
  • 展示层面

    • 第一步,查看页面 DOM 节点是否存在;
    • 第二步,查看页面 CSS 定位是否脱离文档流;
    • 第三步,查看页面 CSS 展示属性是否特殊?是否存在 display: none; visiblity: hidden
    • 第四步,查看页面 CSS 层级是否相对较低?是否存在 z-index 被遮挡的情况;

      2. 页面展示出,但布局/样式有问题?

      从设备与浏览器、缓存、屏幕、交互、网络与性能五个角度出发考虑以下因素:
  • 设备与浏览器

    • 设备类型(台式机电脑、笔记本电脑、平板电脑、手机、手表等)
    • 浏览器类型(Chrome、FireFox、Edge、Safari、Safari iOS、IE、QQ、360)及版本
  • 缓存
    • 浏览器缓存
  • 屏幕
    • 屏幕(或视窗)大小
    • 屏幕像素密度(比如Retina屏幕和非Retina屏)
    • 屏幕技术(比如,OLED、LCD、CTR等)
  • 交互
    • 用户的操作(比如用户对屏幕进行缩放、调整默认字号等)
    • 设备色彩样正(比如夜间模式,iOS的暗黑模式等)
  • 网络与性能
    • 网络速度
    • 性能(比如,设备硬件、服务器负载等)

其中最常见的问题来自于浏览器版本不兼容问题、浏览器缓存问题、屏幕大小开发未适配问题、用户操作问题。

参考资料