簡體   English   中英

Angular:如何延遲加載谷歌分析腳本?

[英]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。

你們有什么例子可以向我展示這個嗎?

謝謝你。

您可以使用promisesservices組合來實現這一點。 基本上,您需要做的是創建一個服務,該服務在正文中動態注入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>
            &mdash;
            <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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM