mount Vue apps into container of main Vue app

I finally got it working with the following approach and maybe this will help someone. Suggestions highly appreciated!


Base App:

The base app which renderes all the sub-apps has a CustomAppContainer view that loads the sub-apps into a div container.

<template>
  <div id="customAppContainer"></div>
</template>

<script>
export default {
  mounted() {
    this.updateCustomAppContainer();
  },
  methods: {
    updateCustomAppContainer() {
      const fullRoute = this.$router.currentRoute.fullPath;
      const routeSegments = fullRoute.split("/");
      const appsIndex = routeSegments.indexOf("apps");
      const appKey = routeSegments[appsIndex + 1];

      document.getElementById(
        "customAppContainer"
      ).innerHTML = `<object style="width: 100%; height:100%;" data="http://localhost:3000/subApps/${appKey}"></object>`;
    }
  },
  watch: {
    $route(to, from) {
      this.updateCustomAppContainer();
    }
  }
};
</script>

Further I added mode: 'history' to the router.

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      component: Home
    },
    {
      path: '/apps/*',
      component: CustomAppContainer,
    }
  ]
})

I put the build files of that app into my production directory in a folder called base


File Server:

I created a static Express file server that always serves the index.html file of the base app. But I can request the index.html file of the sub-app too.

const express = require('express');
const path = require('path');
const fs = require('fs');
const cors = require('cors');
const app = express();
const router = express.Router();

app.use(cors());

app.use(express.static(path.resolve(__dirname, '../base')));

fs.readdirSync(path.resolve(__dirname, '../apps')).forEach(appName => {
    app.use(express.static(path.resolve(__dirname, `../apps/${appName}`)));
});

router.get('/subApps/:appName', function(req, res) {
    res.sendFile(path.resolve(__dirname, `../apps/${req.params.appName}/index.html`));
});

router.get('*', function(req, res) {
    res.sendFile(path.resolve(__dirname, '../base/index.html'));
});

app.use('/', router);

app.listen(3000, function() {
    console.log('Fileserver listening on port 3000');
});

Custom App:

When creating custom apps all I have to do is to extend the router config

export default new Router({
    base: '/my-first-custom-app/',
    mode: 'history',
    routes: [
        {
            path: '/',
            component: PageOne,
        },
        {
            path: '/two',
            component: PageTwo,
        },
    ],
});

and rename the #app ID to #customApp in the index.html, main.js and App.vue.

Further I put the build files of that sub-app into my production directory in a folder called apps.


This allows me to create mulitple apps and render them into a div container of my main app container. The sub-app itself doesn't have to be a Vue app. I can create new apps with Jquery, Angular or React too, or even just a plain .html file.


1

Vue does not know CustomAppContainer

Try to add import CustomAppContainer from "path/to/CustomAppContainer.vue"
It must be in .vue file.


2

Vue does not know #customAppContainer

Yes, that selector must be in index.html. Yours is in CustomAppContainer. Try to add to index.html (usually in /public folder) something like <div id="#app"></div> and replace .$mount('#customAppContainer'); with .$mount('#app');


3

Vue router needs <router-view> tag. So, try this markup:

<!-- In template is all page -->
<template>
  <!-- Some stuff as navigation... -->
  <nav>
    <!-- Router links - renders as "a" element -->
    <router-link to="/one">To /one</router-link>
    <!-- Or use standard tags - they're also good -->
    <a href="/two">To /two</a>
  </nav>
  <!-- In this component would be component chosen in router (Home.vue) -->
  <router-view/>
</template>

4

Router.js needs import:

import Home from "path/to/the/Home.vue"
export default new Router({
  base: '/one/',
  routes: [
    {
      path: '/',
      component: Home,
    },
  ],
});

All is good?