AdSense 接入问题,实际上 npm 上已经有一些三方包了,正常的应用场景基本是能覆盖的,但有几个进阶的需求,便需要自己定制开发了:

  1. AdSense 配置需要从配置中心读取,以避免 hard code,也就是需要支持通过传入 option key 自动调用配置中心接口动态获取配置。
  2. 需要区分桌面端和移动端,分别设计不同的样式。
  3. 支持开关。
  4. 支持 Adblock Plus 等广告拦截插件的检测,在被屏蔽的情况下隐藏 AdSense,或者显示其他广告,比如京东联盟等。

以下是实现过程和对应的代码。

1、接入配置中心,动态获取 AdSense 配置

typescriptCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
const defaults: AdsenseConfig = { clientId: '', slotId: '', format: '', className: '', style: '', display: 'inline-block', testMode: false }; let adsenseConfig: Partial<AdsenseConfig> = {}; if (this.dynamic && this.optionKey) { try { adsenseConfig = JSON.parse(this.options[this.optionKey]); } catch (e) {} } adsenseConfig = { ...defaults, ...adsenseConfig };
const defaults: AdsenseConfig = { clientId: '', slotId: '', format: '', className: '', style: '', display: 'inline-block', testMode: false }; let adsenseConfig: Partial = {}; if (this.dynamic && this.optionKey) { try { adsenseConfig = JSON.parse(this.options[this.optionKey]); } catch (e) {} } adsenseConfig = { ...defaults, ...adsenseConfig };

通过optionKey参数,直接读取options中的配置,其中,options对象通过订阅全局的 Observable 对象获取:

typescriptCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
this.optionService.options$ .pipe( skipWhile((options) => isEmpty(options)), takeUntil(this.destroy$) ) .subscribe((options) => { this.options = options; ... });
this.optionService.options$ .pipe( skipWhile((options) => isEmpty(options)), takeUntil(this.destroy$) ) .subscribe((options) => { this.options = options; ... });

2、区分桌面端和移动端

这个直接调用封装好的UserAgentService即可:

xmlCopy code
  • 1
<div #adsense class="ads-wrap" [class.ads-wrap-desktop]="!isMobile" [class.ads-wrap-mobile]="isMobile"></div>

其中,isMobile的值来自:

typescriptCopy code
  • 1
this.isMobile = this.userAgentService.isMobile();
this.isMobile = this.userAgentService.isMobile();

3、开关,Adblock Plus 检测,及备用方案

开关的实现无需多言(同样直接读取配置中心的配置),而至于 Adblock Plus 的检测,网络上的各种魔法解决方案都难免过于复杂,实际上,通过 AdSense 的调用原理,可以轻松找到解决方案。

typescriptCopy code
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
private loadAds() { if (this.enableAds && this.platform.isBrowser && this.visible) { const ads: Record<string, string | boolean> = {}; if (this.pageLevelAds) { ads['google_ad_client'] = this.clientId; ads['enable_page_level_ads'] = true; } if (window) { try { this.createAdsEle(); ((window as any).adsbygoogle = (window as any).adsbygoogle || []).push(ads); if (Array.isArray((window as any).adsbygoogle)) { this.console.warn('Ads is blocked.'); this.hideAdsEle(); } else { this.commonService.updateAdsFlag(false); } } catch (e) { this.console.error('Ads: ', e.message || 'is not working.'); this.hideAdsEle(); } } } else { this.console.warn('Ads is disabled.'); this.hideAdsEle(); } }
private loadAds() { if (this.enableAds && this.platform.isBrowser && this.visible) { const ads: Record = {}; if (this.pageLevelAds) { ads['google_ad_client'] = this.clientId; ads['enable_page_level_ads'] = true; } if (window) { try { this.createAdsEle(); ((window as any).adsbygoogle = (window as any).adsbygoogle || []).push(ads); if (Array.isArray((window as any).adsbygoogle)) { this.console.warn('Ads is blocked.'); this.hideAdsEle(); } else { this.commonService.updateAdsFlag(false); } } catch (e) { this.console.error('Ads: ', e.message || 'is not working.'); this.hideAdsEle(); } } } else { this.console.warn('Ads is disabled.'); this.hideAdsEle(); } }

以上代码融合了开关判断、Adblock Plus 拦截判断、隐藏 AdSense,以及发送状态通知(以调用京东联盟等)。完整效果可以直接在本页面体验。

至此,所有的需求都完美解决。[]~( ̄▽ ̄)~*

附录

完整实现代码请访问:https://github.com/ifuyun/ifuyun.com