docusaurus 2: run custom script for every page - docusaurus

Is it possible to run some custom script for every page?
E.g., I want to run alert(1); on every page. How can I do that without the sizzling of any components?
I know it can be done by creating jsx component and using it in every .mdx file (but every doc then should be a .mdx file). So it's not the thing I'm looking for.

Docusaurus 2 user here! 👋
Docusaurus is Server-Side Rendered and then hydrated to function as a Single Page Application. Without knowing more about what you want to achieve, I can only try to give you a general advice.
One way of achieving this is to create your own plugin, it gives you access to the execution context, such as router events.
I currently use this for analytics reporting when the user changes page. It's not yet documented, but there's a good example in the Docusaurus 2 repository in the docusaurus-plugin-google-analytics package.
Here's a fragment of what I use, this only executes when a new page is loaded, which fits my use case perfectly. There may be another lifecycle hook called when the page is hydrated that I haven't found yet.
analytics-module.js
import ExecutionEnvironment from "#docusaurus/ExecutionEnvironment";
export default (function () {
if (!ExecutionEnvironment.canUseDOM) {
return null;
}
return {
onRouteUpdate({ location }) {
_paq.push(["setCustomUrl", location.pathname]);
_paq.push(["setDocumentTitle", document.title]);
_paq.push(["trackPageView"]);
},
};
})();

Related

asyncData hook when hard refreshing in Nuxt

I just realized that the asyncData hook is not called when hard refreshing the page. But I have important data to load to show on that page. And I want to make sure that they are always available, even when the user hard refreshes the page.
asyncData from the documentation
the promise returned by the asyncData hook is resolved during route transition
In that case, the best way is to use the fetch() hook and display a loader while you do finish your calls thanks to the $fetchState.pending helper.
Actually, I do think that fetch() is better in many ways.
Quick article on the subject: https://nuxtjs.org/blog/understanding-how-fetch-works-in-nuxt-2-12/
The one on the bottom of the page (Sergey's) is cool with some practical example of a nice implementation too.
You could also use this somewhat hacky solution in a layout to see if the initial location (the one you are when you hard refresh) is the one you want to be. Basically, if you land on a specific page (hard refresh or following a new window page for example) but want to have some custom behavior.
beforeCreate() {
if (!['some-other-page'].includes(this.$router.history._startLocation)) {
this.$router.replace({ name: 'index' }).catch(() => {})
}
},
Still, this one infinite loops if used in a middleware (or I made a mistake) and it's less clean than a fetch() hook.

How to get access to template in asyncData?

I want to get access to this.$el in asyncData.
I use a database to store translations.
I want to get a list of translations that are used on the current page.
Then I will send a request to the server to receive them.
After that, I will merge it.
i18.mergeLocaleMessage( locale, message )
How to do it ?
You can access i18n with something like this, no need to access the template for this use case
asyncData ({ app }) {
console.log(app.i18n.t('Hello'))
}
Looking at the lifecycle of Nuxt, asyncData will happen before any template is generated at all, so it is impossible with asyncData.
And even if it was possible with some hacky trick, it would be a bit strange to have to look inside of your template to then have some logic for i18n fetching.
Why not getting a computed nested object and loop on this through your template, after you have fetched all of your required translations ?
Also, you're using asyncData + an API call each time ? So, for every page: you will stop the user, let him wait for the API call and then proceed ?
Latest point, if you are on your page and you hit F5, then asyncData hook will not be triggered. Just to let you know about this caveat.
Alternative solutions:
using the fetch() hook and display a loader until you have fetched all your translations, still better to not rely on the content of the template. This will work even on F5 and can produce a more smooth experience (by not blocking the navigation).
getting your i18n whole translations globally, at some point when your user's connection is idle. Rather than on per-page. Especially because you will need to handle the logic of not fetching some translations that you already have (if the user visits a page twice).

Nuxt choosing layout before initialize

I use nuxt-generate and nuxt/device package. which provides the device type but for the layout since I use nuxt-generate at first load app uses default layout. not the one provided by nuxt/device. but after i go another page it starts using correct layout. so the problem is how can i have the app use layout at first initialize. or at least change it after initialize. is there any way to do these?
this is how i try to choose layout but never works at first open.
layout (context) {
if(context.isMobile) {
return 'mobile'
} else if (context.isDesktop) {
return 'default'
}
},
when you use nuxt-generate nuxt generates a series of static html files, I don't think there is an easy way to solve this, but there can be a series of workarounds:
use nuxt in ssr mode, so that you have a way of knowing who is calling that particular resource
make a distinction at the level of routes like (/desktop/*** and /mobile/***) where both routes lead to a different layout and in the webserver configuration do a redirect on the correct route
do a middleware that redirects to the correct route /desktop /mobile, but I don't recommend it because you lose the advantage of static generation of nuxt

How To Ensure Reference Data Is Loaded In Vue?

I have webpack setup to bundle all of the source. I have a Vue object that is the page and multiple Vue components of my own creation. This works great!
I am now getting reference data from the database to fill in certain default options for some of these components. In my pages Mounted or Created events (there is no difference for my question) I am calling a method that will check to see if the data exists in localStorage and if not, it will extract the data from the database.
Once Extracted, I have it in localStorage so it is not an issue. However, the first time I need to gather the data (or when I need to refresh it because I have another trigger that lets me know when it has changed) the page and components have rendered (with errors because of lack of data) before the data comes back. The fetch method is in a promise, but mounted events don't seem to care if a promise exists within in before it continues to the next component.
So what is the best practice for loading/refreshing reference data in Vue? I am currently not using VueX because this is not a SPA. Sure, it is a single page that is doing things (there are many single pages that do their own thing in this site) but I have no need to make it a full SPA here. But If VueX and its store will give me some sort of guarantee that it will occur first or page/components will run AFTER VueX things, I will learn it.
Have you tried doing so:
<component v-if="page.isDataLoaded">...</component>
in your Vue-component:
data() {
return {
page: {
isDataLoaded: false,
}
}
},
mounted() {
this.fetchPageData().then(() => this.page.isDataLoaded = true);
}
You can use v-if and v-else to show, for example page loader element like so:
<PageLoader v-if="!page.isDataLoaded"></PageLoader>
<component v-else>...</component>

Custom Login page in Docusaurus

I have a task at work, It is to implement the login page inside the Docusaurus.
I am trying to customizing the index.js by adding some HelloWorld. Including login.js which has actual Docusaurus Index page
const Index = require('./login.js');
const React = require('react');
class Button extends React.Component {
render() {
return ("helloworld");}
}
module.exports = Button;
But Error thrown is:
Error: Cannot find module './login.js'
Is it possible to call the class from another js page in Docusaurus?
Docusaurus maintainer here! Yes, it should be possible as the current module system is CommonJS. You have to put that component in the same directory as index.js. Link me to a repository if possible and I can help you take a look.
On a side note, it doesn't make much sense to build a login form for Docusaurus as Docusaurus generates a static site that loses state across page navigations unless you persist them in cookies or localStorage. It'd be quite troublesome to do so. Maybe you could explain what you're trying to build here and I can recommend you better alternatives.
I found a way to work this out, even though there is no official documentation on this matter. I used the Docusaurus Swizzle to intervene in the Root component so that I can use Firebase Authentication (I think that you can use Auth0 or any custom logic in replacement).
I wrote a detailed blog on this so that I hope it can help others, you can see it here: https://medium.com/#thomasdevshare/docusaurus-authentication-with-firebase-c824da24bc51
There is also full example source code in the article, you can clone it.
First of all. What does "login.js" look like? It is an exported module with a well defined (default) namespace?
Secondly, you shouldn't add the file extension to an import. It's "require('./login')" not "require('./login.js')".