@cunpingtai/webad-react-ad-scripts 0.1.0 → 0.2.1
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 +48 -217
- package/dist/index.d.ts +9 -25
- package/dist/index.js +156 -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,83 @@ 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
|
|
61
|
-
|
|
62
|
-
在根路由组件中读取当前 pathname:
|
|
63
|
-
|
|
64
|
-
```tsx
|
|
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";
|
|
73
|
-
|
|
74
|
-
export const Route = createRootRoute({ component: RootComponent });
|
|
75
|
-
|
|
76
|
-
function RootComponent() {
|
|
77
|
-
const pathname = useRouterState({
|
|
78
|
-
select: (state) => state.location.pathname,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return (
|
|
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
|
-
```
|
|
99
|
-
|
|
100
|
-
不要给 `pageKey` 传 `Date.now()` 或随机数。它应当只在真实 SPA 路由切换时变化。
|
|
101
|
-
|
|
102
|
-
## 广告组件
|
|
103
|
-
|
|
104
|
-
Adsterra 提供的 zone key、脚本 URL 和容器 ID 由使用方传入。不要传整段 HTML。
|
|
105
|
-
|
|
106
|
-
### Social Bar
|
|
107
|
-
|
|
108
|
-
```tsx
|
|
109
|
-
import { AdsterraSocialBar } from "@cunpingtai/webad-react-ad-scripts";
|
|
110
|
-
|
|
111
|
-
<AdsterraSocialBar
|
|
112
|
-
slotId="global-social-bar"
|
|
113
|
-
scriptSrc="https://social.ads-cdn.example/social.js"
|
|
114
|
-
/>;
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Social Bar 在一次完整页面加载中最多初始化一次,仅在 `adsterra-full` 模式显示。
|
|
33
|
+
把 `AdvertisingProvider` 放在根 layout 中一次。TanStack Router 同理,在根路由读取 `state.location.pathname` 作为 `pageKey`。
|
|
118
34
|
|
|
119
|
-
|
|
35
|
+
`pageKey` 只在真实路由切换时变化。不要传随机数、时间戳、Tab 状态或窗口宽度。
|
|
120
36
|
|
|
121
|
-
|
|
122
|
-
import { AdsterraNativeBanner } from "@cunpingtai/webad-react-ad-scripts";
|
|
123
|
-
|
|
124
|
-
<AdsterraNativeBanner
|
|
125
|
-
slotId="article-native-1"
|
|
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`。
|
|
37
|
+
本地开发环境不会显示广告。`localhost`、`*.localhost`、`127.0.0.1` 和 `::1` 不会请求中央配置。
|
|
183
38
|
|
|
184
|
-
|
|
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
|
-
```
|
|
39
|
+
## 后台配置
|
|
194
40
|
|
|
195
|
-
|
|
41
|
+
打开 `https://a1.yumeref.com/admin`,编辑对应站点:
|
|
196
42
|
|
|
197
|
-
|
|
43
|
+
- Social Bar、Popunder、Smartlink 每站各配置一个;
|
|
44
|
+
- Banner 和 Native Banner 可以添加多个;
|
|
45
|
+
- 当前统一使用三个 Banner 名称:`placement-728x90`、`placement-320x50`、`placement-300x250`;Native Banner 使用 `placement-native`;
|
|
46
|
+
- Banner 填脚本地址、广告 Key、宽度和高度;
|
|
47
|
+
- Native Banner 填脚本地址和 Container ID。
|
|
198
48
|
|
|
199
|
-
|
|
200
|
-
| ----------------- | ------------- | ------ | --------- | ---------- | -------- |
|
|
201
|
-
| `none` | 否 | 否 | 否 | 否 | 否 |
|
|
202
|
-
| `adsense` | 否 | 否 | 否 | 否 | 否 |
|
|
203
|
-
| `adsense-limited` | 是 | 是 | 否 | 否 | 否 |
|
|
204
|
-
| `adsterra-safe` | 是 | 是 | 是 | 否 | 否 |
|
|
205
|
-
| `adsterra-full` | 是 | 是 | 是 | 是 | 是 |
|
|
49
|
+
包会用浏览器当前 hostname 自动获取该站点配置,不需要额外传网站标识。
|
|
206
50
|
|
|
207
|
-
|
|
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`。例如:
|
|
51
|
+
## 组件
|
|
231
52
|
|
|
232
53
|
```tsx
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
54
|
+
import {
|
|
55
|
+
AdsterraBanner,
|
|
56
|
+
AdsterraNativeBanner,
|
|
57
|
+
AdsterraPopunder,
|
|
58
|
+
AdsterraSmartlink,
|
|
59
|
+
AdsterraSocialBar,
|
|
60
|
+
} from "@cunpingtai/webad-react-ad-scripts";
|
|
61
|
+
|
|
62
|
+
<AdsterraBanner placement="placement-728x90" />
|
|
63
|
+
<AdsterraBanner placement="placement-300x250" />
|
|
64
|
+
<AdsterraNativeBanner placement="placement-native" />
|
|
65
|
+
|
|
66
|
+
<AdsterraSmartlink className="download-button">
|
|
67
|
+
继续下载
|
|
68
|
+
</AdsterraSmartlink>
|
|
69
|
+
|
|
70
|
+
<AdsterraSocialBar />
|
|
71
|
+
<AdsterraPopunder />
|
|
245
72
|
```
|
|
246
73
|
|
|
247
|
-
|
|
74
|
+
这些是后台广告位名称,不是自动识别的尺寸参数;后台配置的尺寸必须与名称对应。同一页面中的同一个 `placement` 只加载一次。Banner 和 Native Banner 还支持 `className`、`style`。组件找不到对应配置、类型不匹配或当前策略不允许时会返回 `null`。
|
|
248
75
|
|
|
249
|
-
##
|
|
76
|
+
## 策略
|
|
250
77
|
|
|
251
|
-
|
|
78
|
+
| 模式 | Banner / Native | Smartlink | Social Bar / Popunder |
|
|
79
|
+
| ----------------- | --------------- | --------- | --------------------- |
|
|
80
|
+
| `none` | 否 | 否 | 否 |
|
|
81
|
+
| `adsense` | 否 | 否 | 否 |
|
|
82
|
+
| `adsense-limited` | 是 | 否 | 否 |
|
|
83
|
+
| `adsterra-safe` | 是 | 是 | 否 |
|
|
84
|
+
| `adsterra-full` | 是 | 是 | 是 |
|
|
252
85
|
|
|
253
|
-
-
|
|
254
|
-
- hostname 精确存在于 Worker 的 `ALLOWED_SCRIPT_HOSTS_JSON`;
|
|
255
|
-
- 不包含账号密码、显式端口或 URL fragment。
|
|
86
|
+
同一个 `pageKey` 中,同一个 `placement` 只初始化一次。普通 React re-render 和 Strict Mode 不会重复请求;真实 SPA 路由切换后可以重新初始化。Social Bar 和 Popunder 在整个文档生命周期各只加载一次。
|
|
256
87
|
|
|
257
|
-
|
|
88
|
+
## CSP
|
|
258
89
|
|
|
259
|
-
|
|
90
|
+
站点 CSP 的 `connect-src` 需要允许 `https://a1.yumeref.com`,`script-src` 需要允许后台中实际填写的广告脚本域名。`ALLOWED_SCRIPT_HOSTS_JSON` 只约束 Worker 集中加载的 provider 脚本,不约束这些站点广告位脚本。
|
|
260
91
|
|
|
261
|
-
|
|
92
|
+
完整的页面布局和广告数量规则见仓库中的 `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 };
|