angular vue.js vuejs2 google-dfp gpt

Google DFP en SPA(Angular, Vue): cómo destruir anuncios y todas sus referencias para evitar la pérdida de memoria



vue.js vuejs2 (1)

Intenté usar DFP de Google tanto en Vue.js como en Angular SPA pero parece estar causando una pérdida de memoria.

En Angular aquí puede ver la prueba de concepto https://github.com/jbojcic1/angular-dfp-memory-leak-proof-of-concept . Para los anuncios, estoy usando el ngx-dfp npm ( https://github.com/atwwei/ngx-dfp ). Para reproducir y ejecutar el proyecto de prueba de concepto, vaya a la página de inicio, que inicialmente tendrá 3 anuncios en el feed, y haga la instantánea del montón. Después de eso, vaya a la página sin anuncios usando un enlace en el encabezado, haga una instantánea del montón otra vez y verá que las referencias a las ranuras se guardan después de que la ranura se destruye, lo que está causando una pérdida de memoria.

En Vue tengo un componente que crea y destruye espacios publicitarios y los estoy agregando dinámicamente en la fuente de contenido. Cuando salgo de la página, el componente se destruye y, antes de Destroy, llamo a destroySlots pero parece que todavía hay algunas referencias.

Aquí está mi componente dfp-ad:

