Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pluggable child apps (multiapp) #30

Open
pi0 opened this issue May 27, 2019 · 31 comments
Open

pluggable child apps (multiapp) #30

pi0 opened this issue May 27, 2019 · 31 comments
Labels
nuxt 3 Nuxt.js 3 targeted RFC pending

Comments

@pi0
Copy link
Member

pi0 commented May 27, 2019

Background

When the project structure grows, sometimes it is not convenient to use a flat directory structure or keep everything in a single repo.

Why?

  • A simple way to split big projects into separate parts with separate business logic
  • We already have srcDir implemented in Nuxt. This feature needs a small refactor effort and does not makes core complicated.

Proposed Solution

One can structure a project like this:

├── shop
|  ├── components
|  |  └── nav.vue
|  ├── layouts
|  |  └── admin.vue
|  ├── middleware
|  |  └── admin.js
|  ├── pages
|  |  └── shop
|  |     └── index.vue
|  └── store
|     └── admin.js
├── nuxt.config.js
├── package.json
├── pages
|  └── index.vue
└── store
   └── public.js

The shop/ dir should basically follow the same standard nuxt directory structure. Builder module combines all children app to the main one. And also creates aliases so components inside admin can be required using:

import Nav from '~shop/components/nav

For registering admin app, it should be registered inside nuxt.config. (Items can be npm packages or local dirs)

export default {
  apps: [
    '~/shop'
  ]
}

Limitations

These are problems with current POC. We can progressively enhance this functionality and workaround these limitations.

  • Currently, store and middleware cannot be supported because srcDir is hardcoded in vue-app templates
  • There is no namespace support. (adding admin/ prefix to store modules and pages) -- children will be registered with the same namespace.
  • Just like the main project, child app files are not lodash templates. We can add a template flag to support and copy the template child apps to the .nuxt
@Koc
Copy link

Koc commented May 27, 2019

Do we really need care of this? IMHO it easier create 2 separate apps with different Nuxt config, packages.json and build/deploy them independently. Otherwise we got big monolith which is hard to maintain.

@pi0
Copy link
Member Author

pi0 commented May 27, 2019

@Koc Exactly that's also my concern/preference to use separate projects. But I know monolith projects that may benefit from this feature to structure their project better. Also, we have the possibility to add parts using an NPM package. (Probably admin was not a good example tough!)

@Koc
Copy link

Koc commented May 27, 2019

Also, we have the possibility to add parts using an NPM package.

For sharing some code between apps we can use monorepo approach, extract common business logic/api clients/etc into separate package and require it using path package or npm-link or tools like Rush

@clarkdo
Copy link
Member

clarkdo commented May 27, 2019

Agree with @Koc concern, we should consider more about this.

  • pages/routes priority in different apps because different apps may contain same route
  • global/scoped middleware/plugin which in run globally or only in specific app
  • complexity for development
  • avoid influence on traditional usage (performance and configuration)

In my perspective, I would like leverage yarn workspace for mono repo, like:

├── cms
|  ├── pages
|  └── store
|  ├── nuxt.config.js
|  └── package.json
├── app
|  ├── middleware
|  ├── pages
|  ├── nuxt.config.js
|  └── package.json
└── package.json

For common app, we already provide components as a dir to put reusable stuff in:

├── pages
├── store
├── components
|  ├── cms
|  └── app
├── nuxt.config.js
└── package.json

If necessary, it would be better to use module or sth like extend to import reusable components or pages.

@pi0
Copy link
Member Author

pi0 commented May 27, 2019

@clarkdo I see your point about route conflicts. that's also mentioned in the notes to be supported

About mono-repo yes that's one solution for separating admin/cms I personally use (and also common/ dir for shared components/base config)

The Purpose of this enhancement is not having different parts (admin/cms/mobile) and not a good practice to use it for that. but splitting the main project into separate and reusable modules. The increasing number of pages for an enterprise project is unavoidable and there is no way to split it into smaller modules.

One other point is that client-side routing works across this sub-apps.

@nicodevs
Copy link

Yes, please! It would be great to have this feature.

@bovas85
Copy link

bovas85 commented May 27, 2019

I'd prefer this to losing the state on micro Frontends. Maybe pairing it with some best practices guidelines would make this more resilient

@gangsthub
Copy link

