简体   繁体   中英

manipulate browser Url within embedded Vue app

I currently render Vue apps into Vue apps. I achieve this by embedding the index.html file of the sub-app into a div container of the main-app.

<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>

If you want to know more about this please have a look here

mount Vue apps into container of main Vue app

and my own answer to this problem

https://stackoverflow.com/a/58265830/9945420

My sub-app rendered within the object tags of the customAppContainer has it own routes, a basic routing would be

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

I can navigate through my sub-app and log the current url

<script>
export default {
  created() {
    console.log(this.$router.currentRoute.fullPath);
  }
};
</script>

On the first route I get / and on the second one I get /two . This is correct. But while navigating through the sub-app the browser URL never changes. When calling localhost:3000/apps/my-first-custom-app/two the fileserver serves the index.html and is not able to forward to /two .

Is it possible to forward to the correct route? And manipulate the browser URL when navigating through the sub-app?


Update:

I tried to remove the object tags because they seem to create a blackbox. I found another approach here

https://stackoverflow.com/a/55209788/9945420

The updated code should be

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

<script>
export default {
  async mounted() {
    await this.updateCustomAppContainer();
  },
  methods: {
    async 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>`;

      const response = await fetch(`http://localhost:3000/subApps/${appKey}`);
      const htmlContent = await response.text();
      document.getElementById("customAppContainer").innerHTML = htmlContent;
    }
  },
  watch: {
    async $route(to, from) {
      await this.updateCustomAppContainer();
    }
  }
};
</script>

This code works with plain HTML files (no Vue apps) but when I want to embed a distributed Vue app this app is not rendered.


Minimalistic reproduction

I created a minimalistic repository for reproduction.

https://github.com/byteCrunsher/temp-reproduction

The development directory contains the static fileserver, the base Vue app that should render the vue apps and the apps themselves. Please keep in mind that the first app is a Vue app and the second app is just a basic HTML file.

The production directory contains the fileserver, the distributed Vue apps and the html file. Simply run the static fileserver from the production directory and go to http://localhost:3000 you should see my current work then.


Update

When I use the vue directive v-html and store the response into a data attribute ( https://hatebin.com/casjdcehrj ) then I get this response from the server

在此处输入图像描述

and this is the content I get when I use await response.text();

在此处输入图像描述

I just solved the problem by my own. I took the IFrame / object tags way

document.getElementById(
    "customAppContainer"
  ).innerHTML = `<object style="width: 100%; height:100%;" data="http://localhost:3000/subApps/${appKey}"></object>`;

This way has the drawback to isolate the embedded app from the main app. But it's possible to communicate via localStorage. The subApp extends its router.js file with

router.afterEach((to, from) => {
    localStorage.subAppRouteUpdate = to.path;
});

This event will trigger whenever I navigate through that embedded app and will store the updated url to the localStorage. The base app can listen to that storage change. In the App.vue it adds an event listener and appends the subApp url to the browser url

<script>
export default {
  created() {
    window.addEventListener("storage", () => {
      const fullRoute = this.$router.currentRoute.fullPath;
      const routeSegments = fullRoute.split("/");
      const appsIndex = routeSegments.indexOf("apps");
      const newBaseUrlSegments = routeSegments.slice(0, appsIndex + 2);
      const newBaseUrl = newBaseUrlSegments.join("/");
      const subAppRoute = localStorage.subAppRouteUpdate;
      const updatedUrl = newBaseUrl.concat(subAppRoute);
      history.replaceState(null, null, updatedUrl);
    });
  }
};
</script>

With this approach I'm currently able to navigate through my containerized subApp and can still udpdate the browser url.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM