likes
comments
collection
share

CSS 20大酷刑

作者站长头像
站长
· 阅读数 68

“开窍”的本质就是积累,积累到一定程度,突然就有了新理解,打开了新世界的大门,然后在“超大积累”的基础上又来了一个大爆发,直接把其他人彻底甩开了。

大家好,我是柒八九

前言

在讲正文之前,看到了一篇九边大佬写的<笨功夫是普通人最后的依靠>文章. 其中有一些文字很打动我. 然后,秉承着独乐乐不如众乐乐的想法,给大家分享出来. 以下是我认为最好的一段文字,如果想看全文,可以搜索文章名字进行查阅.

“开窍”的本质就是积累,积累到一定程度,突然就有了新理解,打开了新世界的大门,然后在“超大积累”的基础上又来了一个大爆发,直接把其他人彻底甩开了。“超大积累”这事是个负担,也是个门槛,更是个壁垒。你想过这个门槛是很难的,别人一样难,过不去都白费,想过去只有一个办法就是死磕,什么“认知”什么“思维高度”都没用,就是得老老实实下硬功夫。 就得硬磕,克服前期的焦虑,克服中间的痛苦,克服后期的迷茫,不断投时间,直到有一天,发现自己投入的时间原来已经这么多,这时候,你发现不再像以前那么迷茫了,也不再像以前那样痛苦了,你已经跨过了那个门槛,进入了一个壁垒,别人很难追上了。 同样的,编程这事也是这个道理,几年前一个读者跟我说,他想学编程,是应该先看数据结构,还是先看操作系统原理?我说你先写出来一万行可运行的代码再说,从最简单的开始,一点一点写,最早的程序可能只有三四行,到后来可能有几百上千行。一万行代码是个“入门门槛”,跨不过去,这辈子都别想入门,别误会,这里说的“入门”相当于学英语刚学完前一千词汇。在这之前,看那些乱七八糟的书都是浪费时间。


好了,言归正传.在前面介绍浏览器性能指标时,发现很多优化方案都是针对CSS的.

根据最新的HTTP档案报告,网络仍然是一个臃肿的混乱,平均网站需要2,400KB的数据,分布在70个HTTP请求中。

CSS 20大酷刑

CSS 20大酷刑

诚然,CSS不是造成网络臃肿的主要原因。尽管如此,仍然有优化的空间,以及可以改变我们使用CSS的方式来提升网站性能的方法。

CSS 20大酷刑 从图中我们可以看到, 一个网站整体资源大小为2400KB,而CSS加上Front的总体资源为70+120, 也就是说和样式相关的资源占整体网站10%左右. 如果我们通过一些合理的方式进行优化处理,那势必对网站整体性能有一个好的提高.

你能所学到的知识点

  1. 前置知识点
  2. 学会使用工具
  3. 针对终点资源的处理
  4. 用 CSS 效果替换图片
  5. 删除不必要的字体
  6. 避免使用 @import
  7. 合并CSS
  8. 使用现代布局技术
  9. 为CSS瘦身
  10. 坚持使用层叠特性
  11. 简化选择器
  12. 警惕耗时的属性
  13. 采用 CSS 动画
  14. 避免为耗时的属性制作动画
  15. 指明哪些元素将成为动画
  16. 采用 SVG 图像
  17. 用 CSS 制作 SVG 样式
  18. 避免使用 Base64 位图图像
  19. 考虑关键 CSS
  20. 考虑渐进式渲染

好了,天不早了,干点正事哇。

CSS 20大酷刑


1. 前置知识点

前置知识点,只是做一个概念的介绍,不会做深度解释。因为,这些概念在下面文章中会有出现,为了让行文更加的顺畅,所以将本该在文内的概念解释放到前面来。如果大家对这些概念熟悉,可以直接忽略

浏览器支持的图片类型简览

类型出现时间特点用途
JPEG (Joint Photographic Experts Group)1992年位图格式, 有损压缩,适用于照片和图像,支持丰富色彩和渐变。 不支持透明度数字摄影,图像分享。
PNG (Portable Network Graphics)1995年位图格式, 无损压缩,支持透明度和多种颜色模式(包括RGBA)。图标、图形、简单图像。
GIF (Graphics Interchange Format)1987年位图格式, 支持透明度和动画,适用于小型动画和图形。小型动画,简单图形。
SVG (Scalable Vector Graphics)1999年矢量图形格式,可无限缩放,通过XML标记描述。图标、图表、插图,适用于各种分辨率设备。
WebP2010年小文件大小和高质量,兼具JPEG和PNG特点。替代JPEG和PNG,但受到浏览器支持限制,提供更小的图像文件。

前端项目使用字体