We've been experimenting this in a PoC at my company. We were able to inject sub-apps routes via Nuxt modules. And were proposing this for one of our products.

I love to see we are aligned, @pi0. Given it's a very specific case that most of Nuxt apps are not going to use, I hope it won't affect the size of their bundles.

@clarkdo is pointing fair concerns.

I think Nuxt core is mature enough to envision this but our current file structure isn't. Therefore a lot of things that come after (in how Nuxt wires up its parts) are coupled with this file structure.

Think about backend architectures. For example, the Hexagonal Architecture. If Nuxt core was really a core, it wouldn't care if there are 1 or 2 apps. Or 1 or 2 UI systems.

Replying to @clarkdo's points:

  • The different apps shouldn't share routes, because each one could be enforced to mount in a different basePath (/, /app) or baseURL or subdomain.
  • global/socped plugins and midldewares are indeed needed
  • development complexity could be improved with a better CLI. Same thing Guillaume Chau is working on for Vue.
  • Traditional biases is a good one. I wouldn't like Nuxt to become Angular.

@ghost
Copy link

ghost commented Jun 4, 2019

This keeps coming to my head. If there is no consensus, how about creating a module that tries to handle handles this without affecting the rest of the user's performance? cc/ @pi0 (❤️)

@pi0
Copy link
Member Author

pi0 commented Jun 14, 2019

@pm-LTK This does not affect the user's performance at all. All sub-apps are finally merged into the same data structure. We just glob multiple directories.

I also thought about the module, but that module should duplicate most of the logic of @nuxt/builder package which introduces inconsistencies. Please refer to related PR to have a better vision of this change.

@HapLifeMan
Copy link

It would be a VERY GREAT feature, in my case for multi-domains support 🙂
Especially to have different pages (components, assets and layouts would be shared between domains).

For instance (it is absolutely not for i18n but it's a good understandable example):

├── pages-com         <==== https://mydomain.com
|  ├── index.vue
|  ├── about.vue
|  └── contact.vue
├── pages-fr          <==== https://mydomain.fr
|  ├── index.vue
|  ├── about.vue
|  └── contact.vue
├── components
├── layouts
├── middleware
├── store
└── package.json

It would fit perfectly for:

  • an admin interface, docs, blog (with subdomains)
  • maintain an app without having multiple repos
  • use shared components/layouts/... but different pages per domain
  • ...

@blowsie
Copy link
Sponsor

blowsie commented Aug 16, 2019

I just stumbled across this after making something similar.
I created a themeable nuxt application. By extending the vue router.

https://codesandbox.io/s/my-app-9ythm

You set the theme path in the nuxt.config, which essentially could be an environment varible.
The code then looks into your themes folder for said theme, and if a page is deinfed for it, use that instead.

That enables you to reuse any existing component code via extends.
You can switch out styles/ templates / js as required.

@simllll
Copy link

simllll commented Aug 20, 2019

We actually ran into something similar as our project got bigger. But we ended up using a monorepo, with several "seperate" nuxt projects, but all the nuxt projects use a shared component library (see a demo how we did that with nuxt: https://github.com/simllll/nuxt-library-demo) and a "module" library where we abstracted all the shared middleware and plugins. Therefore it's only business logic and pages / custom components that are in the nuxt projects. Furthermore one of the sub packages of our mono rep is a cordova mobile app which is using one of the nuxt projects and builds a SPA app out of it.

I would prefer keeping the code "stuctured" but "seperated". Which I'm unsure if this is the case if nuxt allows sub-apps. It's just one special use case that is covered with it (think about e.g. a storybook for your components, another cordova app, or something else people can come up with.. ;))

So for us, we have all freedom and all advantages combined with a monorep approach (which is also used in different projects and is commonly used everyhwere around ;)) and still have a very strickt seperation of code and logik, therefore I would vote for a better documentaton or CLI for a monorepo setup (with a component library or other templates in it) instead.

@pimlie pimlie mentioned this issue Sep 5, 2019
13 tasks
@MartinMuzatko
Copy link

See nuxt/nuxt#5816 please

@MuhaddiMu
Copy link

Do you think below articles covers some parts of it?
If no, what are the limitations?
https://www.muhaddis.info/how-to-use-multiple-nuxt-js-applications-on-the-frontend/

@MartinMuzatko
Copy link

