前端界有哪些越早知道越好的小技巧、小知识?

6个月前 (11-16) 0 点赞 0 收藏 0 评论 12 已阅读

来分享下 鹅厂前端开发工程师 @jesonliao 总结的当前前端界的些新知识。

本文介绍一些 Web 平台的新技术。这些技术不只是尝鲜,而是已经达到可用阶段,用到生产实践中去可以切实提升开发效率和用户体验。本文内容主要选取自 2022 Google I/O 大会的视频 What's new for the web platform。

目录

accent-color<dialog>datetime-localBack/forward cacheloading="lazy"aspect-ratioCSS Containmentcontent-visibilityPriority Hintssize-adjustWebAuthnstructuredCloneTop level awaitPrivate filedsarray.at()SharedArrayBufferCSS cascade layersCSS :has()参考

accent-color

介绍

新增 CSS 属性accent-color,用于修改表单元素的颜色。

表单元素例如<input> <checkbox> <radio>等,它们的样式是由用户代理样式表(user agent stylesheet)决定的,各自浏览器的实现都不一样。

浏览器自带的样式都比较朴实无华,而且修改表单样式是很困难的。以前的做法通常是在表单元素上覆盖或者包裹一些普通元素(div、span)来定制样式。比如 Ant Deisign [1]里的 CheckBox 就是盖了个<span>标签来控制样式。

用法

有了accent-color就可以更方便地给表单元素设置主题色。例如给所有表单元素设置 deeppink 颜色,只需要在根元素应用该属性:

::root {  accent-color: deeppink;}

使用系统 API 的好处是浏览器会帮你处理好很多状态,这是自定义 UI 容易忽略的问题。例如可以自动适配暗黑模式。下面是添加accent-color: deeppink后分别在 light 和 dark 模式下的表现。

兼容性

<dialog>是原生支持的对话框元素,用法跟很多 UI 组件库里的 Modal、Dialog 组件类似。

用法

<dialog id="dialog">  <form method="dialog">    <p>Hi, I'm a dialog.</p>    <button>OK</button>  </form></dialog><button onclick="dialog.showModal()">Open Dialog</button>

默认<dialog>元素是隐藏的,可以通过设置 HTML 属性open=true,或者使用 .show().showModal()来展示对话框内容。

如果<dialog>元素里有<form>表单元素并且设置了method=dialog,那么提交 form 表单时会关闭对话框并且把表单内容作为返回值。

优点

原生实现的<diglog>有几大优点:

完善的无障碍能力;
完备的焦点处理。例如关闭对话框后能回到上一个焦点上;打开后自定聚焦到表单元素上;
强大的样式支持。通过 CSS 能快速实现动画、屏幕自适应等;

总之我们不再需要第三方库,也不用写复杂的控制脚本,就能实现对话框功能了。现在所有浏览器的最新版本都已经支持<dialog>元素。

兼容性

datetime-local

介绍

<input>标签拥有了一种新的类型:datetime-local,用于同时输入日期和时间。

<input type="datetime-local">

目前用用于输入时间的控件有三种:

type=date:输入日期
type=time:输入时间
type=datetime-local:输入日期和时间

此外它还支持这些属性:

max:最大时间
min:最小时间
step:使用递增时的间隔

兼容性

浏览器支持程度已经相当高,又少了一项使用第三方 UI 组件库的必要。

Back/forward cache

介绍

Back/forward cache 简称 bfcache,是 Chrome 浏览器的一项优化。页面在离开时会先被缓存,浏览器前进或者后退操作时能直接使用。Firefox 和 Safari 多年前就支持了,Chrome 终于跟上。

左边是支持bfcache后,优化效果非常明显

页面在进入 bfcache 时,所有页面状态会被“冻结”,页面快照(包括 JS 运行状态)被放入在内存中,关闭浏览器后缓存会销毁。

在进入和离开页面时会触发事件pageshowpagehide,并且加了persisted属性来区分是页面是否使用 bfcache。

window.addEventListener('pagehide', (event) => {  if (event.persisted) {    console.log('页面进入后台,bfcache 生效');  } else {    console.log('页面被销毁了,bfcache 未生效');  }});window.addEventListener('pageshow', (event) => {  if (event.persisted) {    console.log('页面从 bfcache 中恢复');  } else {    console.log('页面正常加载');  }});

用法

这是浏览器做的优化,理论上页面开发者不需要进行额外操作就可以享受。但是并非所有页面都能支持 bfcache,要使 bfcache 生效,有以下几条准则。

1. 不要使用 unload 事件

