避免 JS 问题和黑客攻击的智能解决方案

2025-06-08

避免 JS 问题和黑客攻击的智能解决方案

在当今这个时代,作为一名开发人员并处于快节奏的环境中,我们必须尽快交付。

在尝试快速交付的同时,我们最终以某种un-optimised方式构建了功能。我们开始使用 Stack Overflow 上的第一个解决方案,但它可能并不总是最有效或最正确的解决方案,甚至可能是hack……

我列出了我在多个代码库中看到的一些低效代码片段和 hack,以及正确的解决方法。开始吧!


开演时间


1. 在网页之间导航时重置窗口滚动位置的原生方法

如今,许多现代浏览器都倾向于记住网站页面之间导航时的滚动位置,虽然有时这可能非常有用,但同时也可能导致问题。

当您想要在每次加载页面时重置页面数据或进行 API 调用以保持页面更新时,这可能会导致重大问题。

因为浏览器总是会滚动到之前的滚动位置,而不是像预期的那样滚动到顶部。

现在,我在多个代码库中都看到过使用window.scrollTo(0,0)页面挂载来处理这个问题。由于它在第一次绘制完成后才生效,所以会有点卡顿。

但是如果我们可以禁用浏览器记忆滚动位置的功能,那么我们就不需要添加这个 hack 了。就是这样。

if (window.history.scrollRestoration) {
  window.history.scrollRestoration = 'manual'; //default is 'auto'
}
Enter fullscreen mode Exit fullscreen mode

已解决


2. 无需正则表达式即可轻松精确地验证 URL

我认为搜索次数最多、回答次数最多的问题之一是如何在 JS 中验证一个基本的 URL。我已经看到过很多不同类型的正则表达式和字符串匹配解决方案。

但是有一个更简单的解决方案,使用新的本机 URL 构造函数。

const validateURL = url => {
  try {
   new URL(url)
   return true
  } catch {
   return false
  }
}

Enter fullscreen mode Exit fullscreen mode

震惊


3. 始终在事件监听器(例如滚动或调整大小)上添加节流或去抖动

无论何时监听页面上的事件,重要的是确保事件监听器不会因处理传入的请求而不知所措。

否则,它们很快就会成为瓶颈并导致不必要的性能损失。

当您的监听器快速连续地触发事件时(例如鼠标移动或键盘按下事件时滚动),这通常会成为一个问题。

例如,由于滚动事件的触发频率非常高,因此务必确保事件处理程序不会执行计算量巨大的操作。因为如果执行了这些操作,浏览器将更难以跟上。

  • 节流版本:
const throttle = (fn, wait) => {
    let time = Date.now()
    return () => {
        if ((time + wait - Date.now()) < 0) {
            fn()
            time = Date.now()
        }
    }
}

const cbFuncOnScroll = () => {
    console.log('scroll')
}

const throttledfunc = throttle(cbFuncOnScroll, 200)

document.addEventListener('scroll', throttledfunc)
Enter fullscreen mode Exit fullscreen mode
  • 去抖版本:
const debounce = (func, delay) => {
    let timeout = ''
    return function() {
        clearTimeout(timeout)
        const context = this
        const args = arguments
        timeout = setTimeout(() => {
            func.apply(context, args)
        }, delay || 500)
    }
}

const cbFuncOnScroll = () => {
    console.log('scroll')
}

const debouncedFunc = debounce(cbFuncOnScroll, 200)

