国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
Jump.js
快速了解屏幕背後
一些自定義
測(cè)試頁(yè)面
主腳本
事件委託
單個(gè)處理程序
可訪問(wèn)性注意事項(xiàng)
使用CSS支持原生平滑滾動(dòng)
結(jié)論
關(guān)於使用原生JavaScript進(jìn)行平滑滾動(dòng)的常見(jiàn)問(wèn)題解答 (FAQs)
如何在不使用任何庫(kù)的情況下使用原生JavaScript實(shí)現(xiàn)平滑滾動(dòng)?
為什麼我的平滑滾動(dòng)在Safari中不起作用?
如何平滑地滾動(dòng)到特定元素?
我可以控制平滑滾動(dòng)的速度嗎?
如何實(shí)現(xiàn)水平平滑滾動(dòng)?
如何停止平滑滾動(dòng)動(dòng)畫?
如何實(shí)現(xiàn)具有固定頁(yè)眉的平滑滾動(dòng)?
如何為錨鏈接實(shí)現(xiàn)平滑滾動(dòng)?
如何使用鍵盤導(dǎo)航實(shí)現(xiàn)平滑滾動(dòng)?
如何測(cè)試我的平滑滾動(dòng)實(shí)現(xiàn)的兼容性?
首頁(yè) web前端 js教程 如何在Vanilla JavaScript中實(shí)現(xiàn)光滑的滾動(dòng)

如何在Vanilla JavaScript中實(shí)現(xiàn)光滑的滾動(dòng)

Feb 18, 2025 am 10:49 AM

How to Implement Smooth Scrolling in Vanilla JavaScript

核心要點(diǎn)

  • 使用Jump.js庫(kù)實(shí)現(xiàn)原生JavaScript平滑滾動(dòng),簡(jiǎn)化滾動(dòng)動(dòng)畫,無(wú)需外部依賴。
  • 修改Jump.js原始代碼,將其從ES6轉(zhuǎn)換為ES5,以確保與不同瀏覽器的更廣泛兼容性。
  • 使用requestAnimationFrame方法進(jìn)行平滑動(dòng)畫更新,優(yōu)化性能並提供更流暢的用戶體驗(yàn)。
  • 實(shí)現(xiàn)自定義JavaScript來(lái)攔截默認(rèn)的頁(yè)面內(nèi)鏈接行為,用平滑滾動(dòng)動(dòng)畫替換突然跳轉(zhuǎn)。
  • 集成CSS scroll-behavior屬性,以支持識(shí)別此功能的瀏覽器中的原生平滑滾動(dòng),如果瀏覽器不支持,則提供JavaScript後備機(jī)制。
  • 通過(guò)在滾動(dòng)後將焦點(diǎn)設(shè)置到目標(biāo)元素來(lái)確??稍L問(wèn)性,解決鍵盤導(dǎo)航的潛在問(wèn)題,並增強(qiáng)所有用戶的可用性。

本文由Adrian Sandu、Chris Perry、Jérémy Heleine和Mallory van Achterberg同行評(píng)審。感謝所有SitePoint的同行評(píng)審者,使SitePoint的內(nèi)容達(dá)到最佳狀態(tài)!

平滑滾動(dòng)是一種用戶界面模式,它逐步增強(qiáng)了默認(rèn)的頁(yè)面內(nèi)導(dǎo)航體驗(yàn),在滾動(dòng)框(視口或可滾動(dòng)元素)內(nèi)動(dòng)畫地改變位置,從激活鏈接的位置到鏈接URL的哈希片段中指示的目標(biāo)元素的位置。

這並非什麼新鮮事物,多年來(lái)一直是一種已知的模式,例如,請(qǐng)查看這篇可追溯到2003年的SitePoint文章!順便說(shuō)一句,這篇文章具有歷史價(jià)值,因?yàn)樗故玖丝蛻舳薐avaScript編程,特別是DOM,多年來(lái)的變化和發(fā)展,允許開(kāi)發(fā)更簡(jiǎn)便的原生JavaScript解決方案。

在jQuery生態(tài)系統(tǒng)中,這種模式有很多實(shí)現(xiàn),可以直接使用jQuery或使用插件實(shí)現(xiàn),但在本文中,我們感興趣的是純JavaScript解決方案。具體來(lái)說(shuō),我們將探索和利用Jump.js庫(kù)。