在前端项目中使用字体通常涉及以下步骤:

  1. 选择适合的字体:首先,我们需要选择适合我们项目的字体。我们可以从字体库(如Google FontsAdobe Fonts等)中选择,也可以购买商业字体。确保选择的字体与项目的风格和需求相匹配。

  2. 获取字体文件:一旦选择了字体,我们需要获取字体文件。字体文件通常以TTF(TrueType Font)、OTF(OpenType Font)或WOFF(Web Open Font Format)格式提供。我们可以在字体库的网站上下载这些文件,或者从商业字体提供商那里获取。

  3. 嵌入字体:将字体文件嵌入到我们的项目中。最常见的方法是使用CSS的@font-face规则。这将允许我们在网页中引用并加载字体文件。

  4. 定义字体样式:在CSS中,使用font-family属性定义使用的字体。我们可以为不同的元素、类或ID应用不同的字体。

    /* 引用自定义字体 */
    @font-face {
      font-family: 'CustomFont';
      src: url('path-to-font/custom-font.woff') format('woff');
    }
    
    /* 应用字体样式 */
    body {
      font-family: 'CustomFont', sans-serif;
    }
    
  5. 应用到文本:在HTML中,将适当的字体样式应用于文本元素。

    <p class="custom-font">这是一段使用自定义字体的文本。</p>
    

CSS 常见命名方式简览

BEM

BEM(Block Element Modifier)是一种流行的CSS命名方法,用于在开发中创建可维护、可重用的样式。它的核心思想是将样式的命名规范化,使得开发者可以更清晰地理解代码结构和样式关系。

BEM主要由三个部分组成:

  1. 块(Block)
  2. 元素(Element)
  3. 修饰符(Modifier)

以下是BEM命名方法的简单介绍

  1. 块(Block):块是一个独立的、可重用的组件或模块,它有一个具有描述性的名称。块的名称应该简明扼要,使用连字符分隔单词。
<div class="header">
  <!-- 块:header -->
</div>
  1. 元素(Element):元素是块的组成部分,它们只在特定块的上下文中有意义。元素的名称是由块名和元素名组成,中间用双下划线 __ 分隔。
<div class="header">
  <div class="header__logo">
    <!-- 块:header, 元素:logo -->
  </div>
</div>
  1. 修饰符(Modifier):修饰符用于改变块或元素的外观或状态。修饰符的名称是由块名(或元素名)、双连字符 -- 和修饰符名组成。
<div class="button">
  <!-- 块:button -->
</div>
<div class="button button--primary">
  <!-- 块:button, 修饰符:primary -->
</div>

Atomic CSS

Atomic CSS将样式分解为小的、原子性的类,每个类仅实现一个样式属性。开发者可以通过组合这些类来构建样式。Atomic CSS强调复用小的、独立的样式规则,以实现更高的灵活性。

像最近在CSS界引起轩然大波Tailwind 就是Atomic CSS的代表作. 我们后期也会有对应的文章来介绍它.


CSS Modules

CSS Modules是一种通过将CSS作为模块导入的方式来实现局部作用域的CSS。每个模块的类名是局部的,不会与其他模块中的类名冲突。这有助于避免全局命名冲突和样式泄露。

/* 使用 CSS Modules 的 React 组件 */
import styles from './Button.module.css';

const Button = () => {
  return (
    <button className={styles.button}>
      {/* 按钮内容 */}
    </button>
  );
};


Styled Components

Styled Components是一种在React应用中使用的CSS-in-JS解决方案,它允许将样式直接嵌入组件中。样式会根据组件的上下文进行管理,实现了组件级别的样式隔离。

/* 使用 Styled Components 的 React 组件 */
import styled from 'styled-components';

const Button = styled.button`
  background-color: ${props => props.primary ? 'blue' : 'gray'};
  color: white;
  padding: 10px 20px;
`;

其实针对命名规范还有很多:例如

  • OOCSS(Object Oriented CSS):

    • OOCSS强调将样式抽象为可重用的对象,以实现更好的代码复用性。
  • SMACSS(Scalable and Modular Architecture for CSS):

    • SMACSS强调模块化和可扩展的CSS架构。它将样式分为基础、布局、模块、状态和主题等不同类型,以实现更清晰的组织和命名。

2. 学会使用工具

人类最重要的进化,是学会使用工具. 就像我们,在接触一个新的领域时,学会用工具来辅助我们,总比我们埋头苦干,闭门造车的强.

除非你知道故障出在哪里,否则无法解决性能问题。浏览器DevTools是开始的最佳位置:从菜单启动或按下F12Ctrl + Shift + I,或对于macOS上的Safari/Chrome,按下Cmd + Alt + I

网络选项卡

网络选项卡显示了资源下载时的瀑布图。为了获得最佳效果,禁用缓存并考虑将网络速度限制为较低的速度。寻找下载速度慢或阻塞其他资源的文件。通常情况下,浏览器在下载和解析CSS和JavaScript文件时会阻止浏览器渲染。

CSS 20大酷刑

如果想了解更多关于网络选项卡的使用和介绍,可以查看Chrome Developer关于NetWork的介绍

