071ad353a6c4049f4b31583a95892a6a6989f912

指令檔組件

範例
版本歷史
版本更動
v12.2.4增加 onReady prop
v11.0.0釋出 next/script

Next.js 指令檔組件,next/script 是 HTML <script> 元素的一個擴充功能。它允許開發者在他們的應用程式的任何位置,在 next/head 以外的地方設置第三方指令檔載入的優先權,節省開發者的時間外同時也提高載入效能。

import Script from 'next/script' export default function Home() { return ( <> <Script src="https://www.google-analytics.com/analytics.js" /> </> ) }

概述

網頁通常會使用第三方指令檔,包含不一樣形式的功能,例如:分析、廣告、客戶支援的小工具和同意聲明管理,這些可能會造成一些影響使用者和開發者體驗的問題:

  • 有些第三方指令檔對於載入的效能來說是相當沈重的,並且會降低使用者體驗,特別是如果禁止運轉和延遲任何頁面內容的載入。
  • 開發人員通常會難以決定在應用程式中放置第三方指令檔的位置來確保最理想的載入。

指令檔組件讓開發人員能輕鬆地在應用程式的任何位置中放置第三方指令檔的同時,也負責最佳化其載入策略。

應用

To add a third-party script to your application, import the next/script component: 若要在你的應用程式增加一個第三方指令檔,請引入 next/script 組件:

import Script from 'next/script'

Strategy

透過 next/script,你可以使用 strategy 屬性來決定何時載入第三方指令檔。

<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />

There are four different loading strategies that can be used: 總共有 4 種不同的載入策略可以使用:

  • beforeInteractive: 在頁面互動前載入。
  • afterInteractive: (預設) 在頁面即將為互動之後立即載入。
  • lazyOnload: 在閒置期間載入。
  • worker: (實驗性) 載入一個 web worker。

beforeInteractive

使用 beforeInteractive strategy 指令檔來進行載入的將會從伺服器中初始 HTML 被嵌入,並且在自行打包 JavaScript 開始前執行。此 strategy 應用在任何需要在頁面互動之前獲取和執行的關鍵指令檔。只會在 _document.js 內執行並且是被設計來載入整個頁面所需的指令檔(例如:指令檔將會在應用程式中的任何頁面在伺服器端載入時被載入。)

beforeInteractive 被設計為只能在 \_document.js 中運作的原因是為了支持串流和 Suspense 功能,在 _document 之外的,則無法保證其時間或 beforeInteractive 指令檔順序。

// In _document.js import { Html, Head, Main, NextScript } from 'next/document' import Script from 'next/script' export default function Document() { return ( <Html> <Head /> <body> <Main /> <NextScript /> <Script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" strategy="beforeInteractive" ></Script> </body> </Html> ) }

注意: beforeInteractive 指令檔將只會被嵌入在 HTML 文件的 head 無論該文件被放置在 _document.js 內的哪一處。

使用這個 strategy 的指令檔,應該被盡快載入的範例有:

  • 機器人檢測
  • Cookie 同意聲明管理工具

afterInteractive

使用 afterInteractive strategy 指令檔是被嵌入在客戶端且將會在 Next.js 補充頁面後開始執行。這個 strategy 適用於不需要被立即載入的且可以在頁面具備互動性後立即被擷取和執行的指令檔。

<Script id="google-analytics" strategy="afterInteractive" dangerouslySetInnerHTML={{ __html: ` (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); })(window,document,'script','dataLayer', 'GTM-XXXXXX'); `, }} />

在頁面具備互動性後立即載入的指令檔範例包括有:

  • Tag managers
  • Analytics

lazyOnload

使用 lazyOnload strategy 的指令檔在擷取所有資源後和處於閒置時間時的載入較為晚。此 strategy 適用於那些不需要在頁面之前或立即具備互動性之後的背景或低優先級的指令檔

<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />

Examples of scripts that do not need to load immediately and can be lazy-loaded include: 在頁面具備互動性後不必立即載入且可以被延遲載入的指令檔範例包括有:

  • 聊天支援外掛
  • 社群平台擴充

卸載指令檔到作為一個 Web Worker (試驗性)

注意: worker strategy 尚未穩定且可能會導致應用程式產生不可預期的問題。使用上請多加注意

使用 worker strategy 的指令檔在 web worker 內和 Partytown 被重置和執行。

此 strategy 目前是處於試驗街端,只能被用於比如 nextScriptWorkers flag 在 next.config.js 被啟用的情況下:

module.exports = { experimental: { nextScriptWorkers: true, }, }

接著,執行 next(通常是 npm run devyarn dev),Next.js 將會指引你安裝所需的套件直至完成設定:

npm run dev # 你將會看到相關的指導: # # 請輸入以下指令安裝 Partytown : # # npm install @builder.io/partytown # # ...

