2

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

6
  • did you created the extension using boilerplate through vue-cli, if not then you need to include vue library in index.html. Commented Mar 22, 2021 at 4:27
  • It’s a content script in a chrome extension. I haven’t index.html, how do I include it? Commented Mar 22, 2021 at 9:11
  • No I didn’t use vue-cli Commented Mar 22, 2021 at 9:47
  • if not then vue library is not included by default you need to include it in your content script Commented Mar 22, 2021 at 9:49
  • you can create extension project with vue-cli check this post. Commented Mar 22, 2021 at 9:50

1 Answer 1

3
+50

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

Sign up to request clarification or add additional context in comments.

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.