@cunpingtai/webad-react-ad-scripts 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -218
- package/dist/index.d.ts +9 -25
- package/dist/index.js +145 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @cunpingtai/webad-react-ad-scripts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
给 Next.js 和 TanStack React 项目使用的中央广告组件包。网站只写广告位名称,具体 Adsterra 参数和显示策略由 WebAd 后台按当前 hostname 下发。
|
|
4
4
|
|
|
5
5
|
## 安装
|
|
6
6
|
|
|
@@ -10,252 +10,80 @@ pnpm add @cunpingtai/webad-react-ad-scripts
|
|
|
10
10
|
|
|
11
11
|
需要 React 18.3 或 React 19。
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## 根部接入
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Next.js App Router:
|
|
16
16
|
|
|
17
17
|
```tsx
|
|
18
|
-
// app/advertising-provider.tsx
|
|
19
18
|
"use client";
|
|
20
19
|
|
|
21
20
|
import { AdsProvider } from "@cunpingtai/webad-react-ad-scripts";
|
|
22
21
|
import { usePathname } from "next/navigation";
|
|
23
|
-
import type { ReactNode } from "react";
|
|
24
22
|
|
|
25
|
-
export function AdvertisingProvider({ children }:
|
|
23
|
+
export function AdvertisingProvider({ children }: React.PropsWithChildren) {
|
|
26
24
|
const pathname = usePathname();
|
|
27
|
-
|
|
28
25
|
return (
|
|
29
|
-
<AdsProvider
|
|
30
|
-
endpoint="https://webad-control-plane.example.workers.dev"
|
|
31
|
-
pageKey={pathname}
|
|
32
|
-
>
|
|
26
|
+
<AdsProvider endpoint="https://a1.yumeref.com" pageKey={pathname}>
|
|
33
27
|
{children}
|
|
34
28
|
</AdsProvider>
|
|
35
29
|
);
|
|
36
30
|
}
|
|
37
31
|
```
|
|
38
32
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
```tsx
|
|
42
|
-
// app/layout.tsx
|
|
43
|
-
import { AdvertisingProvider } from "./advertising-provider";
|
|
44
|
-
|
|
45
|
-
export default function RootLayout({
|
|
46
|
-
children,
|
|
47
|
-
}: {
|
|
48
|
-
children: React.ReactNode;
|
|
49
|
-
}) {
|
|
50
|
-
return (
|
|
51
|
-
<html lang="zh-CN">
|
|
52
|
-
<body>
|
|
53
|
-
<AdvertisingProvider>{children}</AdvertisingProvider>
|
|
54
|
-
</body>
|
|
55
|
-
</html>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
## TanStack Router / Start
|
|
33
|
+
把 `AdvertisingProvider` 放在根 layout 中一次。TanStack Router 同理,在根路由读取 `state.location.pathname` 作为 `pageKey`。
|
|
61
34
|
|
|
62
|
-
|
|
35
|
+
`pageKey` 只在真实路由切换时变化。不要传随机数、时间戳、Tab 状态或窗口宽度。
|
|
63
36
|
|
|
64
|
-
|
|
65
|
-
import {
|
|
66
|
-
HeadContent,
|
|
67
|
-
Outlet,
|
|
68
|
-
Scripts,
|
|
69
|
-
createRootRoute,
|
|
70
|
-
useRouterState,
|
|
71
|
-
} from "@tanstack/react-router";
|
|
72
|
-
import { AdsProvider } from "@cunpingtai/webad-react-ad-scripts";
|
|
37
|
+
## 后台配置
|
|
73
38
|
|
|
74
|
-
|
|
39
|
+
打开 `https://a1.yumeref.com/admin`,编辑对应站点:
|
|
75
40
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
41
|
+
- Social Bar、Popunder、Smartlink 每站各配置一个;
|
|
42
|
+
- Banner 和 Native Banner 可以添加多个;
|
|
43
|
+
- 每个页面广告位使用唯一名称,例如 `home-top`、`article-middle`、`sidebar-sticky`;
|
|
44
|
+
- Banner 填脚本地址、广告 Key、宽度和高度;
|
|
45
|
+
- Native Banner 填脚本地址和 Container ID。
|
|
80
46
|
|
|
81
|
-
|
|
82
|
-
<html lang="zh-CN">
|
|
83
|
-
<head>
|
|
84
|
-
<HeadContent />
|
|
85
|
-
</head>
|
|
86
|
-
<body>
|
|
87
|
-
<AdsProvider
|
|
88
|
-
endpoint="https://webad-control-plane.example.workers.dev"
|
|
89
|
-
pageKey={pathname}
|
|
90
|
-
>
|
|
91
|
-
<Outlet />
|
|
92
|
-
</AdsProvider>
|
|
93
|
-
<Scripts />
|
|
94
|
-
</body>
|
|
95
|
-
</html>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
```
|
|
47
|
+
包会用浏览器当前 hostname 自动获取该站点配置,不需要额外传网站标识。
|
|
99
48
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
## 广告组件
|
|
103
|
-
|
|
104
|
-
Adsterra 提供的 zone key、脚本 URL 和容器 ID 由使用方传入。不要传整段 HTML。
|
|
105
|
-
|
|
106
|
-
### Social Bar
|
|
49
|
+
## 组件
|
|
107
50
|
|
|
108
51
|
```tsx
|
|
109
|
-
import {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
|
|
126
|
-
scriptSrc="https://native.ads-cdn.example/widget/invoke.js"
|
|
127
|
-
containerId="container-native-example"
|
|
128
|
-
className="my-native-ad"
|
|
129
|
-
/>;
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
`containerId` 必须与该 Native Banner 代码对应,并且在当前文档中唯一。
|
|
133
|
-
|
|
134
|
-
### Banner 320×50、300×250、728×90
|
|
135
|
-
|
|
136
|
-
同一个组件支持不同固定尺寸:
|
|
137
|
-
|
|
138
|
-
```tsx
|
|
139
|
-
import { AdsterraBanner } from "@cunpingtai/webad-react-ad-scripts";
|
|
140
|
-
|
|
141
|
-
<AdsterraBanner
|
|
142
|
-
slotId="mobile-top"
|
|
143
|
-
scriptSrc="https://banner.ads-cdn.example/mobile/invoke.js"
|
|
144
|
-
zoneKey="exampleMobileZone"
|
|
145
|
-
width={320}
|
|
146
|
-
height={50}
|
|
147
|
-
/>
|
|
148
|
-
|
|
149
|
-
<AdsterraBanner
|
|
150
|
-
slotId="content-box"
|
|
151
|
-
scriptSrc="https://banner.ads-cdn.example/box/invoke.js"
|
|
152
|
-
zoneKey="exampleBoxZone"
|
|
153
|
-
width={300}
|
|
154
|
-
height={250}
|
|
155
|
-
params={{ placement: "article" }}
|
|
156
|
-
/>
|
|
157
|
-
|
|
158
|
-
<AdsterraBanner
|
|
159
|
-
slotId="desktop-top"
|
|
160
|
-
scriptSrc="https://banner.ads-cdn.example/wide/invoke.js"
|
|
161
|
-
zoneKey="exampleWideZone"
|
|
162
|
-
width={728}
|
|
163
|
-
height={90}
|
|
164
|
-
/>;
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
组件按 Adsterra 原始代码的方式设置 `window.atOptions` 并加载 `invoke.js`,不会自行增加外层 iframe。多个 Banner 会串行初始化,避免互相覆盖 `atOptions`。
|
|
168
|
-
|
|
169
|
-
### Smartlink
|
|
170
|
-
|
|
171
|
-
```tsx
|
|
172
|
-
import { AdsterraSmartlink } from "@cunpingtai/webad-react-ad-scripts";
|
|
173
|
-
|
|
174
|
-
<AdsterraSmartlink
|
|
175
|
-
href="https://links.example/go?key=example"
|
|
176
|
-
className="download-button"
|
|
177
|
-
>
|
|
178
|
-
Continue
|
|
179
|
-
</AdsterraSmartlink>;
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
Smartlink 是普通 `<a>`,默认新窗口打开,并固定带有 `noopener noreferrer sponsored`。
|
|
183
|
-
|
|
184
|
-
### Popunder
|
|
185
|
-
|
|
186
|
-
```tsx
|
|
187
|
-
import { AdsterraPopunder } from "@cunpingtai/webad-react-ad-scripts";
|
|
188
|
-
|
|
189
|
-
<AdsterraPopunder
|
|
190
|
-
slotId="global-popunder"
|
|
191
|
-
scriptSrc="https://pop.ads-cdn.example/pop.js"
|
|
192
|
-
/>;
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
Popunder 在一次完整页面加载中最多初始化一次,仅在 `adsterra-full` 模式显示。
|
|
196
|
-
|
|
197
|
-
## 策略与显示规则
|
|
198
|
-
|
|
199
|
-
| Worker 返回模式 | Native Banner | Banner | Smartlink | Social Bar | Popunder |
|
|
200
|
-
| ----------------- | ------------- | ------ | --------- | ---------- | -------- |
|
|
201
|
-
| `none` | 否 | 否 | 否 | 否 | 否 |
|
|
202
|
-
| `adsense` | 否 | 否 | 否 | 否 | 否 |
|
|
203
|
-
| `adsense-limited` | 是 | 是 | 否 | 否 | 否 |
|
|
204
|
-
| `adsterra-safe` | 是 | 是 | 是 | 否 | 否 |
|
|
205
|
-
| `adsterra-full` | 是 | 是 | 是 | 是 | 是 |
|
|
206
|
-
|
|
207
|
-
AdSense Auto Ads 等全局脚本仍由 Worker 配置中的 `providers.*.scripts` 加载。Adsterra 广告位脚本由上述组件加载,因此使用组件方案时通常把 Worker 的 `adsterra.safeScripts` 和 `adsterra.fullScripts` 留空,避免重复加载。
|
|
208
|
-
|
|
209
|
-
## 生命周期与去重
|
|
210
|
-
|
|
211
|
-
- 同一个 `pageKey` 中,相同 `slotId` 只初始化一次。
|
|
212
|
-
- React 普通 re-render、Strict Mode effect 重放和同页重新挂载不会产生第二次广告请求。
|
|
213
|
-
- `pageKey` 改变后,Native Banner 和 Banner 可以按新的 page view 重新初始化。
|
|
214
|
-
- Social Bar 和 Popunder 无法可靠卸载,因此一次完整文档生命周期只初始化一次;SPA 路由切换不会重复加载。
|
|
215
|
-
- 不要使用随机 React `key`,也不要因为 resize、Tab、FAQ 展开或游戏状态变化而改变 `pageKey`。
|
|
216
|
-
|
|
217
|
-
## AdsProvider 参数与状态
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
type AdsProviderProps = {
|
|
221
|
-
endpoint: string;
|
|
222
|
-
pageKey: string;
|
|
223
|
-
timeoutMs?: number; // 默认 3000
|
|
224
|
-
nonce?: string;
|
|
225
|
-
onStatusChange?: (status: AdsProviderStatus) => void;
|
|
226
|
-
children: React.ReactNode;
|
|
227
|
-
};
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
状态包括 `loading-config`、`disabled`、`loading-scripts`、`ready` 和 `error`。例如:
|
|
231
|
-
|
|
232
|
-
```tsx
|
|
233
|
-
<AdsProvider
|
|
234
|
-
endpoint="https://webad-control-plane.example.workers.dev"
|
|
235
|
-
pageKey={pathname}
|
|
236
|
-
nonce={cspNonce}
|
|
237
|
-
onStatusChange={(status) => {
|
|
238
|
-
if (status.state === "error") {
|
|
239
|
-
console.error("Advertising unavailable", status.reason);
|
|
240
|
-
}
|
|
241
|
-
}}
|
|
242
|
-
>
|
|
243
|
-
{children}
|
|
244
|
-
</AdsProvider>
|
|
52
|
+
import {
|
|
53
|
+
AdsterraBanner,
|
|
54
|
+
AdsterraNativeBanner,
|
|
55
|
+
AdsterraPopunder,
|
|
56
|
+
AdsterraSmartlink,
|
|
57
|
+
AdsterraSocialBar,
|
|
58
|
+
} from "@cunpingtai/webad-react-ad-scripts";
|
|
59
|
+
|
|
60
|
+
<AdsterraBanner placement="home-top" />
|
|
61
|
+
<AdsterraNativeBanner placement="article-middle" />
|
|
62
|
+
|
|
63
|
+
<AdsterraSmartlink className="download-button">
|
|
64
|
+
继续下载
|
|
65
|
+
</AdsterraSmartlink>
|
|
66
|
+
|
|
67
|
+
<AdsterraSocialBar />
|
|
68
|
+
<AdsterraPopunder />
|
|
245
69
|
```
|
|
246
70
|
|
|
247
|
-
|
|
71
|
+
Banner 和 Native Banner 还支持 `className`、`style`。组件找不到对应配置、类型不匹配或当前策略不允许时会返回 `null`。
|
|
248
72
|
|
|
249
|
-
##
|
|
73
|
+
## 策略
|
|
250
74
|
|
|
251
|
-
|
|
75
|
+
| 模式 | Banner / Native | Smartlink | Social Bar / Popunder |
|
|
76
|
+
| ----------------- | --------------- | --------- | --------------------- |
|
|
77
|
+
| `none` | 否 | 否 | 否 |
|
|
78
|
+
| `adsense` | 否 | 否 | 否 |
|
|
79
|
+
| `adsense-limited` | 是 | 否 | 否 |
|
|
80
|
+
| `adsterra-safe` | 是 | 是 | 否 |
|
|
81
|
+
| `adsterra-full` | 是 | 是 | 是 |
|
|
252
82
|
|
|
253
|
-
-
|
|
254
|
-
- hostname 精确存在于 Worker 的 `ALLOWED_SCRIPT_HOSTS_JSON`;
|
|
255
|
-
- 不包含账号密码、显式端口或 URL fragment。
|
|
83
|
+
同一个 `pageKey` 中,同一个 `placement` 只初始化一次。普通 React re-render 和 Strict Mode 不会重复请求;真实 SPA 路由切换后可以重新初始化。Social Bar 和 Popunder 在整个文档生命周期各只加载一次。
|
|
256
84
|
|
|
257
|
-
|
|
85
|
+
## CSP
|
|
258
86
|
|
|
259
|
-
|
|
87
|
+
站点 CSP 的 `connect-src` 需要允许 `https://a1.yumeref.com`,`script-src` 需要允许后台中实际填写的广告脚本域名。`ALLOWED_SCRIPT_HOSTS_JSON` 只约束 Worker 集中加载的 provider 脚本,不约束这些站点广告位脚本。
|
|
260
88
|
|
|
261
|
-
|
|
89
|
+
完整的页面布局和广告数量规则见仓库中的 `docs/site-ad-integration-guide.md`。
|
package/dist/index.d.ts
CHANGED
|
@@ -30,46 +30,30 @@ type AdsProviderProps = {
|
|
|
30
30
|
};
|
|
31
31
|
declare function AdsProvider({ endpoint, pageKey, timeoutMs, nonce, onStatusChange, children, }: AdsProviderProps): react.JSX.Element;
|
|
32
32
|
|
|
33
|
-
type AdsterraParams = Record<string, string | number | boolean>;
|
|
34
|
-
|
|
35
33
|
type AdsterraBannerProps = {
|
|
36
|
-
|
|
37
|
-
scriptSrc: string;
|
|
38
|
-
zoneKey: string;
|
|
39
|
-
width: number;
|
|
40
|
-
height: number;
|
|
41
|
-
params?: AdsterraParams;
|
|
34
|
+
placement: string;
|
|
42
35
|
className?: string;
|
|
43
36
|
style?: CSSProperties;
|
|
44
37
|
};
|
|
45
|
-
declare function AdsterraBanner({
|
|
38
|
+
declare function AdsterraBanner({ placement, className, style, }: AdsterraBannerProps): react.JSX.Element | null;
|
|
46
39
|
|
|
47
40
|
type AdsterraNativeBannerProps = {
|
|
48
|
-
|
|
49
|
-
scriptSrc: string;
|
|
50
|
-
containerId: string;
|
|
41
|
+
placement: string;
|
|
51
42
|
className?: string;
|
|
52
43
|
style?: CSSProperties;
|
|
53
44
|
};
|
|
54
|
-
declare function AdsterraNativeBanner({
|
|
45
|
+
declare function AdsterraNativeBanner({ placement, className, style, }: AdsterraNativeBannerProps): react.JSX.Element | null;
|
|
55
46
|
|
|
56
47
|
type AdsterraSmartlinkProps = {
|
|
57
|
-
href: string;
|
|
58
48
|
children: ReactNode;
|
|
59
49
|
target?: HTMLAttributeAnchorTarget;
|
|
60
50
|
className?: string;
|
|
61
51
|
};
|
|
62
|
-
declare function AdsterraSmartlink({
|
|
52
|
+
declare function AdsterraSmartlink({ children, target, className, }: AdsterraSmartlinkProps): react.JSX.Element | null;
|
|
63
53
|
|
|
64
|
-
type AdsterraSocialBarProps =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
type AdsterraPopunderProps = {
|
|
69
|
-
slotId: string;
|
|
70
|
-
scriptSrc: string;
|
|
71
|
-
};
|
|
72
|
-
declare function AdsterraSocialBar(props: AdsterraSocialBarProps): react.JSX.Element;
|
|
73
|
-
declare function AdsterraPopunder(props: AdsterraPopunderProps): react.JSX.Element;
|
|
54
|
+
type AdsterraSocialBarProps = Record<string, never>;
|
|
55
|
+
type AdsterraPopunderProps = Record<string, never>;
|
|
56
|
+
declare function AdsterraSocialBar(_props: AdsterraSocialBarProps): react.JSX.Element;
|
|
57
|
+
declare function AdsterraPopunder(_props: AdsterraPopunderProps): react.JSX.Element;
|
|
74
58
|
|
|
75
59
|
export { AdsProvider, type AdsProviderProps, type AdsProviderStatus, AdsterraBanner, type AdsterraBannerProps, AdsterraNativeBanner, type AdsterraNativeBannerProps, AdsterraPopunder, type AdsterraPopunderProps, AdsterraSmartlink, type AdsterraSmartlinkProps, AdsterraSocialBar, type AdsterraSocialBarProps };
|