Vue page cannot open after refreshing the page - vue.js

I added the routes dynamically to the router and I can visit all pages with router-view. However, when I try to refresh the page I cannot visit the page again although the URL is the same. It is http://localhost:8081/me.
this.$router.addRoute("Main", {
path: '/me',
name: 'Me',
component: () => import(`#/components/Me.vue`)
});
As I said, I can normally visit the page "Me", but when I refresh it, I cannot see its content anymore with the warning:
[Vue Router warn]: No match found for location with path "/me"
I tried to create router with createWebHistory("/") after reading the cause of the error but nothing seems to change. I will appreciate any help.

There are two reasons why this would happen.
First serving SPA application from the server.
Make sure that your back-end is set to serve index.html file for all routes since back-end server is unaware of the routes set by client-side router. Something like express-history-api-fallback-middleware can help is using Node.js/Express.js.
Second problem is that of using addRoute.
As you described, the problem could be that Vue router is taking routing decision before your code in components/common/LeftMenu.vue is getting executed which is responsible for calling addRoute(). Ensure that this code is called before routing decision is being made. Best way would be to move this logic in top-level application component. And, if you can move that to top-level components, that means you can try to declare all routes while defining the router.
Why that should be done?
Using addRoute is not necessarily an anti-pattern but having to rely on addRoute is often code smell. You would really need it in extremely rare scenarios. Routing is a high-level architectural concern for your application and thus better to be declared upfront somewhere at top-level module even before application is getting initialized. Low level components should not attempt to manipulate routing data structure (violation of DIP principles).
One scenario where you might be tempted to use addRoute is taking decision after some data manipulation by the component with addition of some API call. That seems legitimate need but then to address that there are better ways. Considering using route guards (guards support async resolution too!) and prevent user from entering the view till the guard is resolved successfully.

Related

NextJS multiple dynamic routes with different destionation