性能选项卡

  1. DevTools中,点击Performance选项卡。

  2. 确保Screenshots复选框已启用。

  3. 点击“捕获设置”。DevTools会显示与捕获性能指标相关的设置。

  4. 对于CPU,选择4倍减速。DevTools会将我们的CPU减速到正常速度的4倍。

    CSS 20大酷刑

  5. 在DevTools中,点击“录制”按钮。DevTools会在页面运行时捕获性能指标。

  6. 页面性能分析 CSS 20大酷刑

  7. 等待几秒钟。

  8. 点击“停止”按钮。DevTools停止录制,处理数据,然后在性能面板上显示结果。 CSS 20大酷刑

性能选项卡分析浏览器进程。开始记录,运行诸如页面重新加载之类的活动,然后停止记录以查看结果。寻找:

  • 过多的布局/回流操作,浏览器被迫重新计算页面元素的位置和大小。
  • 耗时的绘制操作,像素发生了变化。
  • 合成操作,将页面的绘制部分组合在一起以在屏幕上显示。这通常是处理器最不密集的操作。

如果想了解更多关于网络选项卡的使用和介绍,可以查看Chrome Developer关于Performance的介绍

Lighthouse选项卡

Lighthouse是一个开源的自动化工具,用于提高网页的质量。我们可以对任何网页运行它,无论是公开页面还是需要身份验证的页面。它提供了性能、可访问性、渐进式Web应用、SEO等方面的审查。

具体使用方式,我们之前的文章有介绍过,这里就不再介绍了.


在线分析工具

另外还可以使用在线分析工具,这些工具不受我们的设备和网络速度的影响。

这些工具的使用方式,我们在浏览器性能指标中有过介绍,这里也不过多介绍.


3. 针对终点资源的处理

CSS 不太可能是性能问题的罪魁祸首。然而,它可能加载一些重量级的资源,这些资源可以在几分钟内进行优化。例如:

  • 服务器上启用 HTTP/2GZIP 压缩
  • 使用CDN来增加同时的 HTTP 连接数量,并将文件复制到世界各地的其他位置
  • 删除未使用的文件

Image通常是页面负重的最大原因,但许多网站未能有效优化:

  • 调整位图图像的大小。
  • 确保使用适当的文件格式。通常,照片最适合使用 WebP 格式,矢量图像使用 SVG 格式,其他内容使用 PNG 格式。
  • 使用图像工具通过删除元数据和增加压缩因子来减小文件大小。

图像数据的 xKB 不等于 CSS 代码的 xKB。二进制图像可以并行下载,并且在页面上放置时需要很少的处理。CSS 阻止渲染,浏览器在继续之前必须将其解析成对象模型。


4. 用 CSS 效果替换图片

很少需要为边框、阴影、圆角、渐变和一些几何形状使用背景图像。使用 CSS 代码定义image所需的带宽要少得多,并且以后更容易进行修改或动画处理。

以下是如何使用CSS代码来创建圆角效果和渐变效果:

  1. 圆角效果
.rounded-box {
  width: 200px;
  height: 150px;
  background-color: #f0f0f0;
  border-radius: 10px; /* 使用border-radius属性创建圆角效果 */
}
  1. 渐变效果
