feat(nuxt3): support lazy and custom-resolved components (#3814)

This commit is contained in:
Daniel Roe 2022-03-22 17:04:31 +00:00 committed by GitHub
parent 7458dd1aa6
commit 29078bba74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 48 additions and 4 deletions

View File

@ -47,6 +47,39 @@ If you have a component in nested directories such as:
For clarity, we recommend that the component's file name matches its name. (So, in the example above, you could rename `Button.vue` to be `BaseFooButton.vue`.) For clarity, we recommend that the component's file name matches its name. (So, in the example above, you could rename `Button.vue` to be `BaseFooButton.vue`.)
:: ::
## Dynamic components
If you want to use the Vue `<component :is="someComputedComponent">` syntax, then you will need to use the `resolveComponent` helper provided by Vue.
For example:
```vue
<template>
<component :is="clickable ? MyButton : 'div'" />
</template>
<script setup>
const MyButton = resolveComponent('MyButton')
</script>
```
Alternatively, though not recommended, you can register all your components globally, which will create async chunks for all your components and make them available throughout your application.
```diff
import { defineNuxtConfig } from 'nuxt3'
export default defineNuxtConfig({
components: {
+ global: true,
+ dirs: ['~/components']
},
})
```
::alert{type=info}
The `global` option can also be set per component directory.
::
## Dynamic Imports ## Dynamic Imports
To dynamically import a component (also known as lazy-loading a component) all you need to do is add the `Lazy` prefix to the component's name. To dynamically import a component (also known as lazy-loading a component) all you need to do is add the `Lazy` prefix to the component's name.

View File

@ -104,6 +104,7 @@ export const vuePreset = defineUnimportPreset({
// Component // Component
'defineComponent', 'defineComponent',
'defineAsyncComponent', 'defineAsyncComponent',
'resolveComponent',
'getCurrentInstance', 'getCurrentInstance',
'h', 'h',
'inject', 'inject',

View File

@ -2,7 +2,7 @@ import { pathToFileURL } from 'url'
import { createUnplugin } from 'unplugin' import { createUnplugin } from 'unplugin'
import { parseQuery, parseURL } from 'ufo' import { parseQuery, parseURL } from 'ufo'
import { Component } from '@nuxt/schema' import { Component } from '@nuxt/schema'
import { genImport } from 'knitwork' import { genDynamicImport, genImport } from 'knitwork'
import MagicString from 'magic-string' import MagicString from 'magic-string'
import { pascalCase } from 'scule' import { pascalCase } from 'scule'
@ -37,13 +37,19 @@ function transform (code: string, id: string, components: Component[]) {
const s = new MagicString(code) const s = new MagicString(code)
// replace `_resolveComponent("...")` to direct import // replace `_resolveComponent("...")` to direct import
s.replace(/ _resolveComponent\("(.*?)"\)/g, (full, name) => { s.replace(/(?<=[ (])_?resolveComponent\(["'](lazy-|Lazy)?([^'"]*?)["']\)/g, (full, lazy, name) => {
const component = findComponent(components, name) const component = findComponent(components, name)
if (component) { if (component) {
const identifier = map.get(component) || `__nuxt_component_${num++}` const identifier = map.get(component) || `__nuxt_component_${num++}`
map.set(component, identifier) map.set(component, identifier)
imports.add(genImport(component.filePath, [{ name: component.export, as: identifier }])) if (lazy) {
return ` ${identifier}` // Nuxt will auto-import `defineAsyncComponent` for us
imports.add(`const ${identifier}_lazy = defineAsyncComponent(${genDynamicImport(component.filePath)})`)
return `${identifier}_lazy`
} else {
imports.add(genImport(component.filePath, [{ name: component.export, as: identifier }]))
return identifier
}
} }
// no matched // no matched
return full return full

View File

@ -51,6 +51,7 @@ export const componentsTypeTemplate = {
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
${options.components.map(c => ` '${c.pascalName}': typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(join(options.buildDir, 'types'), c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join(',\n')} ${options.components.map(c => ` '${c.pascalName}': typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(join(options.buildDir, 'types'), c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join(',\n')}
${options.components.map(c => ` 'Lazy${c.pascalName}': typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(join(options.buildDir, 'types'), c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join(',\n')}
} }
} }
export {} export {}

View File

@ -9,6 +9,8 @@ export default {
* @see [Nuxt 3](https://v3.nuxtjs.org/docs/directory-structure/components) and * @see [Nuxt 3](https://v3.nuxtjs.org/docs/directory-structure/components) and
* [Nuxt 2](https://nuxtjs.org/docs/directory-structure/components/) documentation * [Nuxt 2](https://nuxtjs.org/docs/directory-structure/components/) documentation
* @type {boolean | typeof import('../src/types/components').ComponentsOptions | typeof import('../src/types/components').ComponentsOptions['dirs']} * @type {boolean | typeof import('../src/types/components').ComponentsOptions | typeof import('../src/types/components').ComponentsOptions['dirs']}
* @version 2
* @version 3
*/ */
components: { components: {
$resolve: (val, get) => { $resolve: (val, get) => {
@ -27,6 +29,7 @@ export default {
* *
* @see [Nuxt 3 documentation](https://v3.nuxtjs.org/docs/directory-structure/composables) * @see [Nuxt 3 documentation](https://v3.nuxtjs.org/docs/directory-structure/composables)
* @type {typeof import('../src/types/imports').AutoImportsOptions} * @type {typeof import('../src/types/imports').AutoImportsOptions}
* @version 3
*/ */
autoImports: { autoImports: {
global: false, global: false,