Search code examples
vue.jsgoogle-chrome-extensionvuejs3

Vue.js inside Chrome extension Content script


I am trying to use Vue.js 3 inside a content script of a chrome extension.

Here I create the element on the rendered webpage (eg. google.com):

content-script.js

let element = document.querySelector('h1#title')
element.insertAdjacentHTML('afterend', '<div id="counter">COUNTER: {{counter}}</div>');

Here I create the Vue app:

import { createApp } from 'vue'

const CounterApp = {
    data() {
      return {
        counter: 0
      }
    },
    mounted() {
      setInterval(() => {
        this.counter++
      }, 1000)
    }
}
  
createApp(CounterApp).mount('#counter')
console.log(CounterApp)

but in the webpage it displays COUNTER: {{counter}} without actually filling the content.

In the console:

{template: "↵ Counter: {{ counter }}↵
", __props: Array(0), __emits: null, data: ƒ, mounted: ƒ}


Solution

  • I can't test this in a chrome app context but I think this should solve your problem of getting the Vue variable to render.

    The template needs to be with the Vue code, rather than with the content script code. By the time it is inserted into the DOM it is too late for Vue to interpolate your this.counter value.

    content-script.js

    In the content script just append the empty #counter div

    let element = document.querySelector('h1#title')
    element.insertAdjacentHTML('afterend', '<div id="counter"></div>');
    

    Vue app

    Then in the app code add a .render() function to interpolate the value into your string (and turn it into a VNode with h())

    render () { 
      return h(tag, props, stuffToRender)
    }
    

    So if you wanted to render a <p> tag with your interpolated string template COUNTER: {{ this.counter }} in it, try:

    import { createApp, h } from 'vue' // <-- don't forget to import h
    
    // If you have local import failure, try import via CDN url, like:
    //import { createApp, h  } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
    
    const CounterApp = {
      data() {
        return {
          counter: 0
        }
      },
      mounted() {
        setInterval(() => {
          this.counter++
        }, 1000)
      },
      render() {
        return h('p', {}, `COUNTER: ${this.counter}`)
      }
    }
      
    createApp(CounterApp).mount('#counter')
    

    More information on the render() function: vuejs.org