Introduction to Nuxt plugins and modules
I have already explained how we can use MSW to test our Nuxt application’s communication with the backend. I made it work by importing axios
and assigning wrapper.vm.$axios = axios
.
However, this is actually a bad hack, since @nuxtjs/axios
and axios
aren’t the same. The former is a Nuxt.js module that wraps axios
and offers more functionality on top of it. @nuxtjs/axios
and axios
happen to have a similar API which enables us to use the latter in place of the former and mock any additional methods we use (such as setToken
). But what will we do in other cases where we use a Nuxt.js module that does not wrap something with a similar API? How can we use a Nuxt.js module in our tests? To answer that we need to know some internals of Nuxt.js modules.
A Nuxt.js module is often a wrapper around a Nuxt.js plugin.
A Nuxt.js plugin is often a wrapper around a Vue plugin.
Let’s go through these from the bottom up.
Vue plugins
After the snippet above executes, myVueInstance.$hello
will have the value “world”.
A Vue plugin is an object (or module) containing an install()
function (the plugin can also be a function itself instead). What Vue.use()
does is register the plugin and, if it is not already registered, execute the function.
Vue plugins can be installed (with Vue.use()
) either before or after creating the Vue
instance.
Nuxt plugins
A Nuxt plugin is a JavaScript file (typically in the plugins
directory) whose name is specified in the plugins
property in nuxt.config.js
. The file may or may not export a function.
Nuxt plugins that do not export a function
Here is an example of a Nuxt plugin that does not export a function:
When Nuxt starts, it merely imports the file.
When a Nuxt plugin merely wraps a Vue plugin, it can simply execute Vue.use(theVuePlugin)
.
Nuxt plugins that export a function
When Nuxt starts, it will import the file and execute the function, passing it the Nuxt context and the inject
function. In this particular case, inject('hello', 'world')
will do something similar to the following:
Why not simply write the above instead of exporting a function that receives a callback inject
function and calls it? Because the inject
function is smart. Depending on Nuxt configuration and environment (e.g. SSR or client), it might decide to add the attribute only to Vue
, or only to the context
, or also add it to the store, etc. By just executing inject('hello', 'world')
, we tell Nuxt to add (“inject”) the $hello
attribute to wherever it needs to be added.
Nuxt modules
A Nuxt module is a JavaScript file that exports a function and is specified in the modules
property of nuxt.config.js
. Nuxt calls the function when it starts.
The difference between a Nuxt module and a Nuxt plugin is that a module runs earlier. Nuxt first loads and executes modules, then creates the Vue instance and the Nuxt context, and then executes Nuxt plugins. As a result, Nuxt modules can do more customization; for example, they can add plugins. I’m still not entirely clear on that, but for the purposes of this post it doesn’t matter, because it is given that we have a module (@nuxtjs/axios) and we need to load it for testing.
How @nuxtjs/axios works
@nuxtjs/axios is a Nuxt module—a .js file that exports a function. Nuxt loads the file and executes the function.
One of the things the function does is execute Nuxt’s addPlugin()
method. This is the way to programmatically load a plugin without needing to add it to the plugins
property of nuxt.config.js
. Since this plugin does not have a name, we will call it “the Nuxt axios plugin”.
So, Nuxt starts. It initializes the @nuxtjs/axios module. The module, among other things, executes Nuxt’s addPlugin()
, thus registering the Nuxt axios plugin. Later (after finishing loading all modules, creating the Vue instance and Nuxt context, and possibly doing other initialization work) Nuxt will initialize the Nuxt axios plugin. What the plugin initialization does is create an Axios instance, extend it with some additional methods (such as setToken
), and then call Nuxt’s inject
to add it as the $axios
property of Vue
(and of the Nuxt context).