由于在web开发中的项目需要商业化,因此需要引用一些外部脚本,比如谷歌广告、谷歌分析等脚本,本文针对在next项目中如何快速引用外部脚本做一个总结
1、next引用脚本的方式:
a、引用外部脚本
import Script from 'next/script'
<Script src="https://securepubads.g.doubleclick.net/tag/js/gpt.js" />
- strategy属性:
- beforeInteractive:在任何 Next.js 代码之前以及任何页面水合发生之前加载(主要在next相关js代码加载之前)
- afterInteractive:尽早加载,但在页面发生一些水合作用之后(主要在next相关js代码加载之后)
- lazyOnload:在浏览器空闲时加载
在next
的pages路由
模式下分别测试:
一、使用 beforeInteractive
//在pages路由下:
<Script
src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver"
strategy="beforeInteractive"
/>
//案列地址:https://solutions-script-component-strategies.vercel.app/polyfill
//案列代码:https://github.com/vercel/examples/blob/main/solutions/script-component-strategies/pages/polyfill.tsx
查看pages路由
测试结果:
总结:由图可知主要加载顺序是polyfill.io/v3/polyfill
>next
的脚本,同时主要使用的是defer
脚本加载方式
二、使用 afterInteractive
,策略的默认值
//在pages路由下:
<Script
src="https://js.stripe.com/v3/"
strategy="afterInteractive"
onLoad={() =>
addLog(`script loaded correctly, window.Stripe has been populated`)
}
/>
//案列地址:https://solutions-script-component-strategies.vercel.app/stripe
//案列代码:https://github.com/vercel/examples/blob/main/solutions/script-component-strategies/pages/stripe.tsx
总结:由图可知主要加载顺序是
next
>js.stripe.com/v3/
的脚本,是通过next下客户端脚本main脚本,查看源码发现是调用document.createElement("script")
和document.body.appendChild(el)
动态追加script标签
三、使用 lazyOnload
//在pages路由下:
<Script
src="https://connect.facebook.net/en_US/sdk.js"
strategy="lazyOnload"
onLoad={() =>
addLog(`script loaded correctly, window.FB has been populated`)
}
/>
//案列地址:https://solutions-script-component-strategies.vercel.app/fb
//案列代码:https://github.com/vercel/examples/blob/main/solutions/script-component-strategies/pages/fb.tsx
总结:由图可知主要加载顺序是
next
>connect.facebook.net/en_US/sdk
的脚本,是通过next下客户端脚本main脚本,查看源码分析是由document.readyState === "complete"或者监听load事件
后调用document.createElement("script")
和document.body.appendChild(el)
动态追加script标签
在next
的app路由
下测试
一、使用 beforeInteractive
总结:由图可知,因为使用了preload,导致加载几乎一样。
二、使用 afterInteractive
,策略的默认值
总结:由图可知,因为使用了preload,导致加载几乎一样,加载位置还是可以看出相对于
三、使用 lazyOnload
总结:由图可知,懒加载的脚本也是在next脚本加载完后,由
document.readyState === "complete"或者监听load事件
后调用document.createElement("script")
和document.body.appendChild(el)
动态追加script标签
b、引用内联脚本
也支持策略,此处就可以直接使用谷歌分析代码,全局通用
<Script id="Analytics">
{`
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', "xxxxxx");
`}
</Script>
2、加载方式说明
-
async
属性的脚本执行顺序:- 异步加载的脚本会在下载完成后立即执行,而不会等待其他资源(包括其他脚本)的加载和执行。
- 如果页面中有多个带有
async
属性的脚本,它们的执行顺序将取决于它们的下载完成时间。即使在 HTML 中按顺序定义了这些脚本,它们也可能以任意顺序执行。
-
defer
属性的脚本执行顺序:- 延迟加载的脚本会按照它们在 HTML 中的顺序执行。这意味着先出现在 HTML 中的脚本会先执行,后出现的脚本会后执行。
defer
属性使得脚本的执行被延迟到 HTML 解析完成之后,但在DOMContentLoaded
事件之前。
-
preload
- 预加载脚本:
preload
属性告诉浏览器在页面加载期间预加载脚本文件,即使在遇到<script>
标签之前也可以开始下载脚本文件。这样,在需要执行脚本时,可以更快地从浏览器缓存中获取已经下载好的脚本文件。 - 并行下载:通过使用
preload
属性,浏览器可以并行下载脚本文件,这样可以更快地获取脚本资源,而不会阻塞其他资源的下载和页面渲染。 - 提前解析和编译:预加载脚本还可以让浏览器在下载阶段就开始解析和编译脚本文件,以便在需要执行时能够更快地执行。这有助于减少执行时的延迟,并提高页面的响应速度。
preload
属性仅在支持该功能的现代浏览器中起作用。对于不支持preload
属性的旧浏览器,它将被忽略,而脚本将按照正常的方式进行加载和执行
- 预加载脚本:
3、本地项目使用
由于需要在本地测试广告、使用adsense本地测试效果,出现测试广告的概率很低,因此使用admanager来测试。使用admanager中间测试时候会出现一些细节问题:
- admanager的gpt代码,需要给页面上的元素设置id,后续广告展示会直接放在这个容器里,如果切换其他页面,这个id不变的话广告会异常。
- 因为next第一次页面刷新是服务端渲染的页面,在这个页面访问其他页面就是客户端渲染了。两个页面来回切换,其实还是处于一个页面而已,会导致广告代码重复使用
<Script
src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"
/>
"use client";
import { Skeleton } from "../../module/commom/skeleton";
import { useEffect, useRef } from "react";
export interface IHorizontalAdProps {
id: number;
}
export default function HorizontalAd({ id }: IHorizontalAdProps) {
const adRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const current = adRef.current;
if (current) {
const newId = `ad-${Date.now()}-${id}`;
current?.setAttribute("id", newId);
const googletag = (window.googletag = window.googletag || { cmd: [] });
googletag.cmd.push(function () {
const responsiveAdSlot = googletag
.defineSlot(
"/6355419/Travel/Europe",
[
[300, 250],
[728, 90],
[750, 200],
],
`${newId}`
)
.addService(googletag.pubads());
const mapping = googletag
.sizeMapping()
.addSize(
[1440, 768],
[
[750, 200],
[728, 90],
]
)
.addSize([0, 0], [300, 250])
.build();
responsiveAdSlot.defineSizeMapping(mapping);
googletag.enableServices();
googletag.cmd.push(() => {
googletag.display(`${newId}`);
});
});
}
}, [id]);
return (
<>
<div className="flex flex-col justify-center items-center w-full h-[320px] mt-[.89rem] bg-[#222] xl:rounded-[.89rem] xl:mt-[.89rem] xl:h-[13.11rem]">
Ad
<div
ref={adRef}
className="w-[300px] h-[250px] xl:w-[700px] xl:h-[200px]"
>
<Skeleton num={1} className="smn:w-full smn:h-full"></Skeleton>
</div>
</div>
</>
);
}