vuejs3

Why load one app to two div isn't working


Can you guys tell me what I am missing here? I am trying to load one app to two div but I find it works only for the first one.

Below, I am expecting the message from div2app1 which isn't coming. Thanks in advance

<div id="div1app1">
    This is div1 for app1 {{ message }} 
</div>  

<!--lots of html an JS in between -->

<div id="div1app2">
  This is div1 for app2 {{ msg }}   
</div> 

<!--lots of html an JS in between -->   
   
<div id="div2app1">
   This is div2 for app1 {{ message }}   
</div> 


<script type="module">
import { createApp, ref, defineProps, onMounted, shallowReactive, reactive } from 'vue'
      
const app1 = createApp({
  setup() {
    onMounted(async () => {
      console.log('Hello')     
      message.value = 'New Hello'
    })  
    const message = ref('Hello Vue!')
    return {              
      message
    }
  }
})
const app2 = createApp({
  setup() {
    onMounted(async () => {
      console.log('Hello')
      msg.value = 'Hello for App2'
    })

    const msg = ref('Hello Vue!')
    return {       
      msg
    }
  }
})  

app1.mount('#div1app1');
app1.mount('#div2app1');
app2.mount('#div1app2');

</script>

Solution

  • One app instance can only be mounted to one single DOM element:

    const app = createApp()
    app.mount('#app-1')
    // this won't do anything, app is already mounted to #app-1:
    app.mount('#app-2')
    

    But you can create multiple app instances with the same contents and mount them in different DOM nodes:

    const App = {
      template: '<div>some app</div>'
      setup() {
        // Vue magic goes here
      }
    }
    
    const app1 = createapp(App).mount('#app-1')
    const app2 = createapp(App).mount('#app-2')
    

    If you want the same functionality rendered in multiple unrelated places in DOM, and have them perfectly synced, two approaches come to mind:

    1. create multiple distinct app instances with the same app definition:

    const { defineComponent, reactive, createApp } = Vue
    
    const state = reactive({
      foo: 'change me...'
    })
    
    const MyApp = {
      template: '<div>Hello from app. <input v-model="foo"></div>',
      setup: () => state
    }
    
    const app1 = createApp(MyApp).mount('#app-1')
    const app2 = createApp(MyApp).mount('#app-2')
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.5.4/vue.global.prod.min.js"></script>
    <div id="app-1"></div>
    <div id="app-2"></div>

    1. use Vue's native <Teleport /> component (e.g: define your "app" as a component and then use an actual app to display one of the "components" in-place and another one teleported to #div-2):

    const { defineComponent, reactive, createApp } = Vue
    
    const state = reactive({
      foo: 'change me...'
    })
    
    const MyApp = {
      template: '<div>Hello from app. <input v-model="foo"></div>',
      setup: () => state
    }
    
    const app = createApp().component('my-app', MyApp).mount('#app-1')
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.5.4/vue.global.prod.min.js"></script>
    <div id="app-1">
      <my-app></my-app>
      <teleport to="#app-2">
        <my-app></my-app>
      </teleport>
    </div>
    
    <!-- this can be anywhere in DOM -->
    <div id="app-2"></div>


    Why can an app instance only be rendered in one place in DOM?

    Because that's how Vue was designed.

    Or, in more detail, because that's what's needed in 99.99% of cases and being able to render the same app instance in multiple DOM elements at the same time would add useless additional complexity to Vue for little to no benefit, since it can already be achieved with the existing APIs.