unload事件的原意是建立在页面被销毁的基础上,如果页面触发了unload后进入了 bfcache 而没有被销毁,可能会导致很多已有的页面功能不符合预期。为了不让 bfcache 造成 breaking change(破坏性改动),浏览器检测到监听了unload事件的页面就不会启用 bfcache。 可以使用pagehide事件来替代unload,无论 bfcache 是否生效,pagehide事件都会触发。

2. 有条件地使用 beforeunload 事件

限制beforeunload事件的原因跟unload事件一样。但是我们有时必须使用到beforeunload事件,例如在用户填写了表单还未提交的情况下离开页面,我们可以通过拦截beforeunload事件给用户一个提示:“页面未保存,是否离开?”。 解决办法是要有条件地监听beforeunload事件,而且一定要及时取消监听。

function beforeUnloadListener(event) {  event.preventDefault();  return event.returnValue = 'Are you sure you want to exit?';};// A function that invokes a callback when the page has unsaved changes.onPageHasUnsavedChanges(() => {  window.addEventListener('beforeunload', beforeUnloadListener);});// A function that invokes a callback when the page's unsaved changes are resolved.onAllChangesSaved(() => {  window.removeEventListener('beforeunload', beforeUnloadListener);});

3. 避免引用 window.opener

使用window.open()方法打开的页面,可以通过window.opener访问前一个页面。例如:在 window A 中打开了 window B,B.opener 返回 A。我们称页面 B 具有window.opener[2]的引用,这种页面无法被放入 bfcache 中。如果一定要用window.open()方法打开页面,可以加上

4. 页面离开前记得关闭连接

浏览器检测到页面在离开时有以下连接存在的话,就不进入 bfcache:

连接中的 indexDB正在进行的 fetch 或者 XMLHttpRequest 请求连接中的 WebSocket 或者 WebRTC

可以在pagehide事件时关闭以上连接。

Chrome DevTools 里面可以看到当前页面是否支持 bfcache。打开Application > Back-forward Cache面板,点击“Run Test”按钮,如果提示“Restored from back-forward cache”就代表成功使用 bfcache。

loading="lazy"

介绍

Lazy-loading 俗称懒加载,指的是当页面滚动到元素的位置时才开始加载。

这是前端常用的加载速度优化手段之一,以前需要借助第三方库来实现,通过监听滚动事件不断计算元素的位置,对性能会有影响。

现在<img><iframe>都原生支持属性loading="lazy"啦,由浏览器来处理就不用再担心性能问题。

用法

loading属性有以下值:

eager :默认值。立刻加载资源;
lazy:延迟加载,等元素即将出现在视窗时再加载;

<img src="image.png" loading="lazy" alt="…" width="200" height="200">

使用loading="lazy"时应该注意:

<img>标签应该包含widthheight属性,这样浏览器才能准确计算元素位置,从而判断加载时间。
避免在第一屏使用loading="lazy",这样可能会导致“负优化”效果。

兼容性

aspect-ratio

介绍

设置一个宽高比固定的元素在以前是一件麻烦的事情,新增的 CSS 属性aspect-ratio就是为了解决这个问题。

以前的做法是利用 padding-top的百分比特性。当设置padding-top的单位是百分比时,它的参考对象是父元素的宽度。为了图片等元素应用宽高比,我们还需要一个额外的容器元素。

比如我们要实现一个图片的宽度自适应且宽高比固定为 16:9。

<div class="parent">  <div class="container">    <img src="" alt="">  </div></div>

使用padding-top,需要靠容器和绝对定位:

/* 16:9 比例容器 */.container {  width: 100%;  padding-top: 56.25%; /* 9/16 */  background-color: pink;  position: relative;}.container img {  width: 100%;  height: 100%;  position: absolute;  top: 0;  left: 0;}

使用aspect-ratio就要简单得多了:

.container img {  width: 100%;  aspect-ratio: 16 / 9;}

用法

aspect-ratio可以用在任意元素中,格式为<number> / <number>,即宽度和高度的比例。

值得注意的是aspect-ratio的优先级比较低。当aspect-ratio和其他属性例如width heightmin-widthmin-height产生了冲突的话,会以后者为准。

兼容性

CSS Containment

介绍

CSS Containment 是为了提升页面性能的一项 W3C 规范,定义了 CSS 属性 contain[3]

**MDN**[4]: CSS Containment 主要是通过允许开发者将某些子树从页面中独立出来,从而提高页面的性能。如果浏览器知道页面中的某部分是独立的,就能够优化渲染并获得性能提升。 这个定义通过一个 CSS 属性 contain[5]来实现。

这个属性让开发者可以指定布局和渲染的边界,帮助浏览器尽可能减少重排和重绘。主要功能为:

通过隔离子树(isolating subtree)提升渲染性能;
修改子树内容不会影响到外部