在介紹該庫(kù)及其功能和特性的概述之後,我們將對(duì)原始代碼進(jìn)行一些更改以適應(yīng)我們的需求。在此過(guò)程中,我們將復(fù)習(xí)一些核心的JavaScript語(yǔ)言技能,例如函數(shù)和閉包。然後,我們將創(chuàng)建一個(gè)HTML頁(yè)面來(lái)測(cè)試平滑滾動(dòng)行為,然後將其實(shí)現(xiàn)為自定義腳本。然後將添加對(duì)CSS原生平滑滾動(dòng)的支持(如果可用),最後我們將對(duì)瀏覽器導(dǎo)航歷史記錄進(jìn)行一些觀察。

這是我們將創(chuàng)建的最終演示:

查看CodePen上的SitePoint (@SitePoint)的Smooth Scrolling筆。

完整的源代碼可在GitHub上找到。

Jump.js

Jump.js是用原生ES6 JavaScript編寫的,沒(méi)有任何外部依賴項(xiàng)。它是一個(gè)小型實(shí)用程序,只有大約42 SLOC,但提供的最小化包的大小約為2.67 KB,因?yàn)樗仨氝M(jìn)行轉(zhuǎn)譯。 GitHub項(xiàng)目頁(yè)面上提供了一個(gè)演示。

顧名思義,它只提供跳轉(zhuǎn):滾動(dòng)條位置從其當(dāng)前值到目標(biāo)位置的動(dòng)畫變化,通過(guò)提供DOM元素、CSS選擇器或正數(shù)或負(fù)數(shù)值形式的距離來(lái)指定。這意味著在平滑滾動(dòng)模式的實(shí)現(xiàn)中,我們必須自己執(zhí)行鏈接劫持。更多內(nèi)容請(qǐng)參見(jiàn)以下部分。

請(qǐng)注意,目前僅支持視口的垂直滾動(dòng)。

我們可以使用一些選項(xiàng)配置跳轉(zhuǎn),例如持續(xù)時(shí)間(此參數(shù)是必需的)、緩動(dòng)函數(shù)和在動(dòng)畫結(jié)束時(shí)觸發(fā)的回調(diào)。我們稍後將在演示中看到它們的實(shí)際應(yīng)用。有關(guān)完整詳細(xì)信息,請(qǐng)參見(jiàn)文檔。

Jump.js在“現(xiàn)代”瀏覽器上運(yùn)行沒(méi)有問(wèn)題,包括Internet Explorer 10版或更高版本。同樣,請(qǐng)參考文檔以了解支持的瀏覽器完整列表。使用合適的requestAnimationFrame polyfill,它甚至可以在舊版瀏覽器上運(yùn)行。

快速了解屏幕背後

在內(nèi)部,Jump.js源代碼使用window對(duì)象的requestAnimationFrame方法來(lái)安排在滾動(dòng)動(dòng)畫的每一幀中更新視口垂直位置的位置。此更新是通過(guò)將使用緩動(dòng)函數(shù)計(jì)算的下一個(gè)位置值傳遞給window.scrollTo方法來(lái)實(shí)現(xiàn)的。有關(guān)完整詳細(xì)信息,請(qǐng)參見(jiàn)源代碼。

一些自定義

在深入研究演示以展示Jump.js的使用之前,我們將對(duì)原始代碼進(jìn)行一些細(xì)微的更改,但這不會(huì)修改其內(nèi)部工作方式。

源代碼是用ES6編寫的,需要與JavaScript構(gòu)建工具一起使用才能進(jìn)行轉(zhuǎn)譯和捆綁模塊。對(duì)於某些項(xiàng)目來(lái)說(shuō),這可能有點(diǎn)過(guò)分,因此我們將應(yīng)用一些重構(gòu)來(lái)將代碼轉(zhuǎn)換為ES5,以便在任何地方使用。

首先,讓我們刪除ES6語(yǔ)法和功能。腳本定義了一個(gè)ES6類:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>

我們可以使用構(gòu)造函數(shù)和一堆原型方法將其轉(zhuǎn)換為ES5“類”,但請(qǐng)注意,我們永遠(yuǎn)不需要此類的多個(gè)實(shí)例,因此使用普通對(duì)象字面量實(shí)現(xiàn)的單例就可以了:

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>

除了刪除類之外,我們還需要進(jìn)行其他一些更改。 requestAnimationFrame的回調(diào)用於在每一幀中更新滾動(dòng)條位置,在原始代碼中,它是通過(guò)ES6箭頭函數(shù)調(diào)用的,在初始化時(shí)預(yù)綁定到j(luò)ump單例。然後,我們將默認(rèn)緩動(dòng)函數(shù)捆綁在同一個(gè)源文件中。最後,我們使用IIFE(立即調(diào)用函數(shù)表達(dá)式)包裝了代碼,以避免命名空間污染。

現(xiàn)在我們可以應(yīng)用另一個(gè)重構(gòu)步驟,注意借助嵌套函數(shù)和閉包,我們可以只使用函數(shù)而不是對(duì)象:

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>

單例現(xiàn)在變成了將被調(diào)用以動(dòng)畫滾動(dòng)的jump函數(shù),loop和end回調(diào)變成了嵌套函數(shù),而對(duì)象的屬性現(xiàn)在變成了局部變量(閉包)。我們不再需要IIFE,因?yàn)楝F(xiàn)在所有代碼都安全地包裝在一個(gè)函數(shù)中。

作為最後的重構(gòu)步驟,為了避免在每次調(diào)用loop回調(diào)時(shí)重複timeStart重置檢查,第一次調(diào)用requestAnimationFrame()時(shí),我們將向其傳遞一個(gè)匿名函數(shù),該函數(shù)在調(diào)用loop函數(shù)之前重置timerStart變量:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>

再次注意,在重構(gòu)過(guò)程中,核心滾動(dòng)動(dòng)畫代碼沒(méi)有改變。

測(cè)試頁(yè)面

現(xiàn)在我們已經(jīng)自定義了腳本以適應(yīng)我們的需求,我們準(zhǔn)備組裝一個(gè)測(cè)試演示。在本節(jié)中,我們將編寫一個(gè)使用下一節(jié)中介紹的腳本增強(qiáng)平滑滾動(dòng)的頁(yè)面。

該頁(yè)麵包含一個(gè)包含指向文檔中後續(xù)部分的頁(yè)面內(nèi)鏈接的內(nèi)容表(TOC),以及指向TOC的其他鏈接。我們還將混合一些指向其他頁(yè)面的外部鏈接。這是此頁(yè)面的基本結(jié)構(gòu):

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>

在頭部,我們將包含一些CSS規(guī)則來(lái)設(shè)置基本的最簡(jiǎn)佈局,而在body標(biāo)籤的末尾,我們將包含兩個(gè)JavaScript文件:前者是我們重構(gòu)後的Jump.js版本,後者是我們現(xiàn)在將討論的腳本。

主腳本

這是將使用我們自定義的Jump.js庫(kù)版本的動(dòng)畫跳轉(zhuǎn)來(lái)增強(qiáng)測(cè)試頁(yè)面滾動(dòng)體驗(yàn)的腳本。當(dāng)然,此代碼也將用ES5 JavaScript編寫。

讓我們簡(jiǎn)要概述一下它應(yīng)該完成的任務(wù):它必須劫持頁(yè)面內(nèi)鏈接上的點(diǎn)擊,禁用瀏覽器的默認(rèn)行為(突然跳轉(zhuǎn)到點(diǎn)擊鏈接的href屬性的哈希片段中指示的目標(biāo)元素),並將其替換為對(duì)我們的jump()函數(shù)的調(diào)用。

因此,首先要監(jiān)控頁(yè)面內(nèi)鏈接上的點(diǎn)擊。我們可以通過(guò)兩種方式做到這一點(diǎn),使用事件委託或?qū)⑻幚沓绦蚋郊拥矫總€(gè)相關(guān)的鏈接。

事件委託

在第一種方法中,我們將點(diǎn)擊偵聽(tīng)器添加到一個(gè)元素document.body。這樣,頁(yè)面上任何元素的每個(gè)點(diǎn)擊事件都將沿著其祖先的分支冒泡到DOM樹,直到到達(dá)document.body:

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>

當(dāng)然,現(xiàn)在在註冊(cè)的事件偵聽(tīng)器(onClick)中,我們必須檢查傳入的click事件對(duì)象的target,以檢查它是否與頁(yè)面內(nèi)鏈接元素相關(guān)。這可以通過(guò)多種方式完成,因此我們將將其抽象為輔助函數(shù)isInPageLink()。我們稍後將看看此函數(shù)的機(jī)制。

如果傳入的點(diǎn)擊是在頁(yè)面內(nèi)鏈接上,我們將停止事件冒泡並阻止關(guān)聯(lián)的默認(rèn)操作。最後,我們調(diào)用jump函數(shù),為其提供目標(biāo)元素的哈希選擇器和配置所需動(dòng)畫的參數(shù)。

這是事件處理程序:

<code>requestAnimationFrame(function(time) { timeStart = time; loop(time); });

function loop(time) {
    timeElapsed = time - timeStart;

    window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

    if (timeElapsed         requestAnimationFrame(loop)
    else
        end();
}
</code>

單個(gè)處理程序

使用第二種方法來(lái)監(jiān)控鏈接點(diǎn)擊,將上面介紹的事件處理程序的稍微修改後的版本附加到每個(gè)頁(yè)面內(nèi)鏈接元素,因此沒(méi)有事件冒泡:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>

我們查詢所有元素,並使用[].slice()技巧將返回的DOM NodeList轉(zhuǎn)換為JavaScript數(shù)組(如果目標(biāo)瀏覽器支持,更好的替代方法是使用ES6 Array.from()方法)。然後,我們可以使用數(shù)組方法過(guò)濾頁(yè)面內(nèi)鏈接,重新使用上面定義的相同輔助函數(shù),最後將偵聽(tīng)器附加到剩餘的鏈接元素。

事件處理程序與之前幾乎相同,但當(dāng)然我們不需要檢查點(diǎn)擊目標(biāo):

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>

哪種方法最好取決於使用上下文。例如,如果在初始頁(yè)面加載後可能動(dòng)態(tài)添加新的鏈接元素,那麼我們必須使用事件委託。

現(xiàn)在我們轉(zhuǎn)向isInPageLink()的實(shí)現(xiàn),我們?cè)谥暗氖录幚沓绦蛑惺褂么溯o助函數(shù)來(lái)抽象頁(yè)面內(nèi)鏈接的測(cè)試。正如我們所看到的,此函數(shù)接受DOM節(jié)點(diǎn)作為參數(shù),並返回一個(gè)布爾值以指示該節(jié)點(diǎn)是否表示頁(yè)面內(nèi)鏈接元素。僅檢查傳遞的節(jié)點(diǎn)是A標(biāo)籤並且設(shè)置了哈希片段是不夠的,因?yàn)殒溄涌赡苁侵赶蛄硪粋€(gè)頁(yè)面,在這種情況下,必須不禁用默認(rèn)瀏覽器操作。因此,我們檢查屬性href中存儲(chǔ)的值“減去”哈希片段是否等於頁(yè)面URL:

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>

stripHash()是另一個(gè)輔助函數(shù),我們也用它在腳本初始化時(shí)設(shè)置變量pageUrl的值:

<code>requestAnimationFrame(function(time) { timeStart = time; loop(time); });

function loop(time) {
    timeElapsed = time - timeStart;

    window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

    if (timeElapsed         requestAnimationFrame(loop)
    else
        end();
}
</code>

此基於字符串的解決方案以及哈希片段的修剪即使在帶有查詢字符串的URL上也能正常工作,因?yàn)楣2糠衷赨RL的一般結(jié)構(gòu)中位於它們之後。

正如我之前所說(shuō),這只是實(shí)現(xiàn)此測(cè)試的一種可能方法。例如,本教程開(kāi)頭引用的文章使用了不同的解決方案,對(duì)鏈接href與location對(duì)象進(jìn)行了組件級(jí)比較。

應(yīng)該注意的是,我們?cè)趦煞N事件訂閱方法中都使用了此函數(shù),但在第二種方法中,我們將其用作我們已經(jīng)知道是標(biāo)籤的元素的過(guò)濾器,因此對(duì)tagName屬性的第一次檢查是多餘的。這留給讀者作為練習(xí)。

可訪問(wèn)性注意事項(xiàng)

就目前而言,我們的代碼容易受到已知錯(cuò)誤(實(shí)際上是一對(duì)無(wú)關(guān)的錯(cuò)誤,影響B(tài)link/WebKit/KHTML和一個(gè)影響IE的錯(cuò)誤)的影響,這些錯(cuò)誤會(huì)影響鍵盤用戶。當(dāng)通過(guò)製表鍵瀏覽TOC鏈接時(shí),激活一個(gè)鏈接將平滑地向下滾動(dòng)到選定的部分,但焦點(diǎn)將保留在鏈接上。這意味著在下一個(gè)製表鍵按下時(shí),用戶將被送回TOC,而不是送往他們選擇的節(jié)中的第一個(gè)鏈接。

為了解決這個(gè)問(wèn)題,我們將向主腳本添加另一個(gè)函數(shù):

