找到
58
篇与
Web前端
相关的结果
- 第 2 页
-
16 个 JavaScript 简写神技,代码效率提升 60%! JavaScript 是一门强大且灵活的语言,拥有丰富的特性和语法糖。分享下 16 个最常用的 JavaScript 的简写技巧,掌握它们可以让我们编写出更简洁、更优雅的代码,并显著提升开发效率(增加摸鱼时间)。 图片 1. 三元运算符简化条件判断 // 传统写法 let result; if (someCondition) { result = 'yes'; } else { result = 'no'; } // 简写方式 const result = someCondition ? 'yes' : 'no';2. 空值合并运算符 // 传统写法 const name = user.name !== null && user.name !== undefined ? user.name : 'default'; // 简写方式 const name = user.name ?? 'default';3. 可选链操作符 // 传统写法 const street = user && user.address && user.address.street; // 简写方式 const street = user?.address?.street;4. 数组去重 // 传统写法 function unique(arr) { return arr.filter((item, index) => arr.indexOf(item) === index); } // 简写方式 const unique = arr => [...new Set(arr)];5. 快速取整 // 传统写法 const floor = Math.floor(4.9); // 简写方式 const floor = ~~4.9;6. 合并对象 // 传统写法 const merged = Object.assign({}, obj1, obj2); // 简写方式 const merged = {...obj1, ...obj2};7. 短路求值 // 传统写法 if (condition) { doSomething(); } // 简写方式 condition && doSomething();8. 默认参数值 // 传统写法 function greet(name) { name = name || 'Guest'; console.log(`Hello ${name}`); } // 简写方式 const greet = (name = 'Guest') => console.log(`Hello ${name}`);9. 解构赋值 图片 10. 字符串转数字 图片 11. 多重条件判断 图片 12. 快速幂运算 图片 13. 对象属性简写 图片 14. 数组映射 图片 15. 交换变量值 图片 16. 动态对象属性 // 传统写法 const obj = {}; obj[dynamic + 'name'] = value; // 简写方式 const obj = { [`${dynamic}name`]: value };欢迎补充。
-
掌握这 8 个 JavaScript 的 Promise 并发控制技巧,性能大幅提升 Promise 已经成为 JavaScript 里处理异步操作的标准方式。然而,当我们需要同时处理多个异步任务时,如何有效地控制 Promise 的并发,就成为了一个影响性能和用户体验的关键问题。 假设,你需要同时请求 100 个接口来获取数据。如果一股脑地发起所有请求,可能会导致以下问题: 浏览器并发限制:浏览器对同一域名的并发请求数量有限制(通常是 6-8 个)。过多的请求会被阻塞,导致页面加载缓慢 服务器压力过大:大量并发请求可能会给服务器带来巨大的压力,导致响应变慢甚至崩溃 资源竞争:多个异步任务同时访问共享资源(例如数据库连接、文件等),可能会导致资源竞争和死锁 用户体验差:页面长时间处于加载状态,用户体验极差 因此,我们需要对 Promise 的并发进行控制,在保证任务执行效率的同时,避免对系统资源造成过大的压力。 Promise.all:并行执行,统一返回 Promise.all 接收一个 Promise 数组作为参数,并行执行所有 Promise,并在所有 Promise 都 fulfilled 后,返回一个包含所有结果的数组。 const promise1 = Promise.resolve(1); const promise2 = Promise.resolve(2); const promise3 = Promise.resolve(3); Promise.all([promise1, promise2, promise3]) .then(results => { console.log(results); // 输出:[1, 2, 3] });适用场景: 多个异步任务之间没有依赖关系,可以并行执行。 注意: 如果其中任何一个 Promise 被 rejected,Promise.all 会立即 rejected,并且只返回第一个 rejected 的原因。 Promise.allSettled:并行执行,返回所有状态 Promise.allSettled 与 Promise.all 类似,也是并行执行所有 Promise,但它会等待所有 Promise 都 settled(fulfilled 或 rejected),并返回一个包含所有 Promise 状态和结果(或原因)的数组。 const promise1 = Promise.resolve(1); const promise2 = Promise.reject("Error"); const promise3 = Promise.resolve(3); Promise.allSettled([promise1, promise2, promise3]) .then(results => { console.log(results); /* 输出: [ { status: 'fulfilled', value: 1 }, { status: 'rejected', reason: 'Error' }, { status: 'fulfilled', value: 3 } ] */ });适用场景: 需要获取所有 Promise 的执行结果,无论它们是 fulfilled 还是 rejected。 Promise.race:并行执行,谁快用谁 Promise.race 接收一个 Promise 数组作为参数,并行执行所有 Promise,只要其中任何一个 Promise settled(fulfilled 或 rejected),Promise.race 就会返回该 Promise 的结果(或原因)。 图片 适用场景: 只需要获取最快完成的 Promise 的结果,例如设置请求超时。 Promise.any (ES2021):并行执行,返回第一个 fulfilled Promise.any 接收一个 Promise 数组作为参数,并行执行所有 Promise,只要其中任何一个 Promise fulfilled,Promise.any 就会返回该 Promise 的结果。如果所有 Promise 都 rejected,则返回一个 AggregateError。 图片 适用场景: 需要获取第一个成功的 Promise 的结果。 自定义并发控制函数:限制最大并发数 Promise.all 等方法虽然可以并行执行 Promise,但无法控制并发数量。我们可以自己实现一个函数来限制最大并发数。 图片 使用示例: 图片 原理: tasks: 一个包含任务函数的数组,每个任务函数返回一个 Promise。 limit: 最大并发数。 results: 存储所有任务的结果。 running: 存储当前正在执行的任务(Promise)。 current: 指向下一个要执行的任务。 while 循环:只要还有任务未执行或有任务正在执行,就继续循环。 if 条件:如果当前正在执行的任务数量小于 limit 且还有任务未执行,则取出下一个任务执行,并将其添加到 running 数组中。 task.then():监听任务完成,将结果添加到 results 数组,并将任务从 running 数组中移除。 await Promise.race(running):如果当前正在执行的任务数量已达到 limit,则等待任意一个任务完成。 Promise.all(results): 等待所有任务执行, 并返回结果。 使用第三方库:p-limit、async-pool 等 有一些成熟的第三方库可以更方便地实现 Promise 并发控制,例如: p-limit: 一个轻量级的 Promise 并发控制库。 图片 async-pool: 一个支持多种并发策略的 Promise 并发控制库。 使用 Generator 函数和 yield 关键字 Generator 函数可以暂停和恢复执行,结合 yield 关键字,可以实现更细粒度的并发控制。 async function* taskGenerator(tasks) { for (const task of tasks) { yield task(); } } async function runTasks(tasks, limit) { let pool = []; let results = []; for await (let result of taskGenerator(tasks)) { pool.push(result); results.push(result); if (pool.length >= limit) { await Promise.race(pool); pool = pool.filter(p => p.status != 'fulfilled' && p.status != 'rejected') // 手动维护 } } return Promise.all(results) }使用消息队列 对于非常大量的异步任务, 且允许一定的延迟, 可以使用消息队列(例如 RabbitMQ, Kafka 等), 将任务放入队列, 然后由多个消费者并行处理. 欢迎补充。
-
CSS 计算属性 介绍 CSS 计算属性,特别是 calc() 函数,是现代前端开发中非常强大且灵活的工具。它允许开发者在声明 CSS 属性值时进行动态计算,从而实现更加响应式和适应性强的设计。 calc() 函数支持基本的数学运算符:加法(+)、减法(-)、乘法(*)以及除法(/),并且可以混合使用不同类型的单位,如像素(px)、百分比(%)、视口单位(vw, vh)等。 基本概念与语法 calc() 的基本语法如下: property: calc(expression);这里,expression 是一个由数值、单位和运算符组成的表达式。例如,你可以这样设置一个元素的宽度,使其等于父容器宽度的 50%,然后减去 20 像素: .container { width: calc(50% - 20px); }使用场景 响应式布局:通过结合相对单位(如 % 或 vw/vh)和绝对单位(如 px),calc() 可以帮助你创建能够根据屏幕尺寸自动调整大小的布局。 居中对齐:利用 calc() 进行简单的数学运算,轻松实现水平或垂直居中。 自适应间距:为元素间的间距提供基于视窗大小或其他变量的动态调整。 字体大小:结合视口单位 (vw) 和固定单位 (px),使文本大小随屏幕变化而适当缩放,提升可读性。 注意事项 空格要求:在 calc() 中,运算符前后必须有空格,否则会导致解析错误。例如 width: calc(100% -8px); 是无效的,正确的写法应该是 width: calc(100% - 8px);。 嵌套限制:虽然 calc() 支持嵌套使用,但应当避免过于复杂的嵌套,因为这可能导致浏览器兼容性问题或解析错误。尽量保持表达式的简洁。 不支持高级数学运算:calc() 不支持更高级的数学函数,如幂次方、平方根等。对于这些需求,可能需要借助 JavaScript 或其他方法来实现。 实际案例 假设我们有一个包含多个项目的网格布局,每个项目之间需要一定的间隔。我们可以使用 calc() 来确保即使在不同屏幕尺寸下,项目之间的间隔也保持一致: .grid-item { width: calc((100% / 3) - 20px); /* 三列布局,每列间留有20px间距 */ margin: 10px; }在这个例子中,grid-item 的宽度被设定为总宽度的三分之一减去固定的边距,从而保证了无论屏幕大小如何变化,项目之间的空间都保持一致。 其他相关函数 除了 calc() 之外,CSS 还提供了其他几个用于计算属性值的函数,比如 min(), max(), 以及 clamp()。这些函数各自具有独特的优势,适用于特定的场景: min() 和 max():分别返回一组值中的最小值和最大值,有助于在多种条件之间选择最合适的尺寸。 clamp():定义一个范围,并在该范围内取值,非常适合于创建既响应用户偏好又符合设计意图的布局。 总结 calc() 函数及其相关的计算属性极大地增强了 CSS 的表现力,使得网页设计变得更加灵活多变。 掌握这些技术可以帮助你解决许多常见的布局挑战,同时创造出更加美观和用户体验友好的界面。 随着浏览器对这些功能的支持日益增强,现在正是开始探索并应用它们的最佳时机。 如果你正在寻找一种方法来优化你的网站布局或解决特定的设计难题,那么深入研究 calc() 及其相关函数将是非常值得的投资。 图片
-
使用一行 CSS 去除图像背景 通过一个 CSS 属性,你就能去除任何图像的背景。 这一属性在大多数现代浏览器中都得到了支持。 假设你遇到这样的情况: 你已经完成了一个很棒的布局,完全响应式,效果也如预期。 但有几张图像的背景与网站的背景不匹配,就像下面这张图: 图片 图像的背景颜色与预期的背景色完全不同。 通常,你可能首先想到的是调整设计: 更改背景色,使其与图像的背景色匹配。 但其实不需要这样做…… 在 CSS 中,有一个叫做 mix-blend-mode 的属性,就是在这里发生了“魔法”。 什么是 mix-blend-mode? HTML 不是编程语言,它没有严格的语法和逻辑规则。 你的网站中,图像通常被包含在各自的 div 元素中,每个 div 元素都有自己的背景色,而每个图像也有自己的背景。 那 mix-blend-mode 如何起作用呢? 当你对图像元素应用 mix-blend-mode 属性时,浏览器会开始对比图像和 div 元素的颜色。 例如,如果你设置样式为 mix-blend-mode: darken,浏览器会逐像素地进行颜色对比。 这意味着,在每个位置,浏览器会比较图像和 div 元素的两个像素。 如果设置了 darken,那么较暗的像素会被保留。 图片 举个例子: 假设 div 的背景比图像的背景更暗,那么 div 背景的颜色会被保留,图像的背景颜色则会被“去除”。 如果图像背景的亮度比 div 更高,那么图像的背景就会“消失”,而与 div 的背景混合。 这时,看起来图像的背景好像被去除了,但实际上它只是与 div 的背景融合了。 如何判断哪个颜色更暗? 很简单…… 你可以将这两种颜色转换为 HSL 颜色模型,它包含三个参数: 色调 (Hue) 饱和度 (Saturation) 亮度 (Lightness) 其中,亮度值越高,颜色就越亮。所以,在进行比较时,亮度值较低的颜色会被保留。 图片 通常情况下,产品的颜色会比背景颜色更强烈,也更暗。因此,产品的实际图像保持原样,而图像背景会与 div 背景融合,看起来就像图像的背景已经被去掉了。 浏览器支持 图片 总结: 通过使用 mix-blend-mode 属性,尤其是 darken 模式,你可以轻松去除图像的背景,并让它和网页背景无缝融合。这不仅省去了繁琐的设计修改,还能在不改变图像的前提下,完美匹配网站的设计风格。 这样一来,你就能用一行 CSS 去实现图像背景的去除,效果又简单又直观。
-
告别 <div>:7 种语义化 HTML 标签提升代码质量 在开发 Web 应用时,项目往往从简单开始,但随着需求的增长,文档结构会变得复杂起来。 div 标签虽然灵活,但滥用会导致代码杂乱、难以维护。原因在于 div 是纯粹的容器,没有任何语义意义。 幸好,HTML 提供了语义化标签这一优雅的解决方案。 根据 W3Schools 的定义:语义化元素是带有明确意义的 HTML 元素。它们不仅能让浏览器理解,还能清晰地传达意图给开发者和用户。 图片 为什么要使用语义化标签? 提升可访问性:语义化标签帮助辅助技术(如屏幕阅读器)更好地导航内容。 增强 SEO:搜索引擎能更准确地理解内容结构,提高页面排名。 易维护性和可读性:清晰的代码结构更易阅读和协作。 减少代码冗余:更少的额外类名或属性,代码更简洁。 总之,除非别无选择,尽量少用 <div>。 下面是 7 个可以替代 <div> 的语义化标签及其应用场景。 1. section:组织相关内容 <section> 用于将相关内容分组,就像书中的章节,每个部分围绕特定主题。 示例: <section> <h2>我们的服务</h2> <p>我们提供网站开发、设计和营销等多种服务,助您取得成功。</p> </section>2. article:独立的内容单元 <article> 适合表示独立的内容单元,例如文章、博客或新闻条目。 示例: <article> <h3>早晨锻炼的好处</h3> <p>晨练可以提升能量,改善心情,是开启一天的绝佳方式。</p> </article>3. nav:导航链接 <nav> 用于定义导航区域,就像网站的目录或地图,指引用户浏览站点。 示例: <nav> <ul> <li><a href="#home">首页</a></li> <li><a href="#about">关于我们</a></li> <li><a href="#services">服务</a></li> <li><a href="#contact">联系我们</a></li> </ul> </nav>4. header:头部内容 <header> 用于页面或部分内容的头部区域,通常包括标题和导航。 示例: <header> <h1>我的个人博客</h1> <p>分享世界各地的故事与见解。</p> </header>5. footer:底部内容 <footer> 用于表示页面或部分内容的底部,通常包括版权声明、联系信息等。 示例: <footer> <p>© 2024 我的个人博客。保留所有权利。</p> <a href="#privacy-policy">隐私政策</a> </footer>6. main:主要内容区域 <main> 用于包含网页的主要内容,不包括头部、底部或侧边栏。 示例: <main> <h2>欢迎访问我们的网站</h2> <p>我们为您提供量身定制的解决方案。</p> </main>7. aside:补充内容 <aside> 用于表示与主内容间接相关的信息,例如侧边栏或附加说明。 示例: <aside> <h4>相关文章</h4> <ul> <li><a href="#article1">改善睡眠的 10 个小技巧</a></li> <li><a href="#article2">经济实惠的健康饮食指南</a></li> </ul> </aside>可视化:语义化标签的页面结构 使用语义化标签构建页面时,页面结构会更加清晰。例如: <header>...</header> <nav>...</nav> <main> <section>...</section> <article>...</article> <aside>...</aside> </main> <footer>...</footer>这样不仅方便开发人员理解页面布局,还能让搜索引擎和辅助设备更好地解析内容。 结论 滥用 <div> 会导致代码冗杂且不易维护。通过使用 <section>、<article>、<nav>、<header>、<footer>、<main> 和 <aside> 等语义化标签,你可以创建更清晰、更具可读性和更高效的网页结构。 记住:除非没有合适的语义化标签,否则尽量少用 <div>!
-
您应该了解的新 CSS 功能 这篇文章介绍了现代 CSS 的一些新特性,帮助开发者提升网页开发效率和视觉效果,尤其是在布局、动画、性能优化等方面。随着 Web 开发的不断进化,CSS 不仅仅是一个样式语言,它已经成为了一个强大的工具,可以用来创建复杂的布局、动画和交互效果。图片 @scope 一种新的 CSS 规则,用于为选择器定义作用域,使得开发者可以更轻松地在特定的 DOM 子树内应用样式,而不会影响到外部元素。这对于组件化架构、第三方库和大项目的协作开发尤为有用。 <div id="componentA"> <p>This paragraph is inside the scope.</p> </div> <p>This paragraph is outside the scope.</p> <style> @scope (#componentA) { p { color: green; } } </style>在这个例子中,只有 #componentA 内部的段落文字会变成绿色,而外部的段落不会受到影响。 @supports CSS 的功能检测机制,可以根据浏览器是否支持某个 CSS 属性来有条件地应用样式,保证在不同浏览器环境下都能有良好的回退处理。 @supports (display: grid) { .grid-container > * { text-shadow: 0 0 2px red; float: none; } }只有当浏览器支持 display: grid 时,才会应用内部的样式,避免不支持的环境下出现问题。 content-visibility 这项性能优化特性可以在元素不可见时跳过渲染,提高页面加载和渲染的性能,非常适合长列表、复杂布局和图片重度页面。 .content-section { content-visibility: auto; contain-intrinsic-size: auto 500px; }如果元素不在视口内,浏览器将跳过渲染这些内容,从而加速页面加载。contain-intrinsic-size 为内容提供一个估算的尺寸,避免布局偏移。 aspect-ratio 控制元素的宽高比,确保元素在不同屏幕尺寸和布局条件下保持一致的比例,尤其对于响应式图片和视频非常有用。 img { display: inline; width: 300px; background-color: #f0f0f0; vertical-align: top; aspect-ratio: 16 / 9; }在这个例子中,图片会保持 16:9 的宽高比,确保即使在不同的容器大小下也不会变形。 @keyframes 和 @starting-style @keyframes 用于创建复杂的动画序列,@starting-style 解决了 CSS 过渡和动画的初始状态问题,确保动画平滑开始。 @keyframes slideIn { from { transform: translateX(-100%); opacity: 0; } 50% { opacity: 0.5; } to { transform: translateX(0); opacity: 1; } } .animated-box { animation: slideIn 3s ease-in-out; }这里定义了一个 slideIn 动画,使元素从左侧滑入并逐渐显示。 offset-position 和 offset-path 用于控制元素沿自定义路径运动的位置,这为复杂的路径动画提供了更精确的控制,适合滚动绑定动画、互动效果和自定义光标。 .moving-object { offset-path: path("M 0 0 L 200 200 L 400 0"); offset-position: 25%25%; animation: pathMotion 5s linear infinite; } @keyframes pathMotion { 0% { offset-distance: 0%; } 100% { offset-distance: 100%; } }在这个例子中,元素沿着一个折线路径移动,从而实现精确的动画控制。 image-set() 为不同设备像素密度选择最合适的图片,提升响应式设计的效果,同时优化性能,减少带宽消耗。 .responsive-box { width: 500px; height: 250px; background-repeat: no-repeat; background-size: cover; background-image: image-set( url("image-lowres.jpg") 1x, url("image-highres.jpg") 2x ); }在这个例子中,标准分辨率的设备将加载 image-lowres.jpg,而高分辨率设备(例如 Retina 显示屏)将加载 image-highres.jpg。 @property CSS Houdini API 的一部分,允许开发者定义和约束自定义 CSS 属性,使得这些属性在动画时更加流畅,能够支持设计系统、主题切换和响应式排版。 @property --main-color { syntax: '<color>'; inherits: false; initial-value: #00ff00; } .color-box { background-color: var(--main-color); transition: --main-color 0.5s; } .color-box:hover { --main-color: #ff0000; }这个示例展示了如何创建一个自定义颜色属性,并在鼠标悬停时平滑地过渡到新颜色。 text-wrap 和 white-space-collapse 提供了更细致的文本换行控制,可以优化长文本内容的显示效果,提升可读性,避免页面布局的跳动。 .balanced-text { text-wrap: balance; text-align: justify; } .preserved-space-text { text-wrap: pretty; }balanced-text 类会使段落尽可能均匀分布文本,而 preserved-space-text 类则会优化文本换行,避免出现孤立或寡头字。 结论 这些现代 CSS 特性不仅为开发者提供了更多的灵活性和控制,还能够在提升性能、减少代码冲突、优化用户体验等方面发挥重要作用。通过学习和掌握这些功能,开发者可以创建出更加高效、灵活且视觉上令人惊艳的网页应用。赶快开始尝试这些新特性吧,构建未来感十足的网页!
-
深入浅析 JavaScript 节流函数的四种实现方式(Throttle) 深入浅析 本文将详细探讨 JavaScript 中的节流函数(Throttle),并提供四种不同实现方式的代码示例及详细解释。 节流函数用于限制函数在一定时间内执行的次数,防止函数被频繁调用导致性能问题。这在处理高频事件(例如滚动事件、鼠标移动事件等)时非常有用。 想象一下沙漏:沙子不断流入,但流出的沙子数量有限。节流函数的工作原理类似,它控制函数执行的频率,避免函数被瞬间大量调用。 图片 节流函数的典型应用场景包括: 鼠标点击事件: 防止用户快速连续点击导致多次执行同一操作。 滚动事件: 在页面滚动时,节流可以限制加载更多内容的请求频率,避免频繁请求造成服务器压力。 输入框监听: 限制搜索或自动完成请求的频率,优化用户体验。 节流与防抖的差异 在之前的文章中,我们介绍了防抖函数(Debounce)。节流与防抖虽然都能降低函数执行频率,但它们的工作机制有所不同: 节流: 在规定时间间隔内,无论事件触发多少次,函数最多只执行一次。 时间间隔主要指两次函数执行之间的时间差。 防抖: 在事件停止触发后的一个规定时间内,函数只执行一次。 时间间隔则指连续触发事件的持续时间。 四种节流函数实现方式 我们将分别介绍四种常见的节流函数实现:时间戳版、定时器版、组合版以及高级自定义版。 1. 时间戳版节流 这种方法通过记录上一次函数执行的时间戳来判断是否需要执行函数。它能保证函数在规定时间间隔后立即执行。 const throttle = (func, wait) => { // 初始化事件开始的时间为0 let preTime = 0; return function() { // 下面两行不懂的可以看看防抖实现的那篇文章 let context = this; let args = arguments; // 获取当前的时间,使用+来转化为数字类型,方便后面做减法 let now = +new Date(); // 当前时间减去之前的时间,结果大于设定的间隔时间才会执行函数 if (now - preTime > wait) { func.apply(context, args); preTime = now; } } };2. 定时器版节流 该方法利用setTimeout来控制函数的执行。 函数不会立即执行,而是在规定时间间隔后执行。通常用于在事件停止触发后执行一次操作。 const throttle2 = (func, wait) => { let timeout; return function() { let context = this; let args = arguments; // 若没有定时器,说明上一次设定的定时器已到时销毁 if (!timeout) { timeout = setTimeout(function() { func.apply(context, args); timeout = null; }, wait) } } };3. 组合版节流 结合时间戳版和定时器版,可以实现更灵活的节流:既可以在时间间隔到期时立即执行,又可以在事件停止触发后执行一次。 function throttle3(func, wait){ let context, args, timeout; let pretime = 0; let later = function(){ pretime = +new Date(); timeout = null; func.apply(context, args); }; let throttled = function(){ context = this; args = arguments; var now = +new Date(); var remaining = wait - (now - pretime); // 剩余时间为负数表示下一次执行需要立即执行 // remaining > wait在修改了系统时间的情况下可能发生 if(remaining <= 0 || remaining > wait){ // 如果有设置过定时器,清空并置为null if(timeout){ clearTimeout(timeout) timeout = null; } pretime = now; func.apply(context,args); }else if(!timeout){ // 需要在剩余时间后执行 timeout = setTimeout(later,remaining); } }; return throttled; };4. 高级自定义版节流 此版本允许通过配置参数 options 来控制函数的执行时机,更加灵活: leading: false:禁用首次执行。 trailing: false:禁用最后一次执行。 /** * 高级自定义节流函数,限制函数在指定时间段内最多执行一次。 * * @param {Function} func - 要节流的函数。 * @param {number} wait - 节流时间间隔(毫秒)。 * @param {object} [options] - 可选参数。 * @param {boolean} [options.leading=true] - 是否允许在节流时间段开始时立即执行函数。 * @param {boolean} [options.trailing=true] - 是否允许在节流时间段结束时执行函数。 * @returns {Function} 节流后的函数。该函数包含一个 `cancel` 方法用于取消任何待定的执行。 */ function throttle(func, wait, options) { var timeout, context, args, result; var previous = 0; if (!options) options = {}; const later = function () { previous = options.leading === false ? 0 : now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; const throttled = function () { var _now = now(); if (!previous && options.leading === false) previous = _now; var remaining = wait - (_now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = _now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; throttled.cancel = function () { clearTimeout(timeout); previous = 0; timeout = context = args = null; }; return throttled; }这个版本还包含cancel方法,用于取消任何待定的执行。 总结 本文详细介绍了四种 JavaScript 节流函数的实现方式,从简单的计时器方法到更高级的自定义版本,满足不同的应用场景。 选择合适的节流函数实现方式,能够有效提高程序性能,优化用户体验。 建议根据实际需求选择合适的实现方式。 高级自定义版本与流行的库(如 underscore)中的节流函数实现类似。
-
掌握这 8 个技巧,让你的 CSS 代码更简洁优雅 写出简洁优雅的 CSS 代码不仅能提高代码的可维护性,还能减少样式冲突。本文将介绍 8 个实用技巧,帮你提升 CSS 代码质量。 1. 善用 CSS 变量,提升代码复用性 图片 使用 CSS 变量的好处: 集中管理主题色值 一处修改,处处生效 支持运行时动态修改 提高代码可维护性 2. 使用 :is() 和 :where() 简化选择器 /* 之前的写法 */ .card h2, .card h3, .card h4, .card h5, .card h6 { margin-bottom: 16px; } /* 使用 :is() 简化后 */ .card :is(h2, h3, h4, h5, h6) { margin-bottom: 16px; } /* 使用 :where() 降低优先级 */ :where(.card, .panel, .box) p { line-height: 1.5; }这个技巧可以: 减少代码重复 提高代码可读性 灵活控制选择器优先级 3. 巧用 aspect-ratio 控制宽高比 /* 常见的 16:9 视频容器 */ .video-container { width: 100%; aspect-ratio: 16 / 9; background: #000; } /* 保持正方形的头像容器 */ .avatar { width: 100px; aspect-ratio: 1; object-fit: cover; border-radius: 50%; }这个属性特别适合: 响应式图片布局 视频容器 卡片网格布局 保持元素固定比例 4. 使用 clamp() 实现响应式数值 .title { /* 最小 16px,最大 32px,基于视窗宽度动态计算 */ font-size: clamp(16px, 4vw, 32px); } .container { /* 响应式内边距 */ padding: clamp(1rem, 3vw, 3rem); /* 响应式宽度 */ width: clamp(320px, 80vw, 1200px); }clamp() 的优势: 无需媒体查询 平滑过渡 代码更简洁 避免内容溢出 5. 使用 gap 属性简化布局间距 .grid-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; } .flex-container { display: flex; flex-wrap: wrap; gap: 16px; /* 可以分别设置行列间距 */ /* gap: 16px 24px; */ }gap 属性的优点: 替代传统的 margin 更容易维护间距 支持 flex 和 grid 布局 避免临边距叠加问题 6. 使用逻辑属性适配不同书写方向 .container { /* 替代 margin-left/right */ margin-inline: auto; /* 替代 padding-top/bottom */ padding-block: 2rem; /* 替代 width */ inline-size: 100%; /* 替代 height */ block-size: auto; }逻辑属性的优势: 更好的国际化支持 适应不同书写模式 代码更具语义化 简化 RTL 适配 7. 使用 :has() 实现父元素选择 /* 当卡片包含图片时应用样式 */ .card:has(img) { padding: 0; } /* 当表单存在错误输入时改变样式 */ .form:has(:invalid) { border-color: red; } /* 调整空列表的样式 */ ul:not(:has(li)) { display: none; }:has() 选择器的应用场景: 基于子元素状态修改父元素 实现复杂的条件样式 减少 JavaScript 的使用 提高样式的动态性 8. 使用 @layer 管理样式优先级 @layer base, components, utilities; @layer base { h1 { font-size: 2rem; margin-bottom: 1rem; } } @layer components { .button { padding: 0.5rem 1rem; border-radius: 4px; } } @layer utilities { .text-center { text-align: center; } }@layer 的优势: 明确的样式优先级 更好的代码组织 避免优先级混乱 便于维护大型项目 欢迎大家留言补充。
-
CSS view():JavaScript 滚动动画的终结者 每当我与 UI/UX 设计师开完会,他们要求我实现滚动动画时,我内心都忍不住想大声尖叫。为什么?因为我讨厌实现滚动动画。虽然它看起来很棒,实现起来也“相当简单”,但它有许多需要处理的怪癖,尤其是当涉及多个动态元素时。但当客户要求那些花哨的“滚动时淡入”效果时,你能怎么办?你只能卷起袖子写一些 JavaScript,即使这让你内心崩溃。 JavaScript 时代(黑暗时期) 以下是我曾经写过的代码(如果你还在这样做,我理解你的痛苦): window.addEventListener('scroll', () => { const elements = document.querySelectorAll('.fade-in'); elements.forEach((element) => { const elementTop = element.getBoundingClientRect().top; const windowHeight = window.innerHeight; if (elementTop < windowHeight * 0.8) { element.style.opacity = '1'; element.style.transform = 'translateY(0)'; } }); });我要么改变 CSS 属性,要么为具有动画属性的元素添加类,让它执行一些“魔法”。有趣的是,我还必须实现反向操作。这很丑陋,我讨厌它。可怜的浏览器每次滚动事件都要处理这些计算。有时我甚至能听到它的“哭泣”。在移动设备上,电池指示器会像自由落体一样下降。 在积累了一些 JavaScript 经验后,我使用防抖来减少需要运行的计算次数。当然,这稍微好了一些,但这不可能是最好的解决方案,对吧? 发现 Intersection Observer(一线希望) 然后我发现了 Intersection Observer API。终于,有了更好的东西!我不再需要不断检查滚动位置,而是可以告诉浏览器:“嘿,当这个东西变得可见时告诉我”: const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { entry.target.classList.add('visible'); observer.unobserve(entry.target); // 触发后不再观察! } }); }, { threshold: 0.2 } ); document.querySelectorAll('.animate-on-scroll').forEach((el) => observer.observe(el));结合一些 CSS: .animate-on-scroll { opacity: 0; transform: translateY(20px); transition: all 0.6s ease-out; } .animate-on-scroll.visible { opacity: 1; transform: translateY(0); }这确实好多了!但仍然……缺少了一些东西。动画要么开启,要么关闭——没有基于滚动位置的平滑控制。当客户要求那些超级平滑的视差效果或渐进式显示时,我又回到了编写复杂 JavaScript 或使用网上找到的 CSS 技巧的老路上。 然后一切都变了:view() 函数 然后,像 CSS 之神的礼物一样,我发现了 view()。天哪,看看这个: @keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .fade-in { animation: fadeIn linear; animation-timeline: view(); /* 关键所在 */ animation-range: entry 10% cover 30%; /* 细节控制 */ }就这样!没有 JavaScript,没有事件监听器,没有性能噩梦。只有如丝般顺滑的滚动响应动画。 最棒的部分? 你知道最神奇的是什么吗?性能。那些旧的 JavaScript 滚动处理程序会让我的 MacBook 风扇像要起飞一样疯狂旋转。但 view()?即使在移动设备上,也如丝般顺滑。浏览器处理所有繁重的工作,所有操作都在合成器线程上运行(这是一个花哨的说法,意思是“非常快”)。 根据我们的性能测试: 动画类型CPU 使用率平均 FPS内存占用JavaScript38%4512MBIntersectionObserver22%558MBCSS view()5%601MB而且,它只是……有效。不再需要调试为什么动画在错误的时间触发,或者为什么滚动位置计算偏差了几个像素。当有很多动画元素时,不再有“滚动卡顿”。它只是有效。 深入理解 CSS view() 函数 CSS view() 函数的引入标志着我们处理滚动驱动动画方式的重大转变。让我们深入了解这个强大的功能,并理解为什么它正在彻底改变网页动画。 animation-timeline: view() view() 的核心是基于元素在视口中的可见性创建一个进度时间轴。你可以将其视为一个虚拟时间轴,随着元素进入视口而向前移动,随着元素退出视口而向后移动。 .element { animation-timeline: view(); }你可以自定义视口的测量方式: /* 为视口检测添加边距 */ animation-timeline: view(block 10px); /* 垂直边距 */ animation-timeline: view(inline 50%); /* 水平边距 */ animation-timeline: view(10px 20px 30px 40px); /* 所有边距 */理解 animation-range animation-range 属性定义了动画相对于元素在视口中的位置开始和结束的时间。就像为动画设置检查点。 实际应用场景: 1. 渐进式图片加载 @keyframes progressive-load { 0% { filter: blur(20px); scale: 1.1; } 100% { filter: blur(0); scale: 1; } } .lazy-image { animation: progressive-load linear; animation-timeline: view(); animation-range: entry 10% cover 50%; }2. 分步式文字浮现 @keyframes staggered-text { 0% { opacity: 0; transform: translateX(-50px); } 20% { opacity: 1; transform: translateX(0); } 80% { opacity: 1; transform: translateX(0); } 100% { opacity: 0; transform: translateX(50px); } } .staggered-text { animation: staggered-text linear; animation-timeline: view(); animation-range: entry 0% exit 100%; }3. 动态进度条 @keyframes progress-bar { from { width: 0%; } to { width: 100%; } } .progress-bar { animation: progress-bar linear; animation-timeline: view(); animation-range: entry cross(10% 20%); }高级技巧 1. 多时间轴组合 .card { --rotate-axis: 30deg; animation: rotate linear, scale ease-in-out; animation-timeline: view(), scroll(root block); animation-range: entry 0% cover 50%, entry 10% cover 90%; }2. 嵌套动画控制 .parent-element { animation-timeline: view(); } .child-element { animation-delay: calc(parent-animation-progress * 0.2s); }3. 视差滚动系统 .parallax-layer { animation: translate linear; animation-timeline: view(); } .layer-1 { animation-range: entry 0% cover 100%; } .layer-2 { animation-range: entry 10% cover 90%; } .layer-3 { animation-range: entry 20% cover 80%; }浏览器支持与渐进增强 截至 2024 年 12 月,推荐使用以下兼容方案: @supports (animation-timeline: view()) { /* 现代浏览器样式 */ } @supports not (animation-timeline: view()) { /* 回退方案 */ .fallback { transition: all 0.5s ease; } /* 使用 IntersectionObserver 添加 .active 类 */ }推荐使用 @scroll-timeline polyfill 为旧浏览器提供基本支持。 未来展望 即将推出的 CSS 滚动驱动动画规范将带来更多激动人心的特性: scroll() 时间轴类型 时间轴范围自定义单位(svh, lvw) 动画相位控制(animation-phase) 时间轴事件监听(实验性提案) 这些新功能将使创建如下效果成为可能: 基于滚动速度的动画 分页滚动动画 嵌套滚动容器联动 总结 CSS view() 的出现不仅改变了我们实现滚动动画的方式,更重新定义了网页动画的可能性边界。从简单的淡入效果到复杂的视差系统,现在都可以用声明式的简洁语法实现。虽然浏览器支持仍在推进中,但现代前端开发者应该: 优先使用 CSS view() 实现基础动画 使用 JavaScript 作为高级交互的补充 始终考虑渐进增强策略 定期关注 Scroll-driven Animations 规范 的更新 下次设计师再提出复杂的滚动动画需求时,你可以微笑着打开 CSS 文件——那些需要复杂 JavaScript 才能实现的效果,现在可能只需要几行优雅的 CSS 代码。 图片 如果您觉得内容对您有帮助,欢迎在看、点赞、分享 ⬇️❤️⬇️