1

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: ƒ}

SoyChai
  • 320
  • 2
  • 11
sparkle
  • 7,530
  • 22
  • 69
  • 131
  • did you created the extension using boilerplate through vue-cli, if not then you need to include vue library in index.html. – Chandan Mar 22 '21 at 04:27
  • It’s a content script in a chrome extension. I haven’t index.html, how do I include it? – sparkle Mar 22 '21 at 09:11
  • No I didn’t use vue-cli – sparkle Mar 22 '21 at 09:47
  • if not then vue library is not included by default you need to include it in your content script – Chandan Mar 22 '21 at 09:49
  • you can create extension project with vue-cli check [this](https://stackoverflow.com/a/59855100/14475852) post. – Chandan Mar 22 '21 at 09:50
  • if you don't want to use vue-cli and want to do the setup from scratch you can check [this](https://stackoverflow.com/a/37611630/14475852) post. – Chandan Mar 22 '21 at 09:54

1 Answers1

2

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

Kalnode
  • 9,386
  • 3
  • 34
  • 62
Dick Fox
  • 2,954
  • 2
  • 14
  • 17
  • You'll probably fail at the `import` statement as importing modules in this context is unsupported (there's lots of discussion on this). Ultimately there might be a reliable solution for full module import support, however you can consider import via CDN url e.g. `import { createApp, h } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'`. This worked for me and I was able to see the counter inside the webpage. – Kalnode Dec 26 '22 at 21:32