<code>>
    <h1>></h1>Title>
    <nav> id="toc"></nav>
        <ul>></ul>
            <li>></li>
<a> href="http://m.miracleart.cn/link/db8229562f80fbcc7d780f571e5974ec"></a>Section 1>>
            <li>></li>
<a> href="http://m.miracleart.cn/link/ba2cf4148007ed8a8b041f8abd9bbf96"></a>Section 2>>
            ...
        >
    >
     id="sect-1">
        <h2>></h2>Section 1>
        <p>></p>Pellentesque habitant morbi tristique senectus et netus et <a> href="http://m.miracleart.cn/link/e1b97c787a5677efa5eba575c41e8688"></a>a link to another page> ac turpis egestas. <a> href="http://m.miracleart.cn/link/e1b97c787a5677efa5eba575c41e8688index.html#foo"></a>A link to another page, with an anchor> quam, feugiat vitae, ...>
        <a> href="http://m.miracleart.cn/link/7421d74f57142680e679057ddc98edf5"></a>Back to TOC>
    >
     id="sect-2">
        <h2>></h2>Section 2>
        ...
    >
    ...
     src="jump.js">>
     src="script.js">>
>
</code>

它將在我們將傳遞給jump函數(shù)的回調(diào)中運(yùn)行,並將我們要滾動(dòng)到的元素的哈希值傳遞過(guò)去:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>

此函數(shù)的作用是獲取哈希值對(duì)應(yīng)的DOM元素,並測(cè)試它是否已經(jīng)是可以接收焦點(diǎn)的元素(例如錨點(diǎn)或按鈕元素)。如果元素不能默認(rèn)接收焦點(diǎn)(例如我們的容器),則它會(huì)將其tabIndex屬性設(shè)置為-1(允許通過(guò)編程方式接收焦點(diǎn),但不能通過(guò)鍵盤接收)。然後焦點(diǎn)將設(shè)置為該元素,這意味著用戶的下一個(gè)tab按鍵將焦點(diǎn)移動(dòng)到下一個(gè)可用鏈接。

您可以在此處查看主腳本的完整源代碼,其中包含所有先前討論的更改。

使用CSS支持原生平滑滾動(dòng)

CSS對(duì)像模型視圖模塊規(guī)範(fàn)引入了一個(gè)新的屬性來(lái)原生實(shí)現(xiàn)平滑滾動(dòng):scroll-behavior。

它可以取兩個(gè)值,auto表示默認(rèn)的瞬時(shí)滾動(dòng),smooth表示動(dòng)畫滾動(dòng)。該規(guī)範(fàn)沒(méi)有提供任何配置滾動(dòng)動(dòng)畫的方法,例如其持續(xù)時(shí)間和時(shí)間函數(shù)(緩動(dòng))。

我可以使用css-scroll-behavior嗎?來(lái)自caniuse.com的數(shù)據(jù)顯示主要瀏覽器對(duì)css-scroll-behavior功能的支持情況。

不幸的是,在撰寫本文時(shí),支持非常有限。在Chrome中,此功能正在開(kāi)發(fā)中,可以通過(guò)在chrome://flags屏幕中啟用它來(lái)使用部分實(shí)現(xiàn)。 CSS屬性尚未實(shí)現(xiàn),因此鏈接點(diǎn)擊上的平滑滾動(dòng)不起作用。

無(wú)論如何,通過(guò)對(duì)主腳本進(jìn)行微小的更改,我們可以檢測(cè)用戶代理中是否可用此功能並避免運(yùn)行我們的其餘代碼。為了在視口中使用平滑滾動(dòng),我們將CSS屬性應(yīng)用於根元素HTML(但在我們的測(cè)試頁(yè)面中,我們甚至可以將其應(yīng)用於body元素):

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>

然後,我們?cè)谀_本開(kāi)頭添加一個(gè)簡(jiǎn)單的功能檢測(cè)測(cè)試:

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>

因此,如果瀏覽器支持原生滾動(dòng),則腳本將不執(zhí)行任何操作並退出,否則它將像以前一樣繼續(xù)執(zhí)行,並且瀏覽器將忽略不受支持的CSS屬性。

結(jié)論

除了實(shí)現(xiàn)簡(jiǎn)單和性能之外,剛才討論的CSS解決方案的另一個(gè)優(yōu)勢(shì)是瀏覽器歷史行為與使用瀏覽器默認(rèn)滾動(dòng)時(shí)所體驗(yàn)的行為一致。每個(gè)頁(yè)面內(nèi)跳轉(zhuǎn)都推送到瀏覽器歷史堆棧上,我們可以使用相應(yīng)的按鈕來(lái)回瀏覽這些跳轉(zhuǎn)(但至少在Firefox中沒(méi)有平滑滾動(dòng))。