用法

CSS Containment 规范定义了contain属性有以下值:

layout:该元素的内部布局与页面的其他部分完全隔离,内部不受外界任何东西的影响,同时也影响不了外部。
paint:内部元素的绘制不会超出该元素,超出的部分会不可见。
size:该元素的渲染不用去检测内部元素,即跟内部元素的尺寸无关。
style(不常用):设置 counters[6]quotes[7]两个属性不会影响到外部。

还有另外两个值,是其他属性的组合:

content layout + paint 的结合
strict layout + paint + size 的结合

示例

假如页面中有 10000 个元素这样的元素:

<div class="item">  <div>Lorem ipsum...</div></div>

如果你修改了第一个元素的内容(例如把“Lorem ipsum”改成“Hello World”),浏览器就要遍历整个 DOM 树后重新绘制。而如果你给每个元素加上 contain: strict属性,那么浏览器只需要重绘你修改的元素。

这样对页面性能的提升是非常大的,可以打开这个DEMO 页面[8]感受一下。

兼容性

content-visibility

介绍

CSS 属性content-visibily也是CSS Containment[9] 最新草案的一部分,用于优化页面渲染速度。

设置content-visibbily属性可以暂时跳过元素的渲染(包括布局和绘制),直到它需要被用到的时候再进行渲染。通过这个属性,我们可以实现优先渲染首屏内容,页面其他部分先暂停渲染,这样可以极大地加速首屏展示时间,让用户更快体验到页面。

用法

content-visibility具有三个值:

visible:默认值,不产生影响,元素正常渲染。
hidden:该元素内容会被跳过。
auto:当元素不可见(且没有交互操作)时会跳过元素内容的渲染,需要的时候再渲染。

auto是最常用来优化的值。它首先会让元素的内容独立渲染,相当于使用了前面说到的contain: layout + paint + style;当元素不可见的时候,还会有contain: size的效果(当前元素的渲染不用去检测子元素)。简单来说就是,当元素离屏的时候不渲染(包括 layout 和 paint)元素内容,也即跳过。当元素出现在视图中时,浏览器会移除contain: size属性并开始渲染内容。

设置了content-visibility: auto的元素,在离屏时只是不会渲染,但是会在 DOM 里,可以通过 DOM API 或者网页搜索功能找到。

content-visibility: hidden代表不渲染该元素。它和display: none还有visibility: hidden有什么区别呢?

display: none:DOM 树中移除该元素,layout 和 paint 都不参与。缺点是切换到展示状态时需要的代价较大。
visibility: hidden:DOM 树中保留该元素,且会参与 layout,只是在图形绘制上隐藏。切换到展示状态需要的代价较低。但是由于该元素需要参与 layout,内容变更时会影响到外部,所以总体页面的渲染耗时并没有减少。
content-visibility: hidden:DOM 树中保留该元素,不参与 layout 和 paint,页面渲染时会跳过该元素。但是它可以保留渲染状态,切换到展示状态时的代价很低。

示例

content-visibility: auto 可以用于优化首屏渲染速度,可以给内容较长、图片较多的页面,分区设置该属性。这篇介绍文章[10]的示例中提到,将content-visibility: auto应用于分块内容区域,在初始加载时可获得 7 倍的渲染性能提升。

content-visibility: hidden可以用于虚拟滚动列表、单页应用(SPA)的路由切换。可以减少渲染性能损耗,当需要展示时又可以快速渲染出来。

兼容性

Priority Hints

介绍

Priority Hints(优先级提示)用于告诉浏览器相关资源的优先级,让浏览器调整资源的加载顺序从而优化页面的加载体验。Priolity Hints 的内容包括 HTML 标签属性fetchpriority和 JavaScript 的 Fetch API 里的priority参数。

优先级提示是对现有浏览器资源加载优先级的补充。浏览器计算资源的优先级时会考虑这些因素:

资源类型,比如 CSS、Font、Scripts、Images 等等不同类型资源具有不同的优先级。
引用资源的代码顺序。
使用preload标签属性。
使用asyncdefer标签属性。

当以上因素对资源优先级控制的精细度不够时,就需要用到 Priority Hints。Priority Hints 可以认为是在同等优先级的基础上进行调整。

用法

fetchpriority属性可以用在link, img, script, and iframe 标签上。它有三个选项值:

hign:希望浏览器提高该资源的优先级。
low:希望浏览器降低该资源的优先级。
auto(默认):由浏览器自己决定优先级。

Fetch API priority也可以传入上述三个选择值。

使用fetchpriority标签属性和 Fetch API 的priority 参数的示例如下:

