![](/img/trans.png)
[英]angular-google-analytics with phonegap issue, “Refused to load the script”
[英]Angular: How to lazy load google analytics script?
我目前正在使用 Angular 8。我想知道如何延遲加載我的 google 分析腳本。 我發現了一些關於使用 angular.json 中的腳本數組延遲加載的全局腳本的文檔:
"scripts": [
"src/global-script.js",
{ "input": "src/lazy-script.js", "lazy": true },
{ "input": "src/pre-rename-script.js", "bundleName": "renamed-script" },
],
通過https://github.com/angular/angular-cli/wiki/stories-global-scripts
但它說它現在已經折舊了,他們參考了他們的文檔,但我找不到任何關於它的信息。
GTM 谷歌標簽管理器減慢了繪制時間。 因此,只有在我的 angular 應用程序初始加載后,我才希望激活 GTM。
你們有什么例子可以向我展示這個嗎?
謝謝你。
您可以使用promises
和services
組合來實現這一點。 基本上,您需要做的是創建一個服務,該服務在正文中動態注入script
標記並設置腳本的路徑,一旦設置了路徑,就可以解析承諾,以便可以使用腳本的功能。 Ben Nadel 在他的博客上有完整的例子下面是一個服務:
export class DelayedScriptLoader {
private delayInMilliseconds: number;
private scriptPromise: Promise<void> | null;
private urls: string[];
// I initialize the delayed script loader service.
constructor( urls: string[], delayInMilliseconds: number );
constructor( urls: string, delayInMilliseconds: number );
constructor( urls: any, delayInMilliseconds: number ) {
this.delayInMilliseconds = delayInMilliseconds;
this.scriptPromise = null;
this.urls = Array.isArray( urls )
? urls
: [ urls ]
;
}
// ---
// PUBLIC METHODS.
// ---
// I load the the underlying Script tags. Returns a Promise.
public load() : Promise<void> {
// If we've already configured the script request, just return it. This will
// naturally queue-up the requests until the script is resolved.
if ( this.scriptPromise ) {
return( this.scriptPromise );
}
// By using a Promise-based workflow to manage the deferred script loading,
// requests will naturally QUEUE-UP IN-MEMORY (not a concern) until the delay has
// passed and the remote-scripts have been loaded. In this case, we're not even
// going to load the remote-scripts until they are requested FOR THE FIRST TIME.
// Then, we will use they given delay, after which the in-memory queue will get
// flushed automatically - Promises rock!!
this.scriptPromise = this.delay( this.delayInMilliseconds )
.then(
() => {
var scriptPromises = this.urls.map(
( url ) => {
return( this.loadScript( url ) );
}
);
return( Promise.all( scriptPromises ) );
}
)
.then(
() => {
// No-op to generate a Promise<void> from the Promise<Any[]>.
}
)
;
return( this.scriptPromise );
}
// ---
// PRIVATE METHODS.
// ---
// I return a Promise that resolves after the given delay.
private delay( delayInMilliseconds: number ) : Promise<any> {
var promise = new Promise(
( resolve ) => {
setTimeout( resolve, delayInMilliseconds );
}
);
return( promise );
}
// I inject a Script tag with the given URL into the head. Returns a Promise.
private loadScript( url: string ) : Promise<any> {
var promise = new Promise(
( resolve, reject ) => {
var commentNode = document.createComment( " Script injected via DelayedScriptLoader. " );
var scriptNode = document.createElement( "script" );
scriptNode.type = "text/javascript";
scriptNode.onload = resolve;
scriptNode.onerror = reject;
scriptNode.src = url;
document.head.appendChild( commentNode );
document.head.appendChild( scriptNode );
}
);
return( promise );
}
}
// Import the core angular services.
import { Injectable } from "@angular/core";
// Import the application components and services.
import { DelayedScriptLoader } from "./delayed-script-loader";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// Since I don't have a Type Definition for this demo library, I'm just going to declare
// the interface here and then explicitly cast the global value when I reference it.
interface AnalyticsScript {
identify( userID: UserIdentifier, traits: UserTraits ) : void;
track( eventID: EventIdentifier, eventProperties: EventProperties ) : void;
}
export type UserIdentifier = string | number;
export interface UserTraits {
[ key: string ]: any;
}
export type EventIdentifier = string;
export interface EventProperties {
[ key: string ]: any;
}
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Injectable({
providedIn: "root"
})
export class AnalyticsService {
private scriptLoader: DelayedScriptLoader;
// I initialize the analytics service.
constructor() {
this.scriptLoader = new DelayedScriptLoader( "./analytics-service.js", ( 10 * 1000 ) );
}
// ---
// PUBLIC METHODS.
// ---
// I identify the user to be associated with subsequent tracking events.
public identify( userID: UserIdentifier, traits: UserTraits ) : void {
this.run(
( analytics ) => {
analytics.identify( userID, traits );
}
);
}
// I track the given event for the previously-identified user.
public track( eventID: EventIdentifier, eventProperties: EventProperties ) : void {
this.run(
( analytics ) => {
analytics.track( eventID, eventProperties );
}
);
}
// ---
// PRIVATE METHODS.
// ---
// I return a Promise that resolves with the 3rd-party Analytics Script.
private async getScript() : Promise<AnalyticsScript> {
// CAUTION: For the sake of simplicity, I am not going to worry about the case in
// which the analytics scripts fails to load. Ideally, I might create some sort
// of "Null Object" version of the analytics API such that the rest of the code
// can run as expected with various no-op method implementations.
await this.scriptLoader.load();
// NOTE: Since I don't have an installed Type for this service, I'm just casting
// Window to ANY and then re-casting the global service that we know was just
// injected into the document HEAD.
return( ( window as any ).analytics as AnalyticsScript );
}
// I run the given callback after the remote analytics library has been loaded.
private run( callback: ( analytics: AnalyticsScript ) => void ) : void {
this.getScript()
.then( callback )
.catch(
( error ) => {
// Swallow underlying analytics error - they are not important.
}
)
;
}
}
以下是使用此服務的組件:
// Import the core angular services.
import { Component } from "@angular/core";
// Import the application components and services.
import { AnalyticsService } from "./analytics.service";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Component({
selector: "my-app",
styleUrls: [ "./app.component.less" ],
template:
`
<p>
<a (click)="doThis()">Do This</a>
—
<a (click)="doThat()">Do That</a>
</p>
`
})
export class AppComponent {
private analyticsService: AnalyticsService;
// I initialize the app component.
constructor( analyticsService: AnalyticsService ) {
this.analyticsService = analyticsService;
}
// ---
// PUBLIC METHODS.
// ---
// I execute an action (that we're going to track).
public doThat() : void {
this.analyticsService.track(
"do.that",
{
now: Date.now()
}
);
}
// I execute an action (that we're going to track).
public doThis() : void {
this.analyticsService.track(
"do.this",
{
now: Date.now()
}
);
}
// I get called once after the inputs have been bound for the first time.
public ngOnInit() : void {
this.analyticsService.identify(
"bennadel",
{
group: "admin"
}
);
}
}
最好將這些腳本保存在文件夾src\\assets
,並將它們直接添加到index.html
文件中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>Angular App</title>
<base href="/"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
...
<script src="assets/global-script.js"></script>
<script src="assets/lazy-script.js"></script>
<script src="assets/pre-rename-script.js"></script>
</head>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.