在我們編寫的代碼中(我們現(xiàn)在可以將其視為CSS支持不可用時(shí)的後備方案),我們沒(méi)有考慮腳本相對(duì)於瀏覽器歷史記錄的行為。根據(jù)上下文和用例,這可能是或可能不是感興趣的事情,但如果我們認(rèn)為腳本應(yīng)該增強(qiáng)默認(rèn)滾動(dòng)體驗(yàn),那麼我們應(yīng)該期望一致的行為,就像CSS一樣。

關(guān)於使用原生JavaScript進(jìn)行平滑滾動(dòng)的常見(jiàn)問(wèn)題解答 (FAQs)

如何在不使用任何庫(kù)的情況下使用原生JavaScript實(shí)現(xiàn)平滑滾動(dòng)?

在不使用任何庫(kù)的情況下使用原生JavaScript實(shí)現(xiàn)平滑滾動(dòng)非常簡(jiǎn)單。您可以使用window.scrollTo方法,並將behavior選項(xiàng)設(shè)置為smooth。此方法通過(guò)給定數(shù)量滾動(dòng)窗口中的文檔。這是一個(gè)簡(jiǎn)單的示例:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>

在此示例中,當(dāng)您點(diǎn)擊具有類your-element的元素時(shí),頁(yè)面將平滑地滾動(dòng)到頂部。

為什麼我的平滑滾動(dòng)在Safari中不起作用?

使用scrollTo方法並將behavior選項(xiàng)設(shè)置為smooth的平滑滾動(dòng)功能在Safari中不受支持。要使其正常工作,您可以使用polyfill,例如smoothscroll-polyfill。這將在原生不支持它的瀏覽器中啟用平滑滾動(dòng)功能。

如何平滑地滾動(dòng)到特定元素?

要平滑地滾動(dòng)到特定元素,您可以使用Element.scrollIntoView方法,並將behavior選項(xiàng)設(shè)置為smooth。這是一個(gè)示例:

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>

在此示例中,當(dāng)您點(diǎn)擊具有類your-element的元素時(shí),頁(yè)面將平滑地滾動(dòng)到具有類target-element的元素。

我可以控制平滑滾動(dòng)的速度嗎?

平滑滾動(dòng)的速度不能直接控制,因?yàn)樗蔀g覽器處理。但是,您可以使用window.requestAnimationFrame創(chuàng)建一個(gè)自定義平滑滾動(dòng)函數(shù),以便更好地控制滾動(dòng)動(dòng)畫,包括其速度。

如何實(shí)現(xiàn)水平平滑滾動(dòng)?

您可以通過(guò)與垂直平滑滾動(dòng)類似的方式實(shí)現(xiàn)水平平滑滾動(dòng)。 window.scrollToElement.scrollIntoView方法也接受left選項(xiàng)以指定要滾動(dòng)到的水平位置。這是一個(gè)示例:

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>

這將使文檔向右平滑滾動(dòng)100像素。

如何停止平滑滾動(dòng)動(dòng)畫?

不能直接停止平滑滾動(dòng)動(dòng)畫,因?yàn)樗蔀g覽器處理。但是,如果您使用的是自定義平滑滾動(dòng)函數(shù),則可以使用window.cancelAnimationFrame取消動(dòng)畫幀來(lái)停止動(dòng)畫。

如何實(shí)現(xiàn)具有固定頁(yè)眉的平滑滾動(dòng)?

要實(shí)現(xiàn)具有固定頁(yè)眉的平滑滾動(dòng),您需要調(diào)整滾動(dòng)位置以考慮頁(yè)眉的高度。您可以通過(guò)從目標(biāo)滾動(dòng)位置減去頁(yè)眉的高度來(lái)實(shí)現(xiàn)此目的。

如何為錨鏈接實(shí)現(xiàn)平滑滾動(dòng)?

要為錨鏈接實(shí)現(xiàn)平滑滾動(dòng),您可以向鏈接的點(diǎn)擊事件添加事件偵聽(tīng)器,並使用Element.scrollIntoView方法平滑地滾動(dòng)到目標(biāo)元素。這是一個(gè)示例:

<code>requestAnimationFrame(function(time) { timeStart = time; loop(time); });