In my project I have the following requirement: having 2 routes that are dynamic but that are going to be drawn by different components.
More context:
I have a CMS that provides all the info and in most of the cases I'm going to read from a "page" model. This is going to be drawn by the Page component and its going to have a getServerSideProps that makes a call to a CMS endpoint to get that information
The endpoint and component are not the same for the other case with a dynamic route
Both types of routes are completely dynamic so I cannot prepare then in advance (or at least I'm trying to find another solution) since they come from the CMS
As an example, I can have this slugs
mypage.com/about-us (page endpoint & component)
mypage.com/resource (resources endpoint & component)
Both routes are configured using dynamic routes like this
{
source: '/:pages*',
destination: '/cms/pages'
}
The problem here is that it can only match one of the endpoints and the logic for both calling the endpoint and the component used to draw it are completely different
Is there a way of fallbacking to the next matched URL in case that there's more than one that matches similar to the return {notFound: true}?
Some solutions that I have are:
Somehow hardcode all the resources urls (since are less than pages) and pass them as defined routes instead of dynamic routes
Make some dirty code that will check if the slug is from a resource and if not fallback to a page and then return a Resource component or a Page component depending on that (this idea I like it less since it makes the logic of the Page component super dirty)
Thanks so much!

Multiple Vue apps, multiple entry files, same Vuex/Vue3CompostitionApi store [lost reactivity]

I am trying to iteratively replace .cshtml razor views with what I wanted to call Vue "mini-apps". Which should be somewhere in between a micro-frontend and a classic SPA. The aim is to share some of the code base, mainly dependencies. Compile a common chunk-vendors.js and have the "mini-apps" as separate javascript entry files to place on appropriate views. As performance demand would grow, I would progress into splitting chunk-vendors.js and optimize via lazy-loading.
The problem I am hitting here is trying to make two root Vue instances talk to each other through a shared state. Right now only the app that is imported/mounted first stays reactive. I thought that my problem was in the Vue 2 reactivity system/how Vuex binds itself to a concrete Vue instance here. When I implemented a primitive store, the situation ended up being exactly the same.
What confuses me about this is that if I were to instantiate two applications in a single main.js entry file, the store sharing would just work. Which suggest that Vue is either creating some kind of hidden root instance or that my Vuex code analysis deduction of it binding to a concrete instance was incorrect.
I would highly appreciate it if someone could tell me why this can't work, optionally suggest a workaround?
I have created a reproduction both in Vue 2 with Vuex and in Vue 3 with composition API/primitiveStoreImplementation here.
Vue-cli is building the app in an MPA mode with pages specified in vue.config.json, then imported in the root index.html file. The store is initialised once and saved for later check/loading on the window object. In the context of asp/razor I would have webpack set up to remove the redundant files, only leaving javascript bundles. Also, the dev proxy would proxy everything except the path to the script bundles. All of this is removed for the sake of the demonstration.
(once I find a solution I hope to replace the source link with specific code snippets)
Options considered:
I am trying to avoid it, but I might have to always run a "coordinator" root instance that will check the presence of certain elements on a page and load/unload the "mini-apps" as components using something like portal-vue when needed. That coordinator would also contain a state with modules, some of which would be marked as "shared" thus operations from multiple "mini-apps" would be allowed (ownership flag check).
I have considered sessionStorage/localStorage, the problem is that the 'storage' events are only triggered across tabs and not within one document first |Note. I would have to trigger a custom event or play around with iframes. Feels too hacky, but that might be an axiom here. It would also duplicate the same state across many store instances.
These are some relevant articles I have found on this topic:
Probably closest to what I am trying to achieve:
Using Vuex with multiple Vue instances
Same but different:
Build Vue microfrontend app (with routing and vuex store)
The use case for multiple entries are sub-apps that don't coexist on the same page, although they can. They could be web components or regular app bundles. They can even interact with each other but they need something else for this - global event bus or a mediator app that passes data between them.
The problem is that there are more than one Vue library copies and/or more than one Vuex store instance. In order to avoid this, they would need to be precisely loaded only once on the page and reused between apps, i.e. vue and vuex are loaded as CDN libs, possibly used as Webpack externals to use window.Vue and window.Vuex transparently for respective import. Not only Vuex but store needs to be a singleton on the page (basically a said mediator). This is acceptable solution but primarily suitable for existing applications that have design restrictions and need a workaround.
I am trying to avoid it, but I might have to always run a "coordinator" root instance that will check the presence of certain elements on a page and load/unload the "mini-apps" as components using something like portal-vue when needed.
This is the common way to do this. Vue 3 has teleports that has give less control than portal-vue. It has no downsides for application design if done properly. The same thing is achieved similarly in other frameworks (Angular, React) as well, where portals appeared earlier.
I have considered sessionStorage/localStorage, the problem is that the 'storage' events are only triggered across tabs and not within one document
This is solved by using window postMessage and message event in the same tab. In case this shouldn't be limited to a single window, there are third party libs that use both for cross-tab synchronzation, a native alternative is BroadcastChannel that has less browser support than LS but doesn't have its limitations regarding tabs.

How to not rerender a component when revisiting a page in Nuxt.js?

When I visit another page and come back, I want to express the Google Maps coordinates and data that I had.
There's a lot of data that needs to be handled to use the Store, so it's hard to use.
I wonder if there is a way to recycle pages without erasing them from DOM.
If you wish to preserve the page's content as is while navigating, you may use keep-alive which will cache the route's content. See docs here.
However, my personal experience is that caching Vue or Nuxt pages with keep-alive sometimes leads to unexpected bugs in my applications since oftentimes I write code with the expectation that all the values I defined in data() are going to have the default value. But that's not the case when caching the components.
As you've said, this requirement often arises when some expensive data have been fetched. I prefer to either cache this fetch result (mostly in local storage) or in Vuex Store. One can then safely destroy the component when navigating away and reuse the cached data when navigating back, but the component will be otherwise initialized with the default data().
You can use keep-alive attribute:
https://nuxtjs.org/docs/2.x/features/nuxt-components#keep-alive
Then your mounted lifecycle will called only once.
But, there is no prevention to store huge amount of data in the vuex store.

Relay modern caching example

I would like to enable caching in my react native application. I am using GraphQL with Relay modern. I found out that caching is not enabled by default in relay modern, but they have exposed RelayQueryResponseCache from relay-runtime, which we can add to the fetchQuery function in our API. I read discussion here and here about it, but have not seen any example to get started. Can someone help me out on this?
EDIT:
Ok I came up with a solution. I think it misses few things but so far it serves our needs.
I have noticed that passing anything into QueryRenderer into cacheConfig results passing that value into fetchQuery function inside my environment.
So I have created a Component which loads the data by some relation and resolves it into the correct json structure requested by the query. Then I return this into the state. Then I extended my Component which contains QueryRenderer with the created 'cache loader'. Now when componentWillMount() is called I ask for the cached data. During this I have set this.state.loading = true so I am able to handle loading state. Reading from DB is async.
I am using this class in other components as well. Every one handles its cache data. I just pass it to QueryRenderer.
However I was thinking that this makes some extra logic need to add for each Component which is supported by this caching. Probably passing the cache resolver as cacheConfig and resolve the cached data immediately inside the environment would be much more cleaner.

How does one use :wildcards in parent routes?

I'm building an express app in express 4.0 (rc3), since I'm starting from scratch and in development for a while, but if there's a way to do this in 3.0, that'd be welcome too.
What I want is a set of comment REST routes that I can attach to other routes in my API. So:
/posts/:postID/comments/:commentID
/profiles/:profileID/comments/:commentID
The way I was doing it was to encapsulate the comment routes into a module, including a buildRoutes(router) function in the module.
Then I can do app.use('/api/comments', commentController.buildRoutes(express.Router())) in my main server definition, and then in my profile module's buildRoutes(router), I can do
buildRoutes = function(profileRouter)
.... build the basic CRUD routes ...
profileRouter.get('/:profileID', show)
profileRouter.use('/:profileID', commentController.buildRoutes(express.Router()))
It seems like only the .VERB methods actually replace :wildcards in the route, and not the .use one. I could always muddle through with a piece of custom middleware that goes on a /api/profiles/* and maps the appropriate URL parameters into req.fields, but I want to make sure that this is actually required.
So this wasn't particularly easy to do the way I originally intended. However, I just avoided the entire problem by reframing my buildRoutes method to accept a baseURL and a router argument. Instead of modularizing it completely, now I say, profileController.buildRoutes('/api/profiles/', router) which in turn calls commentController.buildRoutes('/api/profiles/:profileID/comments', router), and so on.
It's not terribly satisfying (I would rather encapsulate path/routing information and hide that from the controller) but it works.