Pass Shopify AppBridge instance to various Nuxt / Vue components - vue.js

I'm trying to initialize a Shopify AppBridge instance (https://shopify.dev/tools/app-bridge) one time per store (user), and then use that same instance throughout my app to use the various AppBridge features.
My original idea was to add the AppBridge instance into a Vuex store; but I am unable to call any functions within the AppBridge object when retrieving it from the store (something to do with it being a serialized object?). There are various functions I need to use within the instance/object; so this won't work.
So, what is the best way for me to do the following:
App loads
Middleware or function runs, initializing AppBridge with the user's / store's details
Other pages are loaded, use the same AppBridge instance for various features
My current setup is that I re-create a new AppBridge instance on every page / component, but that causes complications.
Any ideas? Much appreciated!

Related

Why use Vuex in SSR?

My question is, what's the point of setting up Vuex for the server when the state will be overwritten when the client side hydration takes place?
I have some data (Helm env variables) that I want to store in the vuex store for later use.
These variables is only available to me on the server, so I started trying to add them to the store in my createApp script when running on the server.
The store state however is reset when the client side hydration kicks in, so no env variables left.
Google told me I should use like window.INITIAL_DATA to set the state again on the client:
store.replaceState(window.INITIAL_DATA)
But if have to use the window object to pass store data to the client, what's the point of using Vuex on the server at all?
Isn't it better to skip Vuex overhead on the server and just use Vuex on the client and populate it with INITIAL_DATA?
I'm probably missing something..
https://ssr.vuejs.org/guide/data.html#data-store
During SSR, we are essentially rendering a "snapshot" of our app. The asynchronous data from our components needs to be available before we mount the client side app - otherwise the client app would render using different state and the hydration would fail.
To address this, the fetched data needs to live outside the view components, in a dedicated data store, or a "state container". On the server, we can pre-fetch and fill data into the store while rendering. In addition, we will serialize and inline the state in the HTML after the app has finished rendering. The client-side store can directly pick up the inlined state before we mount the app.
Also to mention:
The data you access while SSR in any Component needs to come from somewhere if you want to share information across Components, this is what Vuex is there for.

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.

Vue instance per component over single instance

due to the structure of our websites we currently are unable to create one main app instance, as there is too much HTML within this.
So instead we are currently looking for the class of app and then creating a new Vue instance per component, which isn't great for communicating between components but it's our current work around.
We are working to create a new structure to support just one overall app. However, just wondering if creating a new instance of Vue for each component is bad for browser performance over having just one instance with the component inside this?
Short answer: No
There won’t be any performance difference between an app that uses a root Vue component with child components and an app that uses multiple root Vue components.
All components are still just Vue instances - so it isn’t any different. The only difference is the organization and usage of the instances.

Application-wide globals in vue.js

Does Vue.js have an idiomatic way of declaring application-wide globals like baseUrl and then referencing them throughout the app (for API calls etc)?
If so, what is the best way to configure such globals at build-time to create development, test and prod instances? For example, I imagine doing export BASEURL='http://dev.myapp.com', running a build and getting an app with this baseUrl configured. This would in turn allow me to create automated builds for Continuous Delivery certain branches are updated (develop -> dev.myapp.com, master -> www.myapp.com etc).
Thanks!
What I do: use some state management tool, like Vuex.
If you don't want to add complexity to your app, I use a parent Vue instance, let say App.vue and define global data in that instance, then when I need that data from the parent I call this.$root.baseUrl in case you want the baseUrl. Notice that you can also call this.$parent.baseUrl but this will only work for direct childs, if you are inside a child of a child, you wont get the global data, that's why is better the $root object.

How to store app-wide variables when using CommonJS?

I am developing apps using Titanium and trying to implement the CommonJS approach. I like the modular setup, but I am wondering how best to deal with things like a shopping cart: temporary, user-created data that needs to last through the lifetime of the app.
I can see three approaches:
1. Create a special module for such a Cart. It will be created the first time it's require()d, and you can access the cart in its current state from any other module by require()ing it from those modules.
Pass a quasi global Cart object to every module that needs it. This is in breach of the letter and the spirit of CommonJS.
Store the Cart in local memory using Ti.App.Properties. This way, the cart is retained even when the user quits the app.
Any thoughts on what would be best?
The solution I'd prefer is to create a CommonJS module the following way:
function ShoppingCart(options) {
// do some setup for the shopping cart
}
ShoppingCart.prototype.add(product, qty)
// add product to cart
}
ShoppingCart.prototype.remove(product, qty)
// remove product from cart
}
ShoppingCart.prototype.clear()
// empty cart (and create new, empty one)
}
// etc.
ShoppingCart = new ShoppingCart();
module.exports = ShoppingCart;
How to access?
var Cart = require('path/to/ShoppingCart');
Cart.add();
Cart.remove();
Cart.clear();
This creates a kind of singleton which is created the first time you're calling it and it is kept until the app is finished (removed from memory) or you implement the clear method and clean it by yourself. You can also persist the data using this singleton, it's up to you which parts you gonna implement. It's similar to your first idea.
Your second idea has several disadvantages because data access isn't encapsulated into a module and data is persisted always so you need to detect if it is old and can be removed or not.
Finally it depends on your task. Do you need persisten storage you should combine the module with a database. Do you need this information only during runtime, the module is enough.