function loop(time) {
    timeElapsed = time - timeStart;

    window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

    if (timeElapsed         requestAnimationFrame(loop)
    else
        end();
}
</code>

這將使頁(yè)面上的所有錨鏈接平滑地滾動(dòng)到其目標(biāo)元素。

如何使用鍵盤導(dǎo)航實(shí)現(xiàn)平滑滾動(dòng)?

使用鍵盤導(dǎo)航實(shí)現(xiàn)平滑滾動(dòng)比較複雜,因?yàn)樗枰獢r截鍵盤事件並手動(dòng)滾動(dòng)文檔。您可以通過(guò)向keydown事件添加事件偵聽(tīng)器並使用window.scrollTo方法平滑地滾動(dòng)文檔來(lái)實(shí)現(xiàn)此目的。

如何測(cè)試我的平滑滾動(dòng)實(shí)現(xiàn)的兼容性?

您可以使用BrowserStack等在線工具測(cè)試平滑滾動(dòng)實(shí)現(xiàn)的兼容性。這些工具允許您在不同的瀏覽器和不同的設(shè)備上測(cè)試您的網(wǎng)站,以確保您的實(shí)現(xiàn)可以在所有環(huán)境中正常工作。

以上是如何在Vanilla JavaScript中實(shí)現(xiàn)光滑的滾動(dòng)的詳細(xì)內(nèi)容。更多資訊請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願(yuàn)投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請(qǐng)聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開(kāi)發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺(jué)化網(wǎng)頁(yè)開(kāi)發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)程式碼編輯軟體(SublimeText3)

如何在JS中與日期和時(shí)間合作? 如何在JS中與日期和時(shí)間合作? Jul 01, 2025 am 01:27 AM

JavaScript中的日期和時(shí)間處理需注意以下幾點(diǎn):1.創(chuàng)建Date對(duì)像有多種方式,推薦使用ISO格式字符串以保證兼容性;2.獲取和設(shè)置時(shí)間信息可用get和set方法,注意月份從0開(kāi)始;3.手動(dòng)格式化日期需拼接字符串,也可使用第三方庫(kù);4.處理時(shí)區(qū)問(wèn)題建議使用支持時(shí)區(qū)的庫(kù),如Luxon。掌握這些要點(diǎn)能有效避免常見(jiàn)錯(cuò)誤。

為什麼要將標(biāo)籤放在的底部? 為什麼要將標(biāo)籤放在的底部? Jul 02, 2025 am 01:22 AM

PlacingtagsatthebottomofablogpostorwebpageservespracticalpurposesforSEO,userexperience,anddesign.1.IthelpswithSEObyallowingsearchenginestoaccesskeyword-relevanttagswithoutclutteringthemaincontent.2.Itimprovesuserexperiencebykeepingthefocusonthearticl

什麼是在DOM中冒泡和捕獲的事件? 什麼是在DOM中冒泡和捕獲的事件? Jul 02, 2025 am 01:19 AM

事件捕獲和冒泡是DOM中事件傳播的兩個(gè)階段,捕獲是從頂層向下到目標(biāo)元素,冒泡是從目標(biāo)元素向上傳播到頂層。 1.事件捕獲通過(guò)addEventListener的useCapture參數(shù)設(shè)為true實(shí)現(xiàn);2.事件冒泡是默認(rèn)行為,useCapture設(shè)為false或省略;3.可使用event.stopPropagation()阻止事件傳播;4.冒泡支持事件委託,提高動(dòng)態(tài)內(nèi)容處理效率;5.捕獲可用於提前攔截事件,如日誌記錄或錯(cuò)誤處理。了解這兩個(gè)階段有助於精確控制JavaScript響應(yīng)用戶操作的時(shí)機(jī)和方式。

如何減少JavaScript應(yīng)用程序的有效載荷大??? 如何減少JavaScript應(yīng)用程序的有效載荷大小? Jun 26, 2025 am 12:54 AM

如果JavaScript應(yīng)用加載慢、性能差,問(wèn)題往往出在payload太大,解決方法包括:1.使用代碼拆分(CodeSplitting),通過(guò)React.lazy()或構(gòu)建工具將大bundle拆分為多個(gè)小文件,按需加載以減少首次下載量;2.移除未使用的代碼(TreeShaking),利用ES6模塊機(jī)制清除“死代碼”,確保引入的庫(kù)支持該特性;3.壓縮和合併資源文件,啟用Gzip/Brotli和Terser壓縮JS,合理合併文件並優(yōu)化靜態(tài)資源;4.替換重型依賴,選用輕量級(jí)庫(kù)如day.js、fetch

