简体   繁体   中英

Accessing Vue component methods from Chrome Extension

I'm building a chrome extension for an specific page: https://www.vidangel.com/ .

I don't own the page, but inspecting it, I can see it's built on Vue. I need to read a specific information that the page uses multiple times, which is accessed trough a method called "getActiveTags" (in case anyone will bother looking at the bundle file https://www.vidangel.com/js/app.25e52655.js ). It is a method in one of the Vue components:

<template>
      <div class="lineup-barvisual">
        <div class="lineup-barvisual-panel">
          <div class="lineup-barvisual-title">You'll see</div>
          <svg>
            <defs>
              <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
                <stop class="stop-start" offset="0%" />
                <stop class="stop-end" offset="100%" />
              </linearGradient>
            </defs>
            <rect class="backdrop"></rect>
            <g v-if="tag_set && lineup">
              <rect class="cuts" v-for="tag in getActiveAudioTags()" :key="tag.id" :x="tagLeft(tag)" :width="tagWidth(tag)"/>
            </g>
          </svg>
        </div>
        <div class="lineup-barvisual-panel">
          <div class="lineup-barvisual-title">You'll hear</div>
          <svg>
            <rect class="backdrop"></rect>
            <g v-if="tag_set && lineup">
              <rect class="cuts" v-for="tag in getActiveTags()" :key="tag.id" :x="tagLeft(tag)" :width="tagWidth(tag)" />
            </g>
          </svg>
        </div>
      </div>
    </template>
    
    <script>
      import api from 'services/api'
      export default {
        props: ['tag_set', 'lineup'],
        methods: {
          tagLeft(tag) {
            return ((tag.start_approx / this.lineup.work.duration) * 100) + '%'
          },
          tagWidth(tag) {
            return Math.max((((tag.end_approx - tag.start_approx) / this.lineup.work.duration) * 100), 0.075) + '%'
          },
          getActiveTags() {
            return this.tag_set.tags.filter(tag => this.lineup.isTagOn(tag))
          },
          getActiveAudioTags() {
            return this.getActiveTags().filter(tag => tag.type === 'audiovisual')
          },
          paint() {
            this.$forceUpdate()
          }
        },
        watch:{
          'lineup.id'(id, old_id) {
            api.hub.$off(`model.lineup.${old_id}.touched`, this.paint)
            if (id) api.hub.$on(`model.lineup.${id}.touched`, this.paint)
          }
        },
        beforeDestroy() {
          if (this.lineup) {
            api.hub.$off(`model.lineup.${this.lineup.id}.touched`, this.paint)
          }
        }
      }
    </script>

I couldn't figure out where these 'tag_set' and 'lineup' props come from (I don't have a great Vue knowledge), but they seem to be related to this:

import api from 'services/api'

let TagSet = api.defineResource({
  name: 'TagSet',
  endpoint: 'tag-sets',
  relations: {
    belongsTo: {
      CatalogItem: {
        localField: 'work',
        localKey: 'work_id'
      }
    },
    hasMany: {
      Tag: {
        localField: 'tags',
        foreignKey: 'tag_set_id'
      }
    }
  },
  methods: {
    tags_count() {
      return this.tags.length
    },
    tags_active(lineup) {
      return this.tags.filter(t => lineup.refmap[t.ref_id])
    },
    tags_active_count(lineup) {
      return this.tags_active(lineup).length
    }
  },
  watchChanges: false
})

api.defineResource({
  name: 'Tag',
  relations: {
    belongsTo: {
      TagCategory: {
        localField: 'category',
        localKey: 'category_id'
      }
    }
  }
})

export default TagSet

Though I have no idea how this works. I suppose the tagset information is stored in some kind of redux object, because it is accessed from multiple components.

The current solution I'm using is to override the bundle file with declarativeNetRequest, with a slightly modified script, which includes in the getActiveTags method a small bit of code that creates or updates an invisible span on the DOM, which has as it's InnerText a strigfied version of the return value of getActiveTags:

    getActiveTags: function () {
        var t = this;
        let tags = this.tag_set.tags.filter(e => t.lineup.isTagOn(e));
        let tagsMap = tags.map(tag => ({begin: tag.begin, end: tag.end}));
        let elemento = document.getElementById('lista-tags');
        if (!elemento) {
          elemento = document.createElement('span');
          elemento.id = 'lista-tags';
          elemento.style = 'display: none';
          document.body.appendChild(elemento);
        }
        elemento.innerText = JSON.stringify(tagsMap);
        return tags;

I have tried to add the "getActiveTags" method to the global window object, which became accessible on dev tools, but couldn't be accessed from the extension code, even at contentScript (I don't know why). But even that solution being better, it's still problematic, because I have to keep a copy of the bundle saved with theses modifications, so the user could be seeing an outdated version of the page, in case the original page makes modifications.

So what I would really want is a way to access from the window object, the method of the vue component defined by the original script, without having to override the bundle. Is there a way to achieve that? How would I go about finding the vue component? (I have tried for a long time inspecting the window object on dev tools, without finding the vue components.) Thank you.

I figured it out after a long time. You can access vue components methods by the.__vue__ object of the element. So, in my case I had to do:

document.querySelector('.lineup-barvisual').__vue__.getActiveTags()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM