unjs / unhead

Unhead is the any-framework document head manager built for performance and delightful developer experience.

Home Page:https://unhead.unjs.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bring back "idle" trigger

enkot opened this issue · comments

Describe the feature

In the latest 1.9.0 release "idle" trigger was removed in favor of user implementation. To me, unhead has always been about simplifying things, and removing "idle" is a step backwards, IMHO.

Now users will be writing something like this or create custom useIdleScript wrapper, which doesn't make life easier :)

import { waitIdle } from '../utils'

const { $script } = useScript('https://example.com/script.js', {
  trigger: waitIdle
})

Can we still have an "idle" trigger with some note in the docs that it doesn't work in all browsers and you can polyfill the requestIdleCallback?

Additional information

  • Would you be willing to help implement this feature?

I do agree with you that it provides a poorer end-user experience, thank you for the feedback.

However, the scope of the useScript is first and foremost to support high-level integrations and abstractions as a first concern. This requires providing a minimal and solid core to build off of.

For example, in Nuxt, we instead will ship a onNuxtReady hook which is implemented like so.

export type NuxtUseScriptOptions<T = any> = Omit<UseScriptOptions<T>, 'trigger'> & {
  trigger?: UseScriptOptions<T>['trigger'] | 'onNuxtReady'
}

export function useScript<T>(input: UseScriptInput, options?: NuxtUseScriptOptions<T>) {
  input = typeof input === 'string' ? { src: input } : input
  options = options || {}
  if (options.trigger === 'onNuxtReady')
    options.trigger = new Promise(resolve => onNuxtReady(resolve))
  return _useScript<T>(input, options as any as UseScriptOptions<T>)
}

Using onNuxtReady over idle is actually important as there's a suspense edge case that this hook takes care of.

You can implement your own useScript with an idle hook in the same way.

import { useScript as _useScript, type UseScriptInput } from '@unhead/vue'
import type { UseScriptOptions } from '@unhead/schema'

export function useScript<T>(input: UseScriptInput, options?: Omit<UseScriptOptions<T>, 'trigger'> & {
  trigger?: UseScriptOptions<T>['trigger'] | 'idle'
}) {
  input = typeof input === 'string' ? { src: input } : input
  options = options || {}
  if (options.trigger === 'idle')
    options.trigger = new Promise(resolve => window.requestIdleCallback(() => resolve()))
  return _useScript<T>(input, options as any as UseScriptOptions<T>)
}

I am considering exporting a composable directly from Unhead with the idle callback polyfill as a Promise, at least then it can be tree shaken.

Thank you for the detailed answer.

onNuxtReady - I see, yes, makes sense to run requestIdleCallback after suspense:resolve. Looks like insider news about Nuxt 4.0 :)

In general, I understand that this is a difficult question about supporting idle out of the box, because Safari still does not support it.