<!-- We don't want a high priority for this above-the-fold image --><img src="/images/in_viewport_but_not_important.svg" fetchpriority="low" alt="I'm an unimportant image!"><!-- We want to initiate an early fetch for a resource, but also deprioritize it --><link rel="preload" href="/js/script.js" as="script" fetchpriority="low"><script>  fetch('https://example.com/', {priority: 'low'})  .then(data => {    // Trigger a low priority fetch  });</script><!-- The third-party contents of this iframe can load with a low priority --><iframe src="https://example.com" width="600" height="400" fetchpriority="low"></iframe>

兼容性

目前在只有基于 Chromium 的浏览器支持fetchpriority属性。

size-adjust

介绍

size-adjust是新增的 CSS@font-face描述符,作用是调整字形大小,以便让不同字体获得一致的表现。

为什么需要它?不同字体的字形大小是不一样的,即使设置了相同的font-size,效果也不一样。设置font-size: 64px后不同字体的展示效果如下:

用法

size-adjust需要放在@font-face内,用来修饰选定字体。接受一个百分比作为值,代表该字体的缩放比例。

size-adjust: <percentage>

示例

给 Arial 字体加上size-adjust属性后,字体放大了。

<h1>Size Adjust</h1><h1 class="adjusted">Size Adjust</h1>@font-face {  font-family: "Arial";  src: local(Arial);}@font-face {  font-family: "Size Adjusted Arial";  src: local(Arial);  size-adjust: 150%;}h1 {  font-size: 64;  border: 1px solid #999;}h1.adjusted {  font-family: "Size Adjusted Arial";}

兼容性

WebAuthn

介绍

WebAuthn,即 Web Authentication,一个用于在浏览器上进行认证的 API,是 W3C 的推荐标准(《Web Authentication: 一个用于访问公钥凭证的 API》[11])。

与传统的由服务端储存密码(或者密码的 hash)不同,WebAuthn 采用的是非对称加密的认证方式,服务端储存的是公钥,对应的私钥由客户端保管。

非对称加密的基础是一对公私和私钥,特点是公钥加密后的密文必须由私钥解密,反之私钥加密后必须由公钥解密。利用这个特点可以用来验证对方的身份,类似 HTTPS 的认证方式。流程大致如下:

注册阶段:由客户端(认证器)生成一对公私钥,服务器储存公钥,私钥存在客户端(认证器)中。
认证登录:服务器发送一段文本到客户端,客户端用私钥加密后再传给服务端,服务端用储存的公钥进行解密,并验证解密后的内容是否与原文一致。

认证器(Authenticator)有两类:

平台特定的认证器(Platform authenticators):由当前设备提供的认证方式,例如指纹识别、人脸识别等。
跨平台认证器(Cross platform authenticators):例如 USB Key,或其他支持 FIDO 协议的硬件设备。

可以在这里示例网站 https://webauthn.io/[12] 体验一下。

使用 WebAuthn 的优点有:

足够安全。整个认证过程私钥都存在客户端(认证器)中,没有传输丢失风险。而且即使服务端的公钥泄漏了,也不会造成影响。
方便。用户不需要记忆密码,可以借助设备的生物认证传感器直接登录。

用法

注册阶段通过浏览器接口navigator.credentials.create()创建秘钥。

const publicKeyCredentialCreationOptions = {    challenge: Uint8Array.from(        randomStringFromServer, c => c.charCodeAt(0)),    rp: {        name: "Duo Security",        id: "duosecurity.com",    },    user: {        id: Uint8Array.from(            "UZSL85T9AFC", c => c.charCodeAt(0)),        name: "lee@webauthn.guide",        displayName: "Lee",    },    pubKeyCredParams: [{alg: -7, type: "public-key"}],    authenticatorSelection: {        authenticatorAttachment: "cross-platform",    },    timeout: 60000,    attestation: "direct"};const credential = await navigator.credentials.create({    publicKey: publicKeyCredentialCreationOptions});

登录认证时通过接口navigator.credentials.get()获取校验内容。

const publicKeyCredentialRequestOptions = {    challenge: Uint8Array.from(        randomStringFromServer, c => c.charCodeAt(0)),    allowCredentials: [{        id: Uint8Array.from(            credentialId, c => c.charCodeAt(0)),        type: 'public-key',        transports: ['usb', 'ble', 'nfc'],    }],    timeout: 60000,}const assertion = await navigator.credentials.get({    publicKey: publicKeyCredentialRequestOptions});

兼容性

WebAuthn 的兼容性已经非常不错,可以在一些新兴的网站上使用。

structuredClone

介绍

新增函数structedClone()用于深拷贝

对于引用类型的值,拷贝方式分为浅拷贝和深拷贝,浅拷贝就是只复制了对象的外层属性,通常用扩展运算符进行操作:

const myOriginal = {  someProp: "with a string value",  anotherProp: {    withAnotherProp: 1,    andAnotherProp: true  }};// 浅拷贝const myShallowCopy = {...myOriginal};

深拷贝需要递归地复制每一层属性,一个简单的深拷贝函数可以用递归实现:

function deepCopyObj(obj) {    if (null == obj || "object" != typeof obj) return obj;    if (obj instanceof Date) {        var copy = new Date();        copy.setTime(obj.getTime());        return copy;    }    if (obj instanceof Array) {        var copy = [];        for (var i = 0, len = obj.length; i < len; i++) {            copy[i] = deepCopyObj(obj[i]);        }        return copy;    }    if (obj instanceof Object) {        var copy = {};        for (var attr in obj) {            if (obj.hasOwnProperty(attr)) copy[attr] = deepCopyObj(obj[attr]);        }        return copy;    }    throw new Error("Unable to copy obj this object.");}

自己写深拷贝函数需要考虑的情况很多,通常经不住考验。更推荐的做法是使用 lodash 里的_.deepCopy()函数。

还有更加加单直接的方式,就是直接使用JSON.parse(JSON.stringify())

const myDeepCopy = JSON.parse(JSON.stringify(myOriginal));

这种方式更像是一种作弊:把对象转换成了字符串,再转换回来。由于这种方式非常常用,所以 V8 引擎专门对JSON.parse()做了优化来加速这种拷贝方式。虽然执行效率不是问题,但这种方式还是有很多弊端。以下场景都不能用JSON.parse(JSON.stringify())

递归的数据结构:遇到递归的数据结构JSON.stringify()会抛异常;
一些内置 JS 类型:比如Map, Set, Date, RegExp or ArrayBuffer ,遇到这些类型JSON.stringify()也会抛异常;
函数:JSON.stringify()会忽略函数;

现在structuredClone()来了,内置的深拷贝函数,执行效率高,而且支持各种 JS 类型。

用法

const myDeepCopy = structuredClone(myOriginal);

structuredClone()支持拷贝递归的数据结构,所有原始类型(除了 Symbol)和绝大部分内置类型都能拷贝。但是它有以下限制:

不能拷贝函数;
不能拷贝 DOM nodes;
拷贝对象时会丢弃以下内容:
RegExp 对象的lastIndex属性;
属性的 descriptors(描述),例如 getters、setters、readable 等;
原型链不会被拷贝,新对象的 prototype 都会指向 Object;

structedClone()函数不是来自 ECMA 规范,而是一项 W3C 提案,但是各大 JS 平台都已经支持,包括Node.js

兼容性

Top level await

介绍

JavaScript 支持在最外层使用await关键字了。

以前,await必须放在acync函数体内,否则会报错。

await Promise.resolve(console.log(' '));<br/>// → SyntaxError: await is only valid in async function(async function() {  await Promise.resolve(console.log(' '));  // →  }());

现在可以直接在最外层使用await

await Promise.resolve(console.log(' '));<br/>// →  

用法

注意必须在模块中使用顶级的await,传统的脚本引入方式并不适用。

<script type="module">  import { processData } from "./utils.js";  const dataResponse = await fetch('/data');  processData(await dataResponse.json());</script>

兼容性

Private fileds

介绍

JavaScript 中的类(class)支持私有成员了。

在成员变量名前加上#符号标识该成员为私有。支持私有属性、私有方法以及静态私有属性和方法。注意不是用 private 关键字哦。

用法

// 私有属性class ClassWithPrivateField {  #privateField;}// 私有方法class ClassWithPrivateMethod {  #privateMethod() {    return 'hello world';  }}// 静态私有属性class ClassWithPrivateStaticField {  static #PRIVATE_STATIC_FIELD;}// 静态私有方法class ClassWithPrivateStaticMethod {  static #privateStaticMethod() {    return 'hello world';  }}

兼容性

array.at()

JavaScript 数组中新增了 at() 方法,传入下标获取该项的值。at()方法允许传入负数,代表从最后一个元素开始反向计数。

用法

const array = [5, 12, 8, 130, 44];console.log(array.at(1));// -> 12console.log(array.at(-2));// -> 130

array.at()array[]相比没有什么特别之处,最大的区别是前者可以传入负数。对于要取数组最后一个元素的操作会方便很多:

// 以前const last = array[array.length - 1];// 现在const last = array.at(-1);

兼容性

SharedArrayBuffer

介绍

SharedArrayBuffer即共享内存,可以实现不同线程之间共享内存,增加线程之间的通信效率。可用于 Web Worker 和 WebAssembly 的场景。

SharedArrayBuffer的历史很复杂。它早在 2017 年就被很多浏览器实现,但是在 2018 年因为幽灵漏洞[13]而被禁用。随后 Chrome 68(2018 年 7 月)重新启用了SharedArrayBuffer,通过站点隔离——给每个网页分配一个独立的进程——的方式来避免漏洞。但是这种方式消耗的资源过高所以只在 Chrome 桌面版中支持。到了 2020 年,新的标准提出了更安全的使用方式——在跨源隔离环境下才能使用SharedArrayBuffer

跨源隔离

为了减少某些 Web API 带来的漏洞风险,浏览器提供了一个可选加入的环境 ,称为“跨源隔离”(cross-origin isolated)。在这种环境下网页能够使用一些特权 API,包括SharedArrayBufferperformance.measureUserAgentSpecificMemory()等。

跨源隔离通过 COOP(跨源打开程序策略 )和 COEP (跨源嵌入器策略)来开启。你需要在返回主文档的 HTTP 中加上这两个头部字段:

Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp

跨源隔离环境下会有相应的限制:

COOP(跨源打开程序策略):当前页面使用window.open()方式打开一个不同源页面后,窗口之间无法进行交互。例如无法通过window.opener获得前一个窗口的引用。
COEP(跨源嵌入器策略):要求页面所引用的全部资源(包括 js、img、video 等)如果是不同源的话,必须经过明确授权。授权方式可以是使用 CORP 或 CORS。

用法

SharedArrayBufferArrayBuffer的接口一致,但是前者的内存地址是在共享内存区块中。在 Web Worker 中的使用方式如下:

// 主线程// 新建 1KB 共享内存const sharedBuffer = new SharedArrayBuffer(1024);// 主线程将共享内存的地址发送出去w.postMessage(sharedBuffer);// 在共享内存上建立视图,供写入数据const sharedArray = new Int32Array(sharedBuffer);// Worker 线程onmessage = function (ev) {  // 主线程共享的数据,就是 1KB 的共享内存  const sharedBuffer = ev.data;  // 在共享内存上建立视图,方便读写  const sharedArray = new Int32Array(sharedBuffer);  // ...};

兼容性

CSS cascade layers

介绍

CSS Cascade Layers,也叫做CSS 级联层,是Cascading and Inheritance Level5[14] 规范中新增的一个 @ 规则at-rule ),对应的 CSS 属性写法@layer。CSS 级联层是用来控制 CSS 权重(Specificity)的。

Cascade layers 可以理解为将 CSS 样式划分到不同的层上,层与层之间有权重比较,优先级更高的层会覆盖优先级低的层的样式。层内部的优先级按照原有的 CSS 选择器优先级进行计算。也就是说,如果层 A 的优先级比层 B 更低,那么定义在层 A 里的样式规则,无论优先级有多高,都比不过定义在层 B 里的样式规则。

通过将 CSS 合理地分层,可以防止意外的样式覆盖,并且可以更好地管理 CSS 的结构。

用法

使用层的方式很简单,将 CSS 样式规则用@layer <layer_name> {}嵌套起来就可以了。

@layer layer_name {  h1 {    color: blue;  }}

也可以不写名字,创建匿名层:

@layer {  h1 {    color: blue;  }}

可以先声明层,然后再定义层内的样式规则:

@layer base;@layer base {  # ...}

也可以一次性声明多个层:

@layer theme, layout, utilities;

还可以通过@import导入文件时创建层,这个非常有用:

@import './theme.css' layer(theme);

层的声明顺序代表了层的优先级。越后声明的层优先级越高。注意的是,未使用层的样式优先级高于使用了层的样式。

示例

一个最常见的用法是将不同功能的样式划分到不同的层上去。例如:

/* 预先建立层顺序,从最低到最高优先级 */@layer reset, theme, components, utilities;/* 重置 */@layer reset {}/* 主题样式 */@layer theme {}/* 组件样式 */@layer components {}/* 功能样式 */@layer utilities {}

更合理的组织方式是将样式放入不同的文件中,通过@import引入:

/* 预先定义层的顺序 */@layer base,       theme,       layouts,       components,       utilities;/* Base */@import '../styles/base/normalize.css' layer(base); /* normalize or rest file */@import '../styles/base/base.css' layer(base); /* body and base styles */@import '../styles/base/theme.css' layer(theme); /* theme variables */@import '../styles/base/typography.css' layer(theme); /* theme typography */@import '../styles/base/utilities.css' layer(utilities); /* base utilities *//* Layouts */@import '../styles/components/post.css' layer(layouts); /* post layout *//* Components */@import '../styles/components/cards.css' layer(components); /* imports card */@import '../styles/components/footer.css' layer(components); /* footer component */

兼容性

CSS :has()

介绍

CSS 伪类:has() 是一个功能非常强大的 CSS 选择器,它的含义是“选择包含选定元素的元素”。这将是 CSS 最重要的更新之一。

:has()伪类很早就出现在规范中,但是由于性能问题,规定只能在document.querySelector() 这样的 DOM 方法中使用。今年来浏览器开始大力支持,可以在 CSS 中直接使用了。因为它可以实现类似“父选择器”和“前面兄弟选择器”的功能,对 CSS 的开发会有颠覆性的影响。

用法

:has()接受一个选择器作为参数,如果某个元素有后代(相对于该元素的 :scope[15])能匹配传入的选择器,那么该元素会被选择。

:has( <forgiving-relative-selector-list> )

示例

选择子元素是图片的<a>标签,相当于“父选择器”:

/* 子元素是图片的a标签 */a:has(> img) {  display: block;}

选择后面是<p>标签的<h5>,相当于“前面兄弟选择器”:

/* 后面是p标签的h5 */h5:has(+ p) {  font-size: 1rem;}

:has()会颠覆很多样式的写法。例如给校验通过的表单一个绿色边框,以前需要通过 JS 来控制样式,现在只需要 CSS 就行:

/* 提交按钮未被禁用的表单 */form:has(.submit:not(:disabled)) {  border-color: green;}

兼容性

目前:has()的兼容性并不好,但是各大浏览器对它的支持已经提上日程,相信很快就能用上。

参考

What's new for the web platform: https://www.youtube.com/watch?v=5b4YcLB4DVI[16]
CSS accent-color: https://goo.gle/399xjOz[17]
CSS color-scheme: https://goo.gle/3N1kpRe[18]
dialog: https://goo.gle/3L07LjR[19]
selectmenu: https://goo.gle/3M5jVt8[20]
Selectmenu demos: https://goo.gle/3wftMGh[21]
Input datetime-local: https://goo.gle/3ysFCzj[22]
COLRv1 fonts: https://goo.gle/3L2zeS3[23]
Back/forward cache: https://goo.gle/39SJgbJ[24]
loading="lazy": https://goo.gle/3w3DigU[25]
CSS aspect-ratio: https://goo.gle/3M2a6fK[26]
CSS containment: https://goo.gle/396F080[27]
Content visibility: https://goo.gle/3yAFcqC[28]
Priority hints: https://goo.gle/3M4O388[29]
CSS size-adjust: https://goo.gle/3w3E25G[30]
SIMD: https://goo.gle/3Fy57Rj[31]
Interaction to next paint: https://goo.gle/3NaAyUF[32]
CHIPS: https://goo.gle/3wpBEFk[33]
Topics API: https://goo.gle/3ytEsDL[34]
UA client hints: https://goo.gle/3L1Pov4[35]
Webauthn: https://goo.gle/3wlrGVc[36]
Webauthn passkeys: https://goo.gle/3M6MuGA[37]
Media session API: https://goo.gle/3FwwbAC[38]
Window controls overlay: https://goo.gle/3L3xbNM[39]
Navigation API: https://goo.gle/3KVQGru[40]
Page transition API: https://goo.gle/3kVBFLF[41]
Themes in manifests: https://goo.gle/3ynnhUu[42]
Eyedropper API: https://goo.gle/3w1spfk[43]
Virtual keyboard API: https://goo.gle/3yx0CVI[44]
structuredClone: https://goo.gle/3M2DQc9[45]
createImageBitmap: https://goo.gle/38jXyBI[46]
Top level await: https://goo.gle/39VD8zz[47]
Private fields: https://goo.gle/3ytxPBi[48]
array.at: https://goo.gle/3yrenoU[49]
SharedArrayBuffer: https://goo.gle/3L3J2vb[50]
URLPattern: https://goo.gle/3P5e2y2[51]
Web codecs API: https://goo.gle/3Pnl2GS[52]
CSS cascade layers: https://goo.gle/3M58Ubc[53]
CSS :has(): https://goo.gle/38cggeF[54]
CSS container queries: https://goo.gle/3FA9Odx[55]
Container query polyfill: https://goo.gle/3ytfjJy[56]

参考资料

[1]

Ant Deisign : https://ant.design/components/checkbox-cn/

[2]

window.opener: https://developer.mozilla.org/docs/Web/API/Window/opener

[3]

contain: https://developer.mozilla.org/zh-CN/docs/Web/CSS/contain

[4]

MDN: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_Containment

[5]

contain: https://developer.mozilla.org/zh-CN/docs/Web/CSS/contain

[6]

counters: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Lists_and_Counters/Using_CSS_counters

[7]

quotes: https://developer.mozilla.org/en-US/docs/Web/CSS/quotes

[8]

DEMO页面: https://blogs.igalia.com/mrego/files/2019/01/css-contain-example.html

[9]

CSS Containment: https://drafts.csswg.org/css-contain/#content-visibility

[10]

介绍文章: https://web.dev/content-visibility/

[11]

《Web Authentication: 一个用于访问公钥凭证的API》: https://www.w3.org/TR/webauthn/

[12]

https://webauthn.io/: https://webauthn.io/

[13]

幽灵漏洞: https://zh.wikipedia.org/wiki/幽灵漏洞

[14]

Cascading and Inheritance Level5: https://www.mybj123.com/gohref.php?url=https://www.w3.org/TR/css-cascade-5/#at-layer

[15]

:scope: https://developer.mozilla.org/zh-CN/docs/Web/CSS/:scope

[16]

https://www.youtube.com/watch?v=5b4YcLB4DVI: https://www.youtube.com/watch?v=5b4YcLB4DVI

[17]

https://goo.gle/399xjOz: https://goo.gle/399xjOz

[18]

https://goo.gle/3N1kpRe: https://goo.gle/3N1kpRe

[19]

https://goo.gle/3L07LjR: https://goo.gle/3L07LjR

[20]

https://goo.gle/3M5jVt8: https://goo.gle/3M5jVt8

[21]

https://goo.gle/3wftMGh: https://goo.gle/3wftMGh

[22]

https://goo.gle/3ysFCzj: https://goo.gle/3ysFCzj

[23]

https://goo.gle/3L2zeS3: https://goo.gle/3L2zeS3

[24]

https://goo.gle/39SJgbJ: https://goo.gle/39SJgbJ

[25]

https://goo.gle/3w3DigU: https://goo.gle/3w3DigU

[26]

https://goo.gle/3M2a6fK: https://goo.gle/3M2a6fK

[27]

https://goo.gle/396F080: https://goo.gle/396F080

[28]

https://goo.gle/3yAFcqC: https://goo.gle/3yAFcqC

[29]

https://goo.gle/3M4O388: https://goo.gle/3M4O388

[30]

https://goo.gle/3w3E25G: https://goo.gle/3w3E25G

[31]

https://goo.gle/3Fy57Rj: https://goo.gle/3Fy57Rj

[32]

https://goo.gle/3NaAyUF: https://goo.gle/3NaAyUF

[33]

https://goo.gle/3wpBEFk: https://goo.gle/3wpBEFk

[34]

https://goo.gle/3ytEsDL: https://goo.gle/3ytEsDL

[35]

https://goo.gle/3L1Pov4: https://goo.gle/3L1Pov4

[36]

https://goo.gle/3wlrGVc: https://goo.gle/3wlrGVc

[37]

https://goo.gle/3M6MuGA: https://goo.gle/3M6MuGA

[38]

https://goo.gle/3FwwbAC: https://goo.gle/3FwwbAC

[39]

https://goo.gle/3L3xbNM: https://goo.gle/3L3xbNM

[40]

https://goo.gle/3KVQGru: https://goo.gle/3KVQGru

[41]

https://goo.gle/3kVBFLF: https://goo.gle/3kVBFLF

[42]

https://goo.gle/3ynnhUu: https://goo.gle/3ynnhUu

[43]

https://goo.gle/3w1spfk: https://goo.gle/3w1spfk

[44]

https://goo.gle/3yx0CVI: https://goo.gle/3yx0CVI

[45]

https://goo.gle/3M2DQc9: https://goo.gle/3M2DQc9

[46]

https://goo.gle/38jXyBI: https://goo.gle/38jXyBI

[47]

https://goo.gle/39VD8zz: https://goo.gle/39VD8zz

[48]

https://goo.gle/3ytxPBi: https://goo.gle/3ytxPBi

[49]

https://goo.gle/3yrenoU: https://goo.gle/3yrenoU

[50]

https://goo.gle/3L3J2vb: https://goo.gle/3L3J2vb

[51]

https://goo.gle/3P5e2y2: https://goo.gle/3P5e2y2

[52]

https://goo.gle/3Pnl2GS: https://goo.gle/3Pnl2GS

[53]

https://goo.gle/3M58Ubc: https://goo.gle/3M58Ubc

[54]

https://goo.gle/38cggeF: https://goo.gle/38cggeF

[55]

https://goo.gle/3FA9Odx: https://goo.gle/3FA9Odx

[56]

https://goo.gle/3ytfjJy: https://goo.gle/3ytfjJy

前端界有哪些越早知道越好的小技巧、小知识?

本文收录在
0评论

登录

忘记密码 ?

切换登录

注册