I explained at length that this does not cut it for me here: nuxt/nuxt#5816 (comment)

@MuhaddiMu
Copy link

I explained at length that this does not cut it for me here: nuxt/nuxt.js#5816 (comment)

I go through that I understand that you are looking for something else. Here I'm taking a general opinion the post

@kytosai
Copy link

kytosai commented Jan 28, 2020

I hope the development team nuxt will be able to offer a solution to this problem. It really is a matter of necessity..

@asterd
Copy link

asterd commented Aug 3, 2020

Any news on this?

@natoehv
Copy link

natoehv commented Aug 21, 2020

what about https://podium-lib.io approach? I just create a vue csr podlet (podium) and it works fine, and now I want to create a nuxt podlet, but I'm issues to use podium as a nuxt midleware :(

@hjardines
Copy link

This is something that will bring NuxtJS to the next level. I'm working with the same intention trying to see the best solution, having this native would be amazing.

@brophdawg11
Copy link

I have a semi-related use-case that might fall within this RFC, but happy to open a new issue or RFC for discussion if the concepts are different enough.

I'm trying to figure out if it's possible to hydrate 2 Nuxt micro-apps on the same page (i.e. a header and a footer when the entire page is not necessarily controlled by Vue/Nuxt). I've made some progress but I still have some more investigation to do into whether I can support this with existing hooks/modules or not.

We're currently doing this through a manual vue-server-renderer based application, but are looking to migrate to Nuxt and this seems to be the last hurdle we need to tackle. To keep this comment short, I won't go too deep into the implementation details, but I can put together a small POC repo with an example if that would be helpful.

Our use-case is a route-by-route migration of a large production site over to Vue SSR. Instead of a big-bang migration from our legacy architecture to Vue, we've been moving route-by-route. We have a small proxy that directs supported routes to the new vue-server-renderer-driven Vue SSR app, and unsupported routes to the legacy app. In order to maintain a consistent look and feel during the migration - we chose to export the header and footer Vue components as small consumable micro-apps that could be ingested by the legacy app.

The legacy server makes a call such as https://new-app-server/ui/header and gets back an HTML fragment and corresponding styles/scripts from the Vue app that it can inject into it's rendered HTML. In order to avoid duplicated styles/scripts we have query params that can tell the footer orchestration call to skip including <link>/<script> tags.

Then in the manual vue-server-renderer-driven app, we have a small bit of logic in entry-client.js that instead of just mounting to div#app, loops through looking for multiple mount-points based on data attributes and mounts them using the same Vuex store across the apps (since the header/footer share a Vue store in the main app). Sharing the Vuex store works seamlessly thankfully.

Routing poses an interesting challenge. In our case, we don't have any needs for client-side routing from the header or footer since the legacy app is not a SPA. There's a few potential options here:

  • Don't include a router, but can cause issues if the existing header/footer access this.$route/etc
  • Use a single route like { path: '*', component: HeaderComponent }, but that can conflict ith the normal Nuxt router entries and also have some weird side effects if the legacy app is doing any of its own pushState logic
  • At the moment, we've written a little stubbed OrchestratedRouter library that is effectively api-compatible with VueRouter but just no-op's on the majority of hooks. It's probably no the ideal solution but it has worked for our use-cases thus far.

I'm curious if this is something that this RFC might enable via Nuxt or that would be en entirely new RFC?