<template> <div :id="id" class="ad" ref="adContainer"></div> </template> <script> export default { name: ''dfp-ad'', props: { id: { type: String, default: null }, adName: { type: String, default: null }, forceSafeFrame: { type: Boolean, default: false }, safeFrameConfig: { type: String, default: null }, recreateOnRouteChange: { type: Boolean, default: true }, collapseIfEmpty: { type: Boolean, default: true }, sizes: { type: Array, default: () => [] }, responsiveMapping: { type: Array, default: () => [] }, targetings: { type: Array, default: () => [] }, outOfPageSlot: { type: Boolean, default: false } }, data () { return { slot: null, networkCode: ''something'', topLevelAdUnit: ''something_else'' } }, computed: { slotName () { return `/${this.networkCode}/${this.topLevelAdUnit}/${this.adName}` } }, mounted () { this.$defineTask(() => { this.defineSlot() }) }, watch: { ''$route'': function (to, from) { if (this.recreateOnRouteChange) { this.$defineTask(() => { // this.resetTargetings() // We can''t just change targetings because slot name is different on different pages (not sure why though) // too so we need to recreate it. this.recreateSlot() this.refreshContent() }) } } }, methods: { getState () { return Object.freeze({ sizes: this.sizes, responsiveMapping: this.responsiveMapping, targetings: this.targetings, slotName: this.slotName, forceSafeFrame: this.forceSafeFrame === true, safeFrameConfig: this.safeFrameConfig, clickUrl: this.clickUrl, recreateOnRouteChange: this.recreateOnRouteChange, collapseIfEmpty: this.collapseIfEmpty === true, outOfPageSlot: this.outOfPageSlot }) }, setResponsiveMapping (slot) { const ad = this.getState() const sizeMapping = googletag.sizeMapping() if (ad.responsiveMapping.length === 0) { ad.sizes.forEach(function (size) { sizeMapping.addSize([size[0], 0], [size]) }) } else { ad.responsiveMapping.forEach(function (mapping) { sizeMapping.addSize(mapping.viewportSize, mapping.adSizes) }) } slot.defineSizeMapping(sizeMapping.build()) }, refreshContent () { googletag.pubads().refresh([this.slot]) }, defineSlot () { const ad = this.getState() const element = this.$refs.adContainer this.slot = ad.outOfPageSlot ? googletag.defineOutOfPageSlot(ad.slotName, this.id) : googletag.defineSlot(ad.slotName, ad.sizes, this.id) if (ad.forceSafeFrame) { this.slot.setForceSafeFrame(true) } if (ad.clickUrl) { this.slot.setClickUrl(ad.clickUrl) } if (ad.collapseIfEmpty) { this.slot.setCollapseEmptyDiv(true, true) } if (ad.safeFrameConfig) { this.slot.setSafeFrameConfig( /** @type {googletag.SafeFrameConfig} */ (JSON.parse(ad.safeFrameConfig)) ) } if (!ad.outOfPageSlot) { this.setResponsiveMapping(this.slot) } this.setTargetings(ad.targetings) this.slot.addService(googletag.pubads()) googletag.display(element.id) this.refreshContent() }, setTargetings (targetings) { targetings.forEach(targeting => { this.slot.setTargeting(targeting.key, targeting.values) }) }, resetTargetings () { this.slot.clearTargeting() this.setTargetings(this.targetings) }, recreateSlot () { googletag.destroySlots([this.slot]) this.defineSlot() } }, created () { }, beforeDestroy () { if (this.slot) { googletag.destroySlots([this.slot]) } } } </script> <style lang="scss" scoped> ... </style>

Estoy inyectando GPT y configurando la configuración global en un complemento:

const dfpConfig = { enableVideoAds: true, collapseIfEmpty: true, centering: false, location: null, ppid: null, globalTargeting: null, forceSafeFrame: false, safeFrameConfig: null, loadGPT: true, loaded: false } const GPT_LIBRARY_URL = ''//www.googletagservices.com/tag/js/gpt.js'' const googletag = window.googletag || {} googletag.cmd = googletag.cmd || [] var scriptInjector exports.install = function (Vue, options) { initialize(options) Vue.prototype.$hasLoaded = function () { return dfpConfig.loaded } Vue.prototype.$defineTask = function (task) { googletag.cmd.push(task) } } function initialize (options) { scriptInjector = options.scriptInjector googletag.cmd.push(() => { setup() }) if (dfpConfig.loadGPT) { scriptInjector.injectScript(GPT_LIBRARY_URL).then((script) => { dfpConfig.loaded = true }) window[''googletag''] = googletag } } function setup () { const pubads = googletag.pubads() if (dfpConfig.enableVideoAds) { pubads.enableVideoAds() } if (dfpConfig.collapseIfEmpty) { pubads.collapseEmptyDivs() } // We always refresh ourselves pubads.disableInitialLoad() pubads.setForceSafeFrame(dfpConfig.forceSafeFrame) pubads.setCentering(dfpConfig.centering) addLocation(pubads) addPPID(pubads) addTargeting(pubads) addSafeFrameConfig(pubads) pubads.enableAsyncRendering() // pubads.enableSingleRequest() googletag.enableServices() } function addSafeFrameConfig (pubads) { if (!dfpConfig.safeFrameConfig) { return } pubads.setSafeFrameConfig(dfpConfig.safeFrameConfig) } function addTargeting (pubads) { if (!dfpConfig.globalTargeting) { return } for (const key in dfpConfig.globalTargeting) { if (dfpConfig.globalTargeting.hasOwnProperty(key)) { pubads.setTargeting(key, dfpConfig.globalTargeting[key]) } } } function addLocation (pubads) { if (!dfpConfig.location) { return } if (typeof dfpConfig.location === ''string'') { pubads.setLocation(dfpConfig.location) return } if (!Array.isArray(dfpConfig.location)) { throw new Error(''Location must be an array or string'') } pubads.setLocation.apply(pubads, dfpConfig.location) } function addPPID (pubads) { if (!dfpConfig.ppid) { return } pubads.setPublisherProvidedId(dfpConfig.ppid) }

Aquí está uno de los componentes de los anuncios:

<template> <div class="feed-spacer"> <dfp-ad class="feed-ad" :id="adId" :adName="adName" :sizes="sizes" :responsiveMapping="responsiveMapping" :targetings="targetings" :recreateOnRouteChange="false"> </dfp-ad> </div> </template> <script> import DfpAd from ''@/dfp/component/dfp-ad.vue'' export default { components: {DfpAd}, name: ''feed-ad'', props: [''instance''], data () { return { responsiveMapping: [ {viewportSize: [1280, 0], adSizes: [728, 90]}, {viewportSize: [640, 0], adSizes: [300, 250]}, {viewportSize: [320, 0], adSizes: [300, 250]} ], sizes: [[728, 90], [300, 250]] } }, computed: { adId () { return `div-id-for-mid${this.instance}-leaderboard` }, adName () { return this.$route.meta.pageId }, targetings () { const targetings = [ { key: ''s1'', values: this.$route.meta.pageId }, { key: ''pid'', values: this.$route.meta.pageId }, { key: ''pagetype'', values: this.$route.meta.pageType }, { key: ''channel'', values: this.$route.meta.pageId }, { key: ''test'', values: this.$route.query.test }, { key: ''pos'', values: `mid${this.instance}` } ] switch (this.$route.name) { case ''games'': targetings.push(''some_tag'', this.$route.params.slug) break case ''show'': targetings.push(''some_other_tag'', this.$route.params.slug) break } return targetings } } } </script> <style lang="scss" scoped> ... </style>

¿Alguien tiene un problema similar? ¿Estoy haciendo algo mal? ¿O tal vez simplemente no puede destruir y crear ranuras en SPA sin causar pérdida de memoria?

EDITAR

Aquí está la captura de pantalla de los nodos separados:


Tuvimos una gran pérdida de memoria en nuestra aplicación web vue. Descubrimos que teníamos detectores de eventos en la ventana, parece que dfp los creó.

window.addEventHook = window.addEventListener; window.addEventListener = function () { if (!window.listenerHook) window.listenerHook = []; window.listenerHook.push({name: arguments[0], callback: arguments[1] }); window.addEventHook.apply(window,arguments); };

Usamos esto para guardar toda la escucha de eventos que se adjunta a la ventana cuando la aplicación se cargó por primera vez, luego, cuando queremos eliminar los anuncios dfp, iteramos la matriz y ejecutamos window.removeEventListener en cada uno de ellos (esto eliminará todos los detectores de eventos de la ventana de la ventana , Es necesario agregar cheques para ver que no está eliminando algo importante)

Esto solucionó nuestro problema de pérdida de memoria.