lang 属性只能关联一种语言。这意味着,即使网页中有多种语言,<html>
属性也只能有一种语言。将 lang
设为网页的主要语言。
<html lang="ar,en,fr,pt">...</html>不支持多种语言。
<html lang="ar">...</html>仅设置网页的主要语言。在此示例中,语言为阿拉伯语。
链接
与按钮类似,链接主要从其文本内容中获取其无障碍名称。在创建链接时,有个小窍门是将最有意义的一段文字放入链接本身,而不要像“此处”或“了解详情”这样的填充词。
Check out our guide to web performance <a href="/guide">here</a>.
Check out <a href="/guide">our guide to web performance</a>.
检查动画是否触发布局
使用 transform
以外的内容移动元素的动画可能速度较慢。在以下示例中,我为 top
和 left
添加动画效果并使用 transform
获得了相同的视觉效果。
.box { position: absolute; top: 10px; left: 10px; animation: move 3s ease infinite; } @keyframes move { 50% { top: calc(90vh - 160px); left: calc(90vw - 200px); } }
.box { position: absolute; top: 10px; left: 10px; animation: move 3s ease infinite; } @keyframes move { 50% { transform: translate(calc(90vw - 200px), calc(90vh - 160px)); } }
你可以在以下两个 Glitch 示例中对此进行测试,并使用开发者工具了解性能。
使用相同的标记,我们可以将 padding-top: 56.25%
替换为 aspect-ratio: 16 / 9
,并将 aspect-ratio
设置为指定的 width
/ height
比率。
.container { width: 100%; padding-top: 56.25%; }
.container { width: 100%; aspect-ratio: 16 / 9; }
使用 aspect-ratio
(而非 padding-top
)要更加明确,而且不会为了执行超出常规范围的操作而修改内边距属性。
没错,我使用 reduce
来链接一系列 promise。我就太聪明了了。但这种编码有点智能,若不使用,最好。
然而,在将上述转换转换为异步函数时,很容易造成太多顺序:
async function logInOrder(urls) { for (const url of urls) { const response = await fetch(url); console.log(await response.text()); } }代码简洁得多,但我的第二次提取要等到第一次提取都完全读取完毕后才开始,以此类推。这比并行执行提取的 promise 示例要慢得多。幸运的是,有一条理想的中间地带。
function markHandled(...promises) { Promise.allSettled(promises); } async function logInOrder(urls) { // fetch all the URLs in parallel const textPromises = urls.map(async (url) => { const response = await fetch(url); return response.text(); }); markHandled(...textPromises); // log them in sequence for (const textPromise of textPromises) { console.log(await textPromise); } }在此示例中,系统并行提取和读取网址,但“智能”
reduce
位被替换为标准、单调、可读的 for 循环。
编写 Houdini 自定义属性
下面是一个设置自定义属性(例如 CSS 变量)的示例,但现在设置了语法(类型)、初始值(回退)和继承布尔值(它是否从父项继承值?)。目前执行此操作的方式是在 JavaScript 中通过 CSS.registerProperty()
来实现,但在 Chromium 85 及更高版本中,您的 CSS 文件中将支持 @property
语法:
CSS.registerProperty({ name: '--colorPrimary', syntax: '', initialValue: 'magenta', inherits: false });
@property --colorPrimary { syntax: ''; initial-value: magenta; inherits: false; }
现在,您可以像访问任何其他 CSS 自定义属性一样,通过 var(--colorPrimary)
访问 --colorPrimary
。不过,这里的不同之处在于,--colorPrimary
不仅不能被读取为字符串,里面有数据!
CSS backdrop-filter
会向半透明或透明的元素应用一种或多种效果。为了理解这一点,请参考下图。
![一个叠加在圆圈上的三角形。无法通过三角形看到该圆形。](https://web.dev/static/examples/image/admin/LOqxvB3qqVkbZBmxMmKS.png?hl=zh-cn)
.frosty-glass-pane { backdrop-filter: blur(2px); }
![一个叠加在圆圈上的三角形。三角形是半透明的,可以透过它看到圆圈。](https://web.dev/static/examples/image/admin/VbyjpS6Td39E4FudeiVg.png?hl=zh-cn)
.frosty-glass-pane { opacity: .9; backdrop-filter: blur(2px); }
左侧图片显示了如果未使用或不支持 backdrop-filter
,重叠元素是如何渲染的。右侧的图片使用 backdrop-filter
应用模糊效果。请注意,除了 backdrop-filter
之外,它还使用了 opacity
。如果没有 opacity
,就没有应用模糊处理的条件。毫无疑问,如果将 opacity
设置为 1
(完全不透明),对背景没有任何影响。
不过,与 unload
事件不同的是,beforeunload
也有正当的用途。例如,当您想警告用户他们有未保存的更改时,如果他们离开页面,这些更改将会丢失。在这种情况下,建议仅在用户有未保存的更改时添加 beforeunload
监听器,然后在未保存的更改保存后立即移除这些监听器。
window.addEventListener('beforeunload', (event) => { if (pageHasUnsavedChanges()) { event.preventDefault(); return event.returnValue = 'Are you sure you want to exit?'; } });上述代码无条件地添加
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); });上述代码仅在需要时添加
beforeunload
监听器(在不需要时将其移除)。
尽可能减少使用 Cache-Control: no-store
Cache-Control: no-store
是 Web 服务器可对响应设置的 HTTP 标头,指示浏览器不要将响应存储在任何 HTTP 缓存中。这适用于包含敏感用户信息的资源,例如需要登录才能访问的网页。
包含每个输入组 (.fieldset-item
) 的 fieldset
元素使用 gap: 1px
在元素之间创建细线边框。没有棘手的边界解决方案!
.grid { display: grid; gap: 1px; background: var(--bg-surface-1); & > .fieldset-item { background: var(--bg-surface-2); } }
.grid { display: grid; & > .fieldset-item { background: var(--bg-surface-2); &:not(:last-child) { border-bottom: 1px solid var(--bg-surface-1); } } }
天然网格包裹
最终,最复杂的布局是宏布局,即 <main>
和 <form>
之间的逻辑布局系统。
<input type="checkbox" id="text-notifications" name="text-notifications" >
<label for="text-notifications"> <h3>Text Messages</h3> <small>Get notified about all text messages sent to your device</small> </label>
包含每个输入组 (.fieldset-item
) 的 fieldset
元素使用 gap: 1px
在元素之间创建细线边框。没有棘手的边界解决方案!
.grid { display: grid; gap: 1px; background: var(--bg-surface-1); & > .fieldset-item { background: var(--bg-surface-2); } }
.grid { display: grid; & > .fieldset-item { background: var(--bg-surface-2); &:not(:last-child) { border-bottom: 1px solid var(--bg-surface-1); } } }
标签页 <header>
布局
下一个布局几乎与此相同:我使用 flex 创建垂直排序。
<snap-tabs> <header> <nav></nav> <span class="snap-indicator"></span> </header> <section></section> </snap-tabs>
header { display: flex; flex-direction: column; }
.snap-indicator
应随一组链接一起水平移动,而此标题布局有助于为这一阶段做好准备。此处不存在绝对定位的元素!
Gentle Flex 是一种更真正的居中策略。该控件柔和柔和,因为与 place-content: center
不同,在居中时不会改变子项的框大小。将所有内容堆叠、居中和间隔放置,并尽可能轻轻地移动。
.gentle-flex {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1ch;
}
- 仅处理对齐、方向和分布
- 修改和维护都集中在一处
- Gap 可保证 n 个子元素之间相等间距n
- 大多数代码行
非常适合宏观和微观布局。
用法
.gap-example {
display: grid;
gap: 10px;
gap: 2ch;
gap: 5%;
gap: 1em;
gap: 3vmax;
}
可以向间隔传递 1 个长度,该长度将用于行和列。
.grid { display: grid; gap: 10px; }同时一起设置行和列
.grid { display: grid; row-gap: 10px; column-gap: 10px; }
可以传递 2 个长度,这些长度将用于行和列。
.grid { display: grid; gap: 10px 5%; }同时单独设置行和列
.grid { display: grid; row-gap: 10px; column-gap: 5%; }