So far, I've gotten pretty close in Nuxt with 3 main changes:

  • A new layout that removes the app shell and only renders <Nuxt />
  • A pages/ui/_component.vue page that renders the proper component using render: h => h(componentMap(this.$route.params.component)
  • A root-level {% if (microApp} %} branch in app.html that eliminates the wrapper <html>/<body> markup and only renders styles/scripts and Vue SSR HTML

So the last part that I'm investigating now is whether I have any access to tap into the hydration internals and hydrate multiple apps while sharing a Vuex store.

@brophdawg11
Copy link

I took a quick stab at the above in nuxt/nuxt#8554. Didn't seem to be able to get to the hydration internals through a module.

@atinux atinux added the pending label Jan 25, 2021 — with Volta.net
@joaojuicee
Copy link

This would be amazing, it's not the first time that a client requested a "monorepo" style project where in the same project he'd like to have the frontend but also an admin dashboard and later on decided that he wanted to deploy it as two separate parts.
It would be amazing to be able to separate the bigger project into smaller projects with their own bundle

@amithcabraal-borngroup
Copy link

I have a semi-related use-case that might fall within this RFC, but happy to open a new issue or RFC for discussion if the concepts are different enough.

I'm trying to figure out if it's possible to hydrate 2 Nuxt micro-apps on the same page (i.e. a header and a footer when the entire page is not necessarily controlled by Vue/Nuxt). I've made some progress but I still have some more investigation to do into whether I can support this with existing hooks/modules or not.

We're currently doing this through a manual vue-server-renderer based application, but are looking to migrate to Nuxt and this seems to be the last hurdle we need to tackle. To keep this comment short, I won't go too deep into the implementation details, but I can put together a small POC repo with an example if that would be helpful.

Our use-case is a route-by-route migration of a large production site over to Vue SSR. Instead of a big-bang migration from our legacy architecture to Vue, we've been moving route-by-route. We have a small proxy that directs supported routes to the new vue-server-renderer-driven Vue SSR app, and unsupported routes to the legacy app. In order to maintain a consistent look and feel during the migration - we chose to export the header and footer Vue components as small consumable micro-apps that could be ingested by the legacy app.

The legacy server makes a call such as https://new-app-server/ui/header and gets back an HTML fragment and corresponding styles/scripts from the Vue app that it can inject into it's rendered HTML. In order to avoid duplicated styles/scripts we have query params that can tell the footer orchestration call to skip including <link>/<script> tags.

Then in the manual vue-server-renderer-driven app, we have a small bit of logic in entry-client.js that instead of just mounting to div#app, loops through looking for multiple mount-points based on data attributes and mounts them using the same Vuex store across the apps (since the header/footer share a Vue store in the main app). Sharing the Vuex store works seamlessly thankfully.

Routing poses an interesting challenge. In our case, we don't have any needs for client-side routing from the header or footer since the legacy app is not a SPA. There's a few potential options here:

  • Don't include a router, but can cause issues if the existing header/footer access this.$route/etc
  • Use a single route like { path: '*', component: HeaderComponent }, but that can conflict ith the normal Nuxt router entries and also have some weird side effects if the legacy app is doing any of its own pushState logic
  • At the moment, we've written a little stubbed OrchestratedRouter library that is effectively api-compatible with VueRouter but just no-op's on the majority of hooks. It's probably no the ideal solution but it has worked for our use-cases thus far.

I'm curious if this is something that this RFC might enable via Nuxt or that would be en entirely new RFC?

So far, I've gotten pretty close in Nuxt with 3 main changes:

  • A new layout that removes the app shell and only renders <Nuxt />
  • A pages/ui/_component.vue page that renders the proper component using render: h => h(componentMap(this.$route.params.component)
  • A root-level {% if (microApp} %} branch in app.html that eliminates the wrapper <html>/<body> markup and only renders styles/scripts and Vue SSR HTML

So the last part that I'm investigating now is whether I have any access to tap into the hydration internals and hydrate multiple apps while sharing a Vuex store.

Hi,

Did you ever continue with this ?
A

@MarioC3
Copy link

MarioC3 commented Oct 12, 2021

Hello guys! Great work so far. Quick question, is this still on the roadmap for Nuxt3?

@pi0
Copy link
Member Author

pi0 commented Oct 13, 2021

@MarioC3 Yes! Part of POC is in nuxt-extend which needs to be ported to nuxt kit + some refactors on nuxt3 core to fulfill requirements.

@MarioC3
Copy link

MarioC3 commented Oct 14, 2021

@pi0 Awesome! nuxt-extend is great! Again, thanks for all the work! You guys rock!

@brophdawg11
Copy link

@amithcabraal-borngroup Nothing beyond the quick POC in nuxt/nuxt#8554 unfortunately

@atinux
Copy link
Member

atinux commented Mar 24, 2022

It's coming 👀

nuxt/nuxt#13367

@davodaslanifakor
Copy link

This is my last work for implement this type of feature.

https://medium.com/@DavoudAslaniFakour/creating-a-nuxt-js-project-with-yarn-workspace-lerna-monorepo-in-3-steps-bdd379f4e0ba

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
nuxt 3 Nuxt.js 3 targeted RFC pending
Projects
None yet
Development

No branches or pull requests