🔌⚡ Nuxt module to prune html before sending it to the browser (it removes elements matching CSS selector(s)), useful for boosting performance showing a different HTML for bots/audits by removing all the scripts with dynamic rendering
Nuxt module to prune html before sending it to the browser (it removes elements matching CSS selector(s)), useful for boosting performance showing a different HTML for bots/audits by removing all the scripts with dynamic rendering.
Due to the versatility of Nuxt (and of the SSR in general), a website generated (or served) via node server, has everything it needs already injected in the HTML (ex. css styles). So, usually, for a bot, a audit or for a human, the website its almost visually the same with or without Javascript.
This library is born to remove all the scripts injected into the HTML only if a visitor is a Bot or a Performance Audit (ex. a Lighthouse Audit). This should speed up (blazing fast) your nuxt-website up to a value of ~99 in performance because it cheats various scenarios.
Usually, with less assets, resources and html to download, the number of urls crawled by a bot are widely boosted 📈.
This could cause some unexpected behaviors, but..
Cons.:
SPA routing
on client-side
for bots and audits;hydration
on client-side
for bots and audits:
vue-lazy-hydration
need Javascript client-side code to trigger hydrateOnInteraction, hydrateWhenIdle or hydrateWhenVisible;<client-only>
components;lazy-load
for images.Pros.:
SPA routing
;<client-only>
components could lead in a slower TTI;<client-only>
components can contain a static placeholder;lazy-load
can be fixed with a native attribute, with a custom script
or with classesSelectorsToKeep
(check the configuration);Hydration
decrease performance, so it's ok to prune it for bots or audits
;N.B.: This is known as Dynamic Rendering and it's not considered black-hat or cloaking.
@luxdamore/nuxt-prune-html
as a dependency:
yarn add @luxdamore/nuxt-prune-html
;npm install --save @luxdamore/nuxt-prune-html
;@luxdamore/nuxt-prune-html
to the modules
array of your nuxt.config.js
.
// nuxt.config.js
export default {
// Module - installation
modules: [ '@luxdamore/nuxt-prune-html' ],
// Module - default config
pruneHtml: {
enabled: false, // `true` in production
hideGenericMessagesInConsole: false, // `false` in production
hideErrorsInConsole: false, // deactivate the `console.error` method
hookRenderRoute: true, // activate `hook:render:route`
hookGeneratePage: true, // activate `hook:generate:page`
selectors: [
// CSS selectors to prune
'link[rel="preload"][as="script"]',
'script:not([type="application/ld+json"])',
],
classesSelectorsToKeep: [], // disallow pruning of scripts with this classes, n.b.: each `classesSelectorsToKeep` is appended to every `selectors`, ex.: `link[rel="preload"][as="script"]:not(__classesSelectorsToKeep__)`
link: [], // inject custom links, only if pruned
script: [], // inject custom scripts, only if pruned
htmlElementClass: null, // a string added as a class to the <html> element if pruned
cheerio: {
// the config passed to the `cheerio.load(__config__)` method
xmlMode: false,
},
types: [
// it's possibile to add different rules for pruning
'default-detect',
],
// 👇🏻 Type: `default-detect`
headerNameForDefaultDetection: 'user-agent', // The `header-key` base for `MobileDetection`, usage `request.headers[ headerNameForDefaultDetection ]`
auditUserAgent: 'lighthouse', // prune if `request.header[ headerNameForDefaultDetection ]` match, could be a string or an array of strings
isAudit: true, // remove selectors if match with `auditUserAgent`
isBot: true, // remove selectors if is a bot
ignoreBotOrAudit: false, // remove selectors in any case, not depending on Bot or Audit
matchUserAgent: null, // prune if `request.header[ headerNameForDefaultDetection ]` match, could be a string or an array of strings
// 👇🏻 Type: 'query-parameters'
queryParametersToPrune: [
// array of objects (key-value)
// trigger the pruning if 'query-parameters' is present in `types` and at least one value is matched, ex. `/?prune=true`
{
key: 'prune',
value: 'true',
},
],
queryParametersToExcludePrune: [], // same as `queryParametersToPrune`, exclude the pruning if 'query-parameters' is present in `types` and at least one value is matched, this priority is over than `queryParametersToPrune`
// 👇🏻 Type: 'headers-exist'
headersToPrune: [], // same as `queryParametersToPrune`, but it checks `request.headers`
headersToExcludePrune: [], // same as `queryParamToExcludePrune`, but it checks `request.headers`, this priority is over than `headersToPrune`
// Emitted events for callbacks methods
onBeforePrune: null, // ({ result, [ req, res ] }) => {}, `req` and `res` are not available on `nuxt generate`
onAfterPrune: null, // ({ result, [ req, res ] }) => {}, `req` and `res` are not available on `nuxt generate`
},
};
With link
and script
it's possibile to add one or more objects on the pruned HTML, ex.:
export default {
pruneHtml: {
link: [
{
rel: 'preload',
as: 'script',
href: '/my-custom-lazy-load-for-bots.js',
position: 'phead', // Default value is 'body', other allowed values are: 'phead', 'head' and 'pbody'
},
{
rel: 'stylesheet',
href: '/my-custom-styles-for-bots.css',
position: 'head',
},
],
script: [
{
src: '/my-custom-lazy-load-for-bots.js',
lazy: true,
defer: true,
},
],
},
};
N.B.: the config is only shallow merged, not deep merged.
Possible values are [ 'default-detect', 'query-parameters', 'headers-exist' ]
:
default-detect
: prune based on one header(request.headers[ headerNameForDefaultDetection ]
)
isBot
, trigger .is( 'bot' )
method;auditUserAgent
or matchUserAgent
, trigger .match()
method;query-parameters
: prune based on one or more query parameter, tests key / value
based on queryParametersToPrune / queryParametersToExcludePrune
:
nuxt.config
, ex. { generate: { routes: [ '/?prune=true' ] } }
headers-exist
: prune based on one or more header, tests key / value
based on headersToPrune / headersToExcludePrune
.N.B.: It's possibile to mix different types.
request.headers
only if the project is running as a server (ex. nuxt start
)
generate
your site it's not possibile to check request.headers, so (for types: [ 'default-detect', 'headers-exist' ]
) it always prune, but You can disable this behavior by setting hookGeneratePage
to false
(or by using the type query-parameters
);types: [ 'default-detect' ]
, load the MobileDetect library;<client-only>
components you should prepare a version that is visually the same with the placeholder slot;git clone https://github.com/LuXDAmore/nuxt-prune-html.git
;yarn install
(or npm install
);yarn dev
(or npm run dev
);yarn test
(or npm run test
);yarn generate
(or npm run generate
);/docs
folder.Please make sure to read the issue reporting checklist before opening an issue. Issues not conforming to the guidelines may be closed immediately.
We're using Github discussions as a place to connect with other members of our community. You are free to ask questions and share ideas, so enjoy yourself.
Please make sure to read the contributing guide before making a pull request.
Details changes for each release are documented in the release notes.
MIT License // Copyright (©) 2019-now Luca Iaconelli
If You want to share a beer, we can be really good friends 😄
☀ It's always a good day to be magnanimous - cit.