一但設定都完成了,定義 strategy="worker" 將會自動實例化 Partytown 到你的應用程式內且會卸載指令檔到作為一個 web worker。

<Script src="https://example.com/analytics.js" strategy="worker" />

在 web worker 中載入第三方指令檔時,有許多要權衡的。更多資訊請參考 Partytown 的 Trade-Offs 官方文件。

配置

雖然 worker startegy 不需要額外的配置才能執行,Partytown 支援使用配置物件來修改它的設定,包括啟用 debug 模式、事件轉寄和觸發。

如果你想要增加額外的配置選擇,你可以將它包含在 custom _document.js<Head /> 組件內:

import { Html, Head, Main, NextScript } from 'next/document' export default function Document() { return ( <Html> <Head> <script data-partytown-config dangerouslySetInnerHTML={{ __html: ` partytown = { lib: "/_next/static/~partytown/", debug: true }; `, }} /> </Head> <body> <Main /> <NextScript /> </body> </Html> ) }

為了修改 Partytown 的配置,以下條件必須被滿足:

  1. data-partytown-config 必須被使用來改寫 Next.js 的預設配置設定。
  2. 除非你決定要在另外的目錄中儲存 Partytown 的函式庫檔案,lib: "/_next/static/~partytown/" 屬性和值必須包含在配置內以利 Partytown 知道 Next.js 在哪裡儲存必要的靜態檔案。

注意: 如果你使用 asset prefix 且希望改寫 Partytown 的預設配置設定,你必須將它包含在 lib 路徑的一部分。

您可以到 Partytown 的 configuration options 來檢閱所有可以被增加的屬性。

行內指令檔

行內指令檔,或者未從被外部檔案載入的指令檔,也被 Script 組件支援。可以透過把 JavaScript 放置在大括號內來編寫它們:

<Script id="show-banner" strategy="lazyOnload"> {`document.getElementById('banner').classList.remove('hidden')`} </Script>

或者使用 dangerouslySetInnerHTML 屬性:

<Script id="show-banner" strategy="lazyOnload" dangerouslySetInnerHTML={{ __html: `document.getElementById('banner').classList.remove('hidden')`, }} />

The id property is required for inline scripts in order for Next.js to track and optimize the script. 為了讓 Next.js 追蹤和最佳化指令檔,id 屬性對於行內指令檔是必要的,

載入後執行程式碼 (onLoad)

注意: onLoadbeforeInteractive 載入 strategy 上使用. 請考慮使用 onReady 作為代替

有一些第三方指令檔要求使用者在指令檔完成載入後執行一次 JavaScript 程式碼,以利實例化內容或呼叫一個函式。如果你使用 afterInteractivelazyOnload 來作為載入的 strategy, 你可以在使用 onLoad 屬性載入程式碼後執行:

import { useState } from 'react' import Script from 'next/script' export default function Home() { const [stripe, setStripe] = useState(null) return ( <> <Script id="stripe-js" src="https://js.stripe.com/v3/" onLoad={() => { setStripe({ stripe: window.Stripe('pk_test_12345') }) }} /> </> ) }

掛載後執行程式碼 (onReady)

一些第三方指令檔要求使用者在指令檔完成載入後及每次掛載組件時(例如路徑導航後)執行 JavaScript 程式碼。你可以在指令檔首次載入 load 事件之後執行程式碼,然後再使用 onReady 屬性重新安裝每個後續組件後執行程式碼:

import { useRef } from 'react' import Script from 'next/script' export default function Home() { const mapRef = useRef() return ( <> <div ref={mapRef}></div> <Script id="google-maps" src="https://maps.googleapis.com/maps/api/js" onReady={() => { new google.maps.Map(mapRef.current, { center: { lat: -34.397, lng: 150.644 }, zoom: 8, }) }} /> </> ) }

處理錯誤 (onError)

注意: onError 無法與 beforeInteractive 載入 strategy 一起使用。

有時在指令檔載入失敗時捕捉錯誤會很有幫助。這些錯誤可以使用 onError 屬性處理:

import Script from 'next/script' export default function Home() { return ( <> <Script id="will-fail" src="https://example.com/non-existant-script.js" onError={(e) => { console.error('Script failed to load', e) }} /> </> ) }

額外的屬性

有許多 DOM 屬性可以被分配給不被 Script 組件使用的 <script> 元素,例如 nonce自訂 data 屬性。包含任何附加屬性將自動地轉發並輸出到最終且最佳化過的 <script> 元素。

import Script from 'next/script' export default function Home() { return ( <> <Script src="https://www.google-analytics.com/analytics.js" id="analytics" nonce="XUENAJFW" data-test="analytics" /> </> ) }
本篇文章由AndreaFan123

AndreaFan123

貢獻翻譯。瞭解如何參與貢獻