document.addEventListener('scroll', debouncedFunc)
Enter fullscreen mode Exit fullscreen mode
  • 奖励:使用 Window RequestAnimation Frame 进行去抖动(最佳
const debounceUsingAnimationFrame = (fn, ...args) => {
    // Setup a timer
    let timeout
    // Return a function to run debounced
    return () => {
        // Setup the arguments
        const context = this

        // If there's a timer, cancel it
        if (timeout) {
            window.cancelAnimationFrame(timeout)
        }

        // Setup the new requestAnimationFrame()
        timeout = window.requestAnimationFrame(() => {
            fn.apply(context, args)
        })
    }
}

const cbFuncOnScroll = () => {
    console.log('scroll')
}

const debouncedAnimationFrameFunc = 
        debounceUsingAnimationFrame(cbFuncOnScroll, 200)

document.addEventListener('scroll', debouncedAnimationFrameFunc)
Enter fullscreen mode Exit fullscreen mode

高级提示:document.addEventListener('scroll', cbFuncOnScroll, { passive: true })此处将 passive 设置为 ,true会告诉浏览器您只想执行自己的操作,而不会调用 preventDefault。以下视频展示了此属性带来的性能提升 - https://youtu.be/NPM6172J22g


4. 可以通过 CSS 实现跨浏览器样式

跨浏览器开发是前端开发人员应该具备的最重要的技能之一,并且由于某些 CSS 属性不兼容,我们可能需要在不同浏览器上调整组件的样式。

您怎么做才能实现这一点?我见过的最常见的解决方案是通过 JS,我们提取 UserAgent 或 Platform,并在此基础上在组件上应用样式。

但这是正确的、唯一的方法吗?

这是我的解决方案

  • Safari 目标 CSS 查询
@supports (-webkit-touch-callout: none) {
   // add styles here to override for safari
}
Enter fullscreen mode Exit fullscreen mode
  • Mozilla 目标 CSS 查询
@-moz-document url-prefix() {
   // add styles here to override for mozilla firefox
}
Enter fullscreen mode Exit fullscreen mode
  • IE11 目标 CSS 查询
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
  // add styles here to override for IE11
}
Enter fullscreen mode Exit fullscreen mode

这是一种无需 JS 即可覆盖或添加特定浏览器样式的简单方法。

简单的


5. 使用 CSS 延迟渲染组件

我们研究过由多个小组件组成的大型组件,在这些小组件中,并不是每个组件最初都在视口内可见。

但仅在用户滚动时可见,但我们通常会加载所有组件并将它们渲染在视口上。

一个好的 JS 解决方案是使用 IntersectionObserver API,仅在组件获得焦点时处理其渲染。这个方案很好,因为 Observers 工作在不同的线程上,不会影响主线程的性能。

但是如果我告诉你有一个更好的解决方案,不使用 JS 而只使用 CSS 呢?

content-visibility属性使用户代理能够跳过元素的渲染工作(包括布局和绘制),直到需要它为止。

由于跳过了渲染,如果大部分内容不在屏幕上,利用内容可见性属性可以使初始用户加载速度更快。

它还能更快地与屏幕内容进行交互。相当棒。

.element {
  content-visibility: auto;
}
Enter fullscreen mode Exit fullscreen mode

专家提示:关于内容可见性的详细解释 - https://web.dev/content-visibility/


6. 在 API 副作用调用中添加 try catch 时避免代码冗余

在开发功能时,我们最常执行的任务是进行 API 调用以获取数据并将其显示在页面上。

但由于这是一个副作用,我们依赖于其他服务。

我们倾向于将 API 调用包装在 try 和 catch 语句中,以确保安全并优雅地处理错误。

但是您不觉得它为我们进行的每个 API 调用添加了太多样板代码吗?

这是一个简单的基于承诺的解决方案,以避免过度使用 try-catch 块

const sideEffectAPIWrapper = (promise) =>
    promise
    .then(result => [result, null])
    .catch(err => [null, err])

const sampleFunc = async () => {
    const [result, error] = await sideEffectAPIWrapper(callAPI())
    if (result) {
        console.log('success')
        return
    }
    if (error) {
        console.log('failure')
        return
    }
}
Enter fullscreen mode Exit fullscreen mode

就这样了


结论

我提到的所有问题都是我在 Web 应用开发中遇到和见过的。我相信你可能也在自己的代码库中遇到过这些问题。

避免黑客攻击和冗余的一个简单方法是问自己是否有更好的替代方法来实现此功能。

在编写或审查代码时问自己这个简单的问题将始终帮助您做出正确的决定,并避免将来代码库中出现性能和代码效率问题。

就这些了,再见啦,朋友们

再见朋友们

鏂囩珷鏉ユ簮锛�https://dev.to/faisalpathan/simple-solutions-to-avoid-js-problems-and-hacks-4fcb
PREV
DeepWiki:GitHub 代码库精通的 AI 指南🚀
NEXT
如何使用 HTML、CSS、JavaScript 逐步创建响应式网站