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'
透過 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
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 的指令檔,應該被盡快載入的範例有:
使用 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'); `, }} />
在頁面具備互動性後立即載入的指令檔範例包括有:
使用 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: 在頁面具備互動性後不必立即載入且可以被延遲載入的指令檔範例包括有:
注意:
worker
strategy 尚未穩定且可能會導致應用程式產生不可預期的問題。使用上請多加注意
使用 worker
strategy 的指令檔在 web worker 內和 Partytown 被重置和執行。
此 strategy 目前是處於試驗街端,只能被用於比如 nextScriptWorkers
flag 在 next.config.js
被啟用的情況下:
module.exports = { experimental: { nextScriptWorkers: true, }, }
接著,執行 next
(通常是 npm run dev
或 yarn 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 的配置,以下條件必須被滿足:
data-partytown-config
必須被使用來改寫 Next.js 的預設配置設定。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
)注意:
onLoad
在beforeInteractive
載入 strategy 上使用. 請考慮使用onReady
作為代替
有一些第三方指令檔要求使用者在指令檔完成載入後執行一次 JavaScript 程式碼,以利實例化內容或呼叫一個函式。如果你使用 afterInteractive
或 lazyOnload
來作為載入的 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" /> </> ) }