.gradient-background {
  width: 300px;
  height: 200px;
  /* 使用linear-gradient()函数创建渐变背景 */
  background: linear-gradient(to bottom, #ff9900, #ff3300); 
}

通过上述示例,我们可以看到如何使用CSS属性和函数来创建圆角效果和渐变效果,而无需使用背景图像。这种方法不仅减少了网络请求和带宽消耗,还使样式更易于修改和维护。

类似地,对于其他元素(如阴影、边框等),我们也可以使用CSS的相关属性来实现样式效果,而无需依赖背景图像。这种做法有助于提高性能并保持代码的可维护性。


5. 删除不必要的字体

诸如Google Fonts之类的服务使将自定义字体添加到任何页面变得容易。然而,一两行代码可能会检索数百KB的字体数据。建议如下:

  1. 只使用我们所需要的字体。
  2. 仅加载所需的字重和样式,例如正常字体、400字重、无斜体。
  3. 在可能的情况下,限制字符集。
  4. 考虑使用可变字体,它通过插值定义多个字重和样式,从而使文件更小。
  5. 考虑使用操作系统字体。

上面的有些方式比较简单,就不展开说明,我们来简单介绍一下--可变字体.

可变字体(Variable Fonts)

可变字体(Variable Fonts)是一种字体技术,允许在单个字体文件中包含多个字重(粗细)和字形(样式)的变化。通过调整不同的轴(Axis),我们可以实时控制字体的多个属性,如粗细、宽度、倾斜度等,从而创造出更加灵活和多样化的字体效果。这种技术使得字体的定制和排版变得更加自由和创意。

选择一个技术,首先需要看这个技术的是否被大众采纳. 我们通过Can I use进行查看,发现它是被大众认可的.

CSS 20大酷刑

下面是一个示例,演示了如何使用可变字体:

/* 引入可变字体 */
@font-face {
  font-family: 'CustomVariableFont';
  src: url('path-to-font/custom-variable-font.woff2') format('woff2-variations');
}

/* 应用可变字体 */
body {
  font-family: 'CustomVariableFont', sans-serif;
  font-weight: 300; /* 字重为轻 */
  font-variation-settings: 'wght' 700, 'slnt' -10, 'wdth' 100; /* 控制字体属性 */
}

h1 {
  font-family: 'CustomVariableFont', sans-serif;
  font-weight: 700; /* 字重为粗 */
  font-variation-settings: 'wght' 700, 'slnt' 0, 'wdth' 100; /* 控制字体属性 */
}

在上面的示例中,我们首先通过@font-face规则引入了可变字体文件。然后,我们使用font-family来定义字体系列,并通过font-weightfont-variation-settings来控制字体的属性。在font-variation-settings中,我们可以指定不同轴的属性值,如'wght'代表字重,'slnt'代表倾斜度,'wdth'代表宽度。这些属性值可以根据具体的字体文件和需求进行调整。

如果想了解更多关于可变字体,可以参考google 文档


6. 避免使用 @import

@import 是一种CSS规则,用于在一个CSS文件中引入另一个CSS文件。通过使用@import,我们可以将多个CSS文件合并成一个文件,以便更好地组织和管理样式。。例如:

/* main.css */
@import url("base.css");
@import url("layout.css");
@import url("carousel.css");

这似乎是一种加载较小组件和字体的合理方式。但实际上不是。@import规则可以嵌套,因此浏览器必须逐个加载和解析每个文件

虽然@import在代码模块化(将样式分成多个文件,每个文件负责不同的样式部分,使得代码更模块化和易于维护)和组织性(可以更好地组织和管理代码,将相关的样式放在一起,提高代码的可读性和可维护性)。上有很高的建树.

但是呢,它的缺点也很明显.

  • 性能问题:使用@import会导致多个HTTP请求,每个@import都会阻塞页面的加载,影响页面性能。这是因为浏览器需要等到导入的样式加载完毕后才能继续加载页面的其余部分。
  • 阻塞渲染:由于@import会阻塞页面的加载,导致页面的渲染时间延长,用户可能会看到白屏。

替代方案

  1. <link>标签:使用<link>标签在HTML的<head>部分直接引入外部CSS文件。这种方法不会阻塞页面加载,同时可以并行加载多个CSS文件。
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="carousel.css">
  1. CSS预处理器:使用CSS预处理器(如Sass、Less、Stylus)来管理样式文件,通过预处理器的导入功能将多个部分的样式文件合并成一个,最终编译为一个CSS文件。

    • 如果在使用了预处理器后,还想使用类似@import的功能,我们可以使用@use(在Sass中使用)
  2. 模块化构建工具:使用模块化构建工具(如Webpack、vite、Rollup)来管理样式,通过构建工具的功能将多个样式文件合并、压缩,然后输出为一个优化后的CSS文件


7. 合并CSS

大多数构建工具(如WebpackViteRollup)允许我们将所有部分组合成一个大的CSS文件,其中不必要的空格、注释和字符都被删除了。

构建工具

Webpack 5 和 Vite 都是现代前端构建工具,它们都能够有效地管理样式。以下是它们分别管理样式的简要描述和示例代码:

Webpack 5 管理样式:

Webpack 5 是一个功能强大的构建工具,可以用于管理和打包各种资源,包括样式文件。

  1. 安装依赖:首先,我们需要在项目中安装Webpack和相应的样式加载器,例如 style-loadercss-loader

  2. 配置样式加载器:在Webpack配置文件中,我们可以配置不同类型的样式加载器,例如处理CSS、Sass、Less等。我们还可以使用 MiniCssExtractPlugin 插件将样式提取为单独的CSS文件。

// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  // ... 其他配置项 ...

  module: {
    rules: [
      {
        test: /\.css$/, // 匹配以 .css 结尾的文件
        use: [
          MiniCssExtractPlugin.loader, // 使用 MiniCssExtractPlugin 将样式提取为单独的文件
          'css-loader', // 解析 CSS
          'postcss-loader', // 可选,用于处理 CSS 前缀等
        ]
      },
      // 添加其他样式格式的规则(如 Sass、Less 等)
    ]
  },

  plugins: [
    new MiniCssExtractPlugin({
      filename: 'styles.css' // 输出的提取后的样式文件名为 styles.css
    })
  ]
};
  1. 在应用中引入样式:在我们的应用代码中,我们可以直接引入样式文件,Webpack 会处理并将其合并到输出文件中。
// app.js
import './styles.css';

Vite 管理样式:

Vite 是一个基于ESM的构建工具,它具有快速的冷启动和实时更新能力,适用于开发环境。

  1. 安装依赖:我们需要在项目中安装 Vite。

  2. 配置样式:Vite 使用默认的样式预处理器,例如 CSS、Sass、Less,无需额外的配置。

  3. 在应用中引入样式:在我们的应用代码中,我们可以直接引入样式文件,Vite 会自动处理。

// app.js
import './styles.css';

Vite 会在开发过程中使用 HMR(热模块替换)来实现实时更新。

可以看潜聊vite


