首页 > 解决方案 > Nuxt: Inside a plugin, how to add dynamic script tag to head?

问题描述

I'm trying to build a Google Analytics plugin to Nuxt that will fetch tracking IDs from the CMS. I am really close I think.

I have a plugin file loading on client side only. The plugin is loaded from nuxt.config.js via the plugins:[{ src: '~/plugins/google-gtag.js', mode: 'client' }] array.

From there the main problem is that the gtag script needs the UA code in it's URL, so I can't just add that into the regular script object in nuxt.config.js. I need to get those UA codes from the store (which is hydrated form nuxtServerInit.

So I'm using head.script.push in the plugin to add the gtag script with the UA code in the URL. But that doesn't result in the script being added on first page load, but it does for all subsequent page transitions. So clearly I'm running head.script.push too late in the render of the page.

But I don't know how else to fetch tracking IDs, then add script's to the head.

// plugins/google.gtag.client.js with "mode": "client
export default ({ store, app: { head, router, context } }, inject) => {
    // Remove any empty tracking codes
    const codes = store.state.siteMeta.gaTrackingCodes.filter(Boolean)

    // Add script tag to head
    head.script.push({
        src: `https://www.googletagmanager.com/gtag/js?id=${codes[0]}`,
        async: true
    })
    console.log('added script')

    // Include Google gtag code and inject it (so this.$gtag works in pages/components)
    window.dataLayer = window.dataLayer || []
    function gtag() {
        dataLayer.push(arguments)
    }
    inject('gtag', gtag)
    gtag('js', new Date())

    // Add tracking codes from Vuex store
    codes.forEach(code => {
        gtag('config', code, {
            send_page_view: false // necessary to avoid duplicated page track on first page load
        })

        console.log('installed code', code)

        // After each router transition, log page event to Google for each code
        router.afterEach(to => {
            gtag('event', 'page_view', { page_path: to.fullPath })
            console.log('afterEach', code)
        })
    })
}

标签: vue.jsnuxt.jsheadgtag.js

解决方案


I ended up getting this to work and we use it in production here.

Code as of this writing looks like this:

export default ({ store, app: { router, context } }, inject) => {
    // Remove any empty tracking codes
    let codes = _get(store, "state.siteMeta.gaTrackingCodes", [])
    codes = codes.filter(Boolean)

    // Abort if no codes
    if (!codes.length) {
        if (context.isDev) console.log("No Google Anlaytics tracking codes set")
        inject("gtag", () => {})
        return
    }

    // Abort if in Dev mode, but inject dummy functions so $gtag events don't throw errors
    if (context.isDev) {
        console.log("No Google Anlaytics tracking becuase your are in Dev mode")
        inject("gtag", () => {})
        return
    }

    // Abort if we already added script to head
    let gtagScript = document.getElementById("gtag")
    if (gtagScript) {
        return
    }

    // Add script tag to head
    let script = document.createElement("script")
    script.async = true
    script.id = "gtag"
    script.src = "//www.googletagmanager.com/gtag/js"
    document.head.appendChild(script)

    // Include Google gtag code and inject it (so this.$gtag works in pages/components)
    window.dataLayer = window.dataLayer || []
    function gtag() {
        dataLayer.push(arguments)
    }
    inject("gtag", gtag)
    gtag("js", new Date())

    // Add tracking codes from Vuex store
    codes.forEach(code => {
        gtag("config", code, {
            send_page_view: false // Necessary to avoid duplicated page track on first page load
        })

        // After each router transition, log page event to Google for each code
        router.afterEach(to => {
            gtag("config", code, { page_path: to.fullPath })
        })
    })
}

If not in a plug-in, this was a good read on how to load 3rd party scripts: How to Load Third-Party Scripts in Nuxt.js


推荐阅读