feat(nuxt3): allow explicitly importing components (#4150)

This commit is contained in:
Daniel Roe 2022-04-07 12:03:37 +01:00 committed by GitHub
parent 114ec8982d
commit 368610e8a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 10 deletions

View File

@ -116,6 +116,26 @@ export default {
</script> </script>
``` ```
## Direct imports
You can also explicitly import components from `#components` if you want or need to bypass Nuxt's auto-importing functionality.
```html{}[pages/index.vue]
<template>
<div>
<h1>Mountains</h1>
<LazyMountainsList v-if="show" />
<button v-if="!show" @click="show = true">Show List</button>
<NuxtLink to="/">Home</NuxtLink>
</div>
</template>
<script setup>
import { NuxtLink, LazyMountainsList } from '#components'
const show = ref(false)
</script>
```
## `<ClientOnly>` Component ## `<ClientOnly>` Component
Nuxt provides the `<ClientOnly>` component for purposely rendering a component only on client side. To import a component only on the client, register the component in a client-side only plugin. Nuxt provides the `<ClientOnly>` component for purposely rendering a component only on client side. To import a component only on the client, register the component in a client-side only plugin.

View File

@ -2,7 +2,7 @@ import { statSync } from 'fs'
import { resolve, basename } from 'pathe' import { resolve, basename } from 'pathe'
import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin, addTemplate, addPluginTemplate } from '@nuxt/kit' import { defineNuxtModule, resolveAlias, addVitePlugin, addWebpackPlugin, addTemplate, addPluginTemplate } from '@nuxt/kit'
import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/schema' import type { Component, ComponentsDir, ComponentsOptions } from '@nuxt/schema'
import { componentsTemplate, componentsTypeTemplate } from './templates' import { componentsPluginTemplate, componentsTemplate, componentsTypeTemplate } from './templates'
import { scanComponents } from './scan' import { scanComponents } from './scan'
import { loaderPlugin } from './loader' import { loaderPlugin } from './loader'
@ -97,6 +97,12 @@ export default defineNuxtModule<ComponentsOptions>({
}) })
addPluginTemplate({ addPluginTemplate({
...componentsPluginTemplate,
options
})
nuxt.options.alias['#components'] = resolve(nuxt.options.buildDir, componentsTemplate.filename)
addTemplate({
...componentsTemplate, ...componentsTemplate,
options options
}) })
@ -108,7 +114,7 @@ export default defineNuxtModule<ComponentsOptions>({
}) })
nuxt.hook('prepare:types', ({ references }) => { nuxt.hook('prepare:types', ({ references }) => {
references.push({ path: resolve(nuxt.options.buildDir, 'types/components.d.ts') }) references.push({ path: resolve(nuxt.options.buildDir, 'components.d.ts') })
}) })
// Watch for changes // Watch for changes

View File

@ -1,7 +1,7 @@
import { isAbsolute, join, relative } from 'pathe' import { isAbsolute, relative } from 'pathe'
import type { Component } from '@nuxt/schema' import type { Component } from '@nuxt/schema'
import { genDynamicImport, genObjectFromRawEntries } from 'knitwork' import { genDynamicImport, genExport, genObjectFromRawEntries } from 'knitwork'
export type ComponentsTemplateOptions = { export type ComponentsTemplateOptions = {
buildDir?: string buildDir?: string
@ -23,8 +23,8 @@ const createImportMagicComments = (options: ImportMagicCommentsOptions) => {
].filter(Boolean).join(', ') ].filter(Boolean).join(', ')
} }
export const componentsTemplate = { export const componentsPluginTemplate = {
filename: 'components.mjs', filename: 'components.plugin.mjs',
getContents ({ options }: { options: ComponentsTemplateOptions }) { getContents ({ options }: { options: ComponentsTemplateOptions }) {
return `import { defineAsyncComponent } from 'vue' return `import { defineAsyncComponent } from 'vue'
@ -45,15 +45,36 @@ export default function (nuxtApp) {
} }
} }
export const componentsTemplate = {
filename: 'components.mjs',
getContents ({ options }: { options: ComponentsTemplateOptions }) {
return [
'import { defineAsyncComponent } from \'vue\'',
...options.components.flatMap((c) => {
const exp = c.export === 'default' ? 'c.default || c' : `c['${c.export}']`
const comment = createImportMagicComments(c)
return [
genExport(c.filePath, [{ name: c.export, as: c.pascalName }]),
`export const Lazy${c.pascalName} = defineAsyncComponent(${genDynamicImport(c.filePath, { comment })}.then(c => ${exp}))`
]
}),
`export const componentNames = ${JSON.stringify(options.components.map(c => c.pascalName))}`
].join('\n')
}
}
export const componentsTypeTemplate = { export const componentsTypeTemplate = {
filename: 'types/components.d.ts', filename: 'components.d.ts',
getContents: ({ options }: { options: ComponentsTemplateOptions }) => `// Generated by components discovery getContents: ({ options }: { options: ComponentsTemplateOptions }) => `// Generated by components discovery
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(options.buildDir, 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')} ${options.components.map(c => ` 'Lazy${c.pascalName}': typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(options.buildDir, c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join(',\n')}
} }
} }
export {} ${options.components.map(c => `export const ${c.pascalName}: typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(options.buildDir, c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join('\n')}
${options.components.map(c => `export const Lazy${c.pascalName}: typeof ${genDynamicImport(isAbsolute(c.filePath) ? relative(options.buildDir, c.filePath) : c.filePath, { wrapper: false })}['${c.export}']`).join('\n')}
export const componentNames: string[]
` `
} }