在使用HTTP/2的情况下,连接不再那么必要,因为它可以对请求进行流水线和多路复用。在某些情况下,如果我们有较小且经常更改的CSS资源,分开的文件可能会有益。然而,大多数网站可能会从发送一个立即由浏览器缓存的单个文件中受益。

当启用GZIP时,缩小可能不会带来显着的好处。尽管如此,实际上并没有什么不利之处。


8.使用现代布局技术

多年来,使用CSS浮动来布局页面是必要的。这种技术是一种“黑科技”。它需要大量的代码和边距/填充微调来确保布局正常工作。即使如此,在较小的屏幕尺寸下,浮动也会出现问题,除非添加了媒体查询。

现代的替代方案有:

  1. CSS Flexbox 用于一维布局,可以根据每个块的宽度换行到下一行。Flexbox非常适用于菜单、图像画廊、卡片等。

  2. CSS Grid 用于具有显式行和列的二维布局。Grid非常适用于页面布局。

这两个选项都更容易开发,使用更少的代码,可以适应任何屏幕尺寸,并且比浮动渲染得更快,因为浏览器可以本地确定最佳布局。

这里就不得不墙裂推荐,阮一峰大佬写的关于Flex/Gird的教程了.


9. 为CSS瘦身

最可靠且速度最快的代码就是我们永远不需要编写的代码!样式表越小,下载和解析速度就越快。

所有开发人员都以良好的意图开始,但随着功能数量的增加,CSS可能会变得越来越庞大。保留旧的、不必要的代码比删除它并冒着破坏某些东西的风险要容易。以下是一些建议供考虑:

  • 谨慎使用大型CSS框架。

  • 将CSS组织成具有明确职责的较小文件(部分文件)。

  • 考虑使用诸如BEM之类的命名方法,以帮助开发独立的组件。

  • 避免深层嵌套的Sass等预处理器声明。扩展后的代码可能会意外地变得很大。

  • 避免使用!important来覆盖样式。

  • 避免在HTML中使用内联样式。

由于现在是前端框架的天下,所以在进行CSS瘦身时,离不开构建工具的处理.

下面我们就针对Webpack4/Webpack5/Vite如何进行CSS瘦身做一次简单介绍

Webpack 4

Webpack 4通常使用purgecss-webpack-plugin插件来删除无用的CSS。该插件会根据代码中实际使用的类名,从构建后的CSS中移除未使用的样式。

安装插件:

npm install purgecss-webpack-plugin --save-dev

在Webpack配置中使用插件:

const PurgeCSSPlugin = require('purgecss-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    new PurgeCSSPlugin({
      paths: glob.sync(['src/**/*.js'], { nodir: true }),
    }),
    // ...
  ],
};

这将根据项目中的JavaScript文件中实际使用的类名,从构建后的CSS中删除未使用的样式。

purgecss-webpack-plugin需要与mini-css-extract-plugin 配合使用,才会达到最佳效果, 具体配置可以参考purgecss-webpack-plugin用法

Webpack 5

Webpack 5中,无用CSS的删除通常是内置的特性,不需要额外的插件。Webpack 5会自动分析代码并从构建后的CSS中删除未使用的样式。

这个特性的底层原理涉及Webpack的代码分析功能和Tree Shaking技术:

  1. 代码分析功能:Webpack 5能够分析整个项目的代码结构,包括入口文件、依赖关系和引用关系。它可以检测哪些CSS类名在项目的JavaScript文件中被实际使用,以及哪些未被使用。

  2. Tree Shaking:这是一种用于剔除不使用的代码的优化技术,它会在打包过程中移除不会被执行的代码。在CSS中,这意味着Webpack 5会识别哪些CSS样式类在JavaScript代码中没有被引用,然后将这些未使用的样式从构建后的CSS中删除。

底层原理的实现涉及Webpack的构建流程和代码解析算法。Webpack会从入口文件开始,递归地分析所有依赖的JavaScript文件,同时识别哪些CSS类名在实际代码中被使用。一旦确定了未使用的CSS类名,Webpack就会在构建最终的CSS文件时将其删除,从而减少输出的文件大小。

Vite

Vite是一个基于ES模块的前端构建工具,它在开发模式下通过ES模块的引用关系来实现无用CSS的删除。

在Vite中,无用CSS的删除是默认的特性,不需要额外的配置。Vite会在开发模式下自动删除未使用的CSS。


10. 坚持使用层叠特性

CSS-in-JS的兴起使开发人员能够避免使用CSS全局命名空间。通常,在构建时会创建随机生成的类名,从而使组件之间不可能发生冲突。

如果CSS-in-JS改善了我们的工作流程,那么继续使用它是可以的。然而,了解CSS级联的好处也是值得的,而不是在每个项目中都与之对抗。例如,我们可以设置默认字体、颜色、大小、表格和表单字段,这些样式会统一应用于单个位置中的每个元素。很少有必要在每个组件中声明每个样式。


11. 简化选择器