JavaScript模塊上的確定JS綜述:ES模塊與COMPORJS JavaScript模塊上的確定JS綜述:ES模塊與COMPORJS Jul 02, 2025 am 01:28 AM

ES模塊和CommonJS的主要區(qū)別在於加載方式和使用場(chǎng)景。 1.CommonJS是同步加載,適用於Node.js服務(wù)器端環(huán)境;2.ES模塊是異步加載,適用於瀏覽器等網(wǎng)絡(luò)環(huán)境;3.語(yǔ)法上,ES模塊使用import/export,且必須位於頂層作用域,而CommonJS使用require/module.exports,可在運(yùn)行時(shí)動(dòng)態(tài)調(diào)用;4.CommonJS廣泛用於舊版Node.js及依賴它的庫(kù)如Express,ES模塊則適用於現(xiàn)代前端框架和Node.jsv14 ;5.雖然可混合使用,但容易引發(fā)問(wèn)題

如何在node.js中提出HTTP請(qǐng)求? 如何在node.js中提出HTTP請(qǐng)求? Jul 13, 2025 am 02:18 AM

在Node.js中發(fā)起HTTP請(qǐng)求有三種常用方式:使用內(nèi)置模塊、axios和node-fetch。 1.使用內(nèi)置的http/https模塊無(wú)需依賴,適合基礎(chǔ)場(chǎng)景,但需手動(dòng)處理數(shù)據(jù)拼接和錯(cuò)誤監(jiān)聽(tīng),例如用https.get()獲取數(shù)據(jù)或通過(guò).write()發(fā)送POST請(qǐng)求;2.axios是基於Promise的第三方庫(kù),語(yǔ)法簡(jiǎn)潔且功能強(qiáng)大,支持async/await、自動(dòng)JSON轉(zhuǎn)換、攔截器等,推薦用於簡(jiǎn)化異步請(qǐng)求操作;3.node-fetch提供類似瀏覽器fetch的風(fēng)格,基於Promise且語(yǔ)法簡(jiǎn)單

編寫清潔和可維護(hù)的JavaScript代碼的最佳實(shí)踐是什麼? 編寫清潔和可維護(hù)的JavaScript代碼的最佳實(shí)踐是什麼? Jun 23, 2025 am 12:35 AM

要寫出乾淨(jìng)、可維護(hù)的JavaScript代碼,應(yīng)遵循以下四點(diǎn):1.使用清晰一致的命名規(guī)範(fàn),變量名用名詞如count,函數(shù)名用動(dòng)詞開(kāi)頭如fetchData(),類名用PascalCase如UserProfile;2.避免過(guò)長(zhǎng)函數(shù)和副作用,每個(gè)函數(shù)只做一件事,如將更新用戶信息拆分為formatUser、saveUser和renderUser;3.合理使用模塊化和組件化,如在React中將頁(yè)面拆分為UserProfile、UserStats等小組件;4.寫註釋和文檔時(shí)點(diǎn)到為止,重點(diǎn)說(shuō)明關(guān)鍵邏輯、算法選

垃圾收集如何在JavaScript中起作用? 垃圾收集如何在JavaScript中起作用? Jul 04, 2025 am 12:42 AM

JavaScript的垃圾回收機(jī)制通過(guò)標(biāo)記-清除算法自動(dòng)管理內(nèi)存,以減少內(nèi)存洩漏風(fēng)險(xiǎn)。引擎從根對(duì)像出發(fā)遍歷並標(biāo)記活躍對(duì)象,未被標(biāo)記的則被視為垃圾並被清除。例如,當(dāng)對(duì)像不再被引用(如將變量設(shè)為null),它將在下一輪迴收中被釋放。常見(jiàn)的內(nèi)存洩漏原因包括:①未清除的定時(shí)器或事件監(jiān)聽(tīng)器;②閉包中對(duì)外部變量的引用;③全局變量持續(xù)持有大量數(shù)據(jù)。 V8引擎通過(guò)分代回收、增量標(biāo)記、並行/並發(fā)回收等策略優(yōu)化回收效率,降低主線程阻塞時(shí)間。開(kāi)發(fā)時(shí)應(yīng)避免不必要的全局引用、及時(shí)解除對(duì)象關(guān)聯(lián),以提升性能與穩(wěn)定性。

See all articles