I'm currently rendering Vue apps inside object tags (iframe could work too) of a container/master Vue app. First I setup a fileserver serving that container or the requested sub-app to render inside the div.
For the sake of simplicity I will only show the required routing of my Node/Express server
// serve the sub-app on demand
router.get('/subApps/:appName', function(req, res) {
res.sendFile(path.resolve(__dirname, `../apps/${req.params.appName}/index.html`);
});
// always render the app container if no sub-app was requested
router.get('*', function(req, res) {
res.sendFile(path.resolve(__dirname, '../base/index.html'));
});
My app container / master Vue app is inside the view file rendered on /apps/:appName
, requests that sub-app and wraps it into object tags
document.getElementById("customAppContainer").innerHTML = `<object style="width: 100%; height:100%;" data="http://localhost:3000/subApps/${appKey}"></object>`;
This approach works fine but the rendered sub-app uses the startup url http://localhost:3000/subApps/app-one
although it should use the url http://localhost:3000/apps/app-one
. When the sub-app loads the router instance I have to change the startup url from the iframe url to the browser url (parent).
I thought about fixing that with history.replaceState
const router = new Router({ ... });
router.afterEach((to, from) => {
localStorage.subAppRouteUpdate = to.path;
});
if (window.location !== window.parent.location) {
const browserUrl = top.location.href;
history.replaceState(null, null, browserUrl);
}
export default router;
Why do I want to do this? The App.vue of the master app should append the sub-app route to the browser url. With this approach it's possible to update the browser url while navigating inside the sub-app. I achieve this by storing the sub-app url to the local storage and listen for local storage changes at the master app side. So the App.vue file uses this code
<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>
to enable a routing while using IFrames. It almost works, this is what I get
Unfortunately it happens that when calling /
of the sub-app the browser url gets updated to
although I'm expecting
Reproduction:
I created a repository for reproduction / testing. Does someone know what might be wrong or how to fix that url updating?
Update:
I think the error occurs because in the router.js of the subApp I'm firing this code
if (window.location !== window.parent.location) {
const browserUrl = top.location.href;
history.replaceState(null, null, browserUrl);
}
router.afterEach((to, from) => {
console.log({ urlToAppend: to.path });
localStorage.subAppRouteUpdate = to.path;
});
The replaceState function will update the IFrame url from /subApps/app-one
to the correct browser url /apps/app-one
. Unfortunately this will trigger the afterEach event and to.path
results in /apps/app-one
although it should be /
.
If the url would be /apps/app-one/users/create
the after each event should trigger with /users/create
of course.
But I didn't figured out how to fix this first triggered event.
It may be a bit hacky solution but it works for me. Just check that current url path is not equal toto.path
in case if event is triggered twiceUPDATE
In
base/src/App.vue
in storage event listener before concatenating sub route to base route you don't check for the possible duplicates. This fix should helpAnd
router.afterEach
should look like this to navigate to subRoutes which are defined within app-one router:If you want page one to be rendered by default when user goes to
http://localhost:3000/apps/app-one
you could check whether last part of the entered url is equal to sub apps base route(/app-one
) and if it does navigate to default page route(/
):