即使是最复杂的CSS选择器也只需要几毫秒来解析,但减少复杂性会减小文件大小并帮助浏览器解析。

假设有如下的页面结构,现在我们想要选择下载PDF的对应的元素.

<body>
  <main class="main">
    <section class="first">
      <h2>前端</h2>
      <h2>柒八九</h2> <!-- 符合 nth-of-type(odd) 条件 -->
      <p>
        <span>第一行内容</span>
        <a href="document.pdf">下载 PDF</a> <!-- 符合 [href$=".pdf"] 条件 -->
      </p>
      <p>
        <span>其他内容信息</span>
        <a href="image.jpg">下载 Image</a>
      </p>
    </section>
  </main>
</body>

如果有同组的小可爱给你写下面的选择器,就问你看到这个代码迷糊不迷糊. CSS 20大酷刑

body > main.main > section.first h2:nth-of-type(odd) + p::first-line > a[href$=".pdf"]

本着人道主义的关怀,我还是给你浅浅的解释一下每段代码的含义哇.

  • body: 选择文档中的<body>元素。
  • >: 选择直接子元素的关系符。
  • main.main: 选择类名为main<main>元素。
  • >: 再次使用直接子元素的关系符。
  • section.first: 选择类名为first<section>元素。
  • h2:nth-of-type(odd): 选择是奇数序号的<h2>元素。这里假设<h2>元素是<section>的子元素,并且是奇数序号的。
  • +: 选择紧接在前一个元素后的元素。
  • p::first-line: 选择<p>元素的第一行。
  • a[href$=".pdf"]: 选择链接的<a>元素,其中链接的href属性以.pdf结尾。

同样,要谨慎使用像Sass这样的预处理器中的深层嵌套,因为这可能会无意中创建复杂的选择器。


12. 警惕耗时的属性

某些属性的渲染速度比其他属性要慢。如果想要增加页面的不流畅感,可以尝试在所有元素上添加盒子阴影!

*, ::before, ::after {
  box-shadow: 5px 5px 5px rgba(0,0,0,0.5);
}

浏览器性能会有所不同,但总体来说,在绘制之前引起重新计算的任何操作都会在性能方面造成更大的开销:

有些属性在绘制之前引起重新计算的原因是因为它们会影响元素的布局外观,导致浏览器需要重新计算并重新渲染受影响的部分。

  1. border-radiusborder-radius属性用于设置元素的圆角边框。当更改此属性时,元素的形状会发生变化,可能会影响元素的周围元素的位置和排列,从而引起重新计算。

  2. box-shadowbox-shadow属性用于添加元素的阴影效果。更改此属性会影响元素的可视外观,可能导致元素的尺寸和位置发生变化,从而引起重新计算。

  3. opacityopacity属性用于设置元素的透明度。更改此属性会影响元素的可视外观,可能会导致元素的尺寸和位置发生变化,从而引起重新计算。

  4. transformtransform属性用于应用元素的2D或3D转换效果,如旋转、缩放和平移。修改此属性可能会改变元素的位置、形状和大小,导致重新计算。

  5. filterfilter属性用于应用元素的图像滤镜效果,如模糊、对比度调整等。更改此属性可能会影响元素的可视外观,导致重新计算。

  6. position: fixed: 使用position: fixed将元素固定在视口中的特定位置,不会随页面滚动而移动。由于这会影响元素的定位,所以更改此属性可能会影响周围元素的位置和布局,从而引起重新计算。


13. 采用 CSS 动画

原生的CSS过渡和动画始终比使用JavaScript修改相同属性的效果要快

然而,不要仅为了效果而使用动画。微妙的效果可以提升用户体验,而不会对性能产生不利影响。过多的动画可能会拖慢浏览器,并导致部分用户出现晕动感。


14. 避免为耗时的属性制作动画

对元素的尺寸或位置进行动画处理可能会导致整个页面在每一帧上重新布局。如果动画只影响合成阶段,性能可以得到改善。

这里就不得不提一下,合成层的概念了.

合成层

只有一些特殊的渲染层才会被提升为合成层,通常来说有这些情况:

  1. transform:3D变换:translate3dtranslateZ
  2. will-change:opacity | transform | filter
  3. opacity | transform | fliter 应用了过渡和动画(transition/animation
  4. video、canvas、iframe

浏览器通常使用硬件加速的GPU来渲染这些效果。

关于这个可以看我们之前写的硬件加速


15. 指明哪些元素将成为动画

will-change 是一个用于优化性能的CSS属性,它允许开发者预先告知浏览器某个元素将会发生变化,从而让浏览器可以做出相应的优化准备。这可以帮助避免一些不必要的渲染和计算,提高页面的流畅性和响应速度。

具体来说,will-change 属性可以应用于一个或多个CSS属性,告知浏览器这些属性可能会在未来的某个时间点发生变化。浏览器可以根据这些信息进行一些优化,例如为元素创建独立的图层,从而在元素发生变化时只重新渲染图层,而不影响整个页面的渲染。

以下是使用示例:

.myelement {
  will-change: transform, opacity;
}

在上面的示例中,我们告知浏览器 .myelement 元素的 transformopacity 属性可能会发生变化。浏览器可以根据这些信息做出优化,例如将该元素放置在独立的图层中,以便在这些属性发生变化时能够更高效地进行渲染。

需要注意的是:

  • will-change 应该作为一种优化手段,而不是滥用。不要随意将所有元素都添加 will-change 属性,因为这可能会导致不必要的内存消耗。
  • will-change 应该在元素需要变化之前的一段时间内添加,而不是立即添加,以便浏览器有足够的时间进行优化准备。
  • 一些浏览器可能会忽略 will-change,或者在某些情况下不起作用。因此,它应该被视为一种辅助性能优化手段,而不是必须的。

总之,will-change 属性可以帮助开发者在需要进行复杂动画或变换的情况下,提前通知浏览器进行性能优化,从而提高页面的响应性和流畅性。

需要注意事项

尽管 will-change 属性可以用于优化性能,但它并不是在所有情况下都会产生积极效果。在某些情况下,错误地使用 will-change 可能会导致性能问题,而不是改善。

以下是可能导致页面更卡顿的一些原因:

  1. 过度使用 will-change 如果过多的元素都被添加了 will-change 属性,浏览器可能会创建大量的图层,导致内存占用增加,反而降低了性能。因此,应该谨慎使用,并只在真正需要优化的元素上添加。

  2. 属性变化频繁: 如果一个元素上添加了 will-change 属性,但该属性的变化频率很高,浏览器可能需要不断地重新创建图层,造成性能开销。在这种情况下,使用 will-change 可能不是一个好的选择。

  3. GPU 资源受限: 一些设备可能具有受限的 GPU 资源,无法承载大量的图层和渲染操作。在这种情况下,添加过多的 will-change 属性可能会导致页面卡顿。

  4. 不合适的属性选择: 如果选择了不适当的属性添加到 will-change 中,浏览器可能会做出错误的优化。例如,添加了大量的 will-change: transform 属性,但只有少数元素实际需要变换,这可能会导致性能下降。

示例代码如下:

.myelement {
  will-change: transform;
  transition: transform 0.3s ease;
}

.myelement:hover {
  transform: translateX(20px);
}

在上面的示例中,当鼠标悬停在 .myelement 元素上时,会发生变换。如果元素频繁发生变换,而且同时使用了 will-change 和过渡效果,可能会导致页面卡顿。

因此,尽管 will-change 可以用于性能优化,但在使用时需要谨慎考虑上述因素,确保它被正确地应用在需要进行复杂变换或动画的元素上。如果添加了 will-change 后出现了性能问题,可以尝试移除它,看看是否有改善。


16. 采用 SVG 图像

可缩放矢量图形(SVG)通常用于标识、图表、图标和简单的图示。与JPG和PNG位图不同,SVG不会定义每个像素的颜色,而是在XML中定义形状,如线条、矩形和圆圈。例如:

<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 800 600">
  <circle cx="400" cy="300" r="50" stroke-width="5" stroke="#f00" fill="#ff0" />
<svg>

简单的SVG比等效的位图要小,并且可以无限缩放而不会失去清晰度。

SVG可以直接内联在CSS代码中作为背景图像。这对于较小、可重复使用的图标非常理想,并且避免了额外的HTTP请求。例如:

.mysvgbackground {
  background: url('data:image/svg+xml;utf8,<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 800 600"><circle cx="400" cy="300" r="50" stroke-width="5" stroke="#f00" fill="#ff0" /></svg>') center center no-repeat;
}

17. 用 CSS 制作 SVG 样式

更常见的情况是,SVG直接嵌入到HTML文档中:

<body>
  <svg class="mysvg" xmlns="https://www.w3.org/2000/svg" viewBox="0 0 800 600">
    <circle cx="400" cy="300" r="50" />
  <svg>
</body>

这将SVG节点直接添加到DOM中。因此,所有的SVG样式属性都可以使用CSS来应用:

circle {
  stroke-width: 1em;
}

.mysvg {
  stroke-width: 5px;
  stroke: #f00;
  fill: #ff0;
}

嵌入的SVG代码量减少,CSS样式可以根据需要进行重用或动画处理。

请注意,将SVG放在<img>标签内或作为CSS背景图像使用会将它们与DOM分离,CSS样式将不会产生影响。


18. 避免使用 Base64 位图图像

标准的位图文件(JPG、PNG和GIF)可以在数据URI中编码为base64字符串。例如:

.myimg {
  background-image: url('data:image/png;base64,ABCDEFetc+etc+etc');
}

然而,不幸的是:

  • base64编码通常比其二进制等效物要大约30%;
  • 浏览器必须在使用之前解析字符串;
  • 修改图像会使整个(缓存的)CSS文件无效。

尽管减少了HTTP请求,但它很少提供明显的好处,特别是在HTTP/2连接下。通常情况下,避免内联位图,除非图像不太可能经常更改,且生成的base64字符串不太可能超过几百个字符。


19. 考虑关键 CSS

那些使用谷歌页面分析工具的人通常会看到建议“内联关键CSS”或“减少渲染阻塞的样式表”。加载CSS文件会阻塞渲染,因此可以通过以下步骤来提高性能:

  1. 提取用于渲染视窗上方元素的样式。
  2. 将这些样式添加到HTML的<head>元素中的<style>元素中。
  3. 使用JavaScript异步加载主要的CSS文件(可以在页面加载后加载)。

这种技术无疑可以提高性能,并且可能对具有一致界面的渐进式Web应用程序或单页面应用程序有所益处。


20. 考虑渐进式渲染

渐进式渲染(Progressive Rendering)是一种优化策略,旨在改善网页加载和渲染性能,以提供更好的用户体验。通过渐进式渲染,页面的内容可以在加载过程中逐步呈现给用户,使用户能够更快地看到页面的部分内容,而不必等待整个页面完全加载和渲染。

渐进式渲染的主要思想是将页面内容分为多个阶段,并在加载过程中逐步完成这些阶段,从而实现快速呈现。以下是渐进式渲染的一些关键概念和技术:

  1. 分段加载内容: 将页面内容划分为多个模块或组件,并按照优先级逐步加载。通常,首屏内容、核心内容和附加内容可以分别进行加载。

  2. 优先加载关键资源: 首先加载对页面呈现至关重要的关键资源,例如文本内容、主要图像和交互所需的脚本。这可以使用户更快地看到页面的主要内容。

  3. 延迟加载次要资源: 对于一些不是首要显示的资源,如下方的图像、广告、辅助内容等,可以采用延迟加载的方式,使页面更快地完成加载和呈现。

  4. 分块渲染: 将页面内容分为不同的块或区域,并在加载完成每个块后立即呈现。这样,即使页面的某些部分尚未完全加载,用户仍然可以浏览已经呈现出来的内容。

  5. 懒加载: 对于一些不在首屏或不在用户视线范围内的内容,可以使用懒加载技术。这意味着只有当用户滚动到相应区域时才加载内容,从而减少初始加载时间。

  6. 逐步呈现动画: 对于页面上的动画效果,可以使用渐进式呈现,以使动画更早地出现并逐步完善。这可以避免用户在等待动画加载时的空白时间。

分段加载内容

与使用单个整站CSS文件不同,渐进式渲染是一种为单独的组件定义独立样式表的技术。每个样式表会在HTML中引用组件之前立即加载:

<head>

  <!-- 适用于各个组件的核心样式 -->
  <link rel='stylesheet' href='base.css' />

</head>
<body>

  <!-- 头部组件 -->
  <link rel='stylesheet' href='header.css' />
  <header>...</header>

  <!-- 主要内容 -->
  <link rel='stylesheet' href='content.css' />
  <main>

    <!-- 表单样式 -->
    <link rel='stylesheet' href='form.css' />
    <form>...</form>

  </main>

  <!-- 底部组件 -->
  <link rel='stylesheet' href='footer.css' />
  <footer>...</footer>

</body>

每个<link>仍然会阻止渲染,但时间较短,因为文件较小。页面会更早可用,因为每个组件按顺序渲染;页面顶部的内容可以在剩余内容加载时被查看。

懒加载

假设我们有一个包含多个段落的网页,我们将通过分块加载和渲染逐步显示这些段落。

<!DOCTYPE html>
<html lang="en">
<head>
  <style>
    /* 初始化时只显示第一个段落 */
    .hidden {
      display: none;
    }
  </style>
  <script>
    // 模拟页面加载和渲染
    function simulateProgressiveRendering() {
      const paragraphs = document.querySelectorAll('.hidden');
      let currentIndex = 0;

      function showNextParagraph() {
        if (currentIndex < paragraphs.length) {
          paragraphs[currentIndex].style.display = 'block';
          currentIndex++;
          requestAnimationFrame(showNextParagraph);
        }
      }

      // 开始加载和渲染
      showNextParagraph();
    }
  </script>
</head>
<body onload="simulateProgressiveRendering()">
  <p class="hidden">段落1</p>
  <p class="hidden">段落2</p>
  <p class="hidden">段落3</p>
  <p class="hidden">段落4</p>
</body>
</html>

渐进式渲染可能对大型网站有益,其中每个页面由不同组件的选择构建。


寄语

针对前端开发工程师来说, CSS是我们再熟悉不过的老朋友了,但是由于它体系的庞杂和多边性,导致我们总是有一种力不从心的感觉.

但是,如果你要想成为一个合格的前端工程师,CSS的也是一道必选题. 我们需要不断的去理解不断的去实践.

这里,就不得不推荐,张鑫旭大佬的书籍了. 有时候,不是我们不用功,而是我们陷入了一种弯路.


后记

分享是一种态度

全文完,既然看到这里了,如果觉得不错,随手点个赞和“在看”吧。

CSS 20大酷刑

转载自:https://juejin.cn/post/7273047512297553956
评论
请登录