feat(app): make asyncData working with <script setup nuxt> (#220)

This commit is contained in:
Anthony Fu 2021-06-16 20:42:58 +08:00 committed by GitHub
parent 77e489aae3
commit 11a5a3e14f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 128 additions and 4 deletions

View File

@ -4,7 +4,7 @@ Nuxt provides `asyncData` to handle data fetching within you application.
## `asyncData`
Within your pages and components you can use `asyncData` to get access to data that resolves asynchronously. (This helper only works within a component defined with `defineNuxtComponent`).
Within your pages and components you can use `asyncData` to get access to data that resolves asynchronously.
### Usage
@ -20,6 +20,10 @@ asyncData(key: string, fn: () => Object, options?: { defer: boolean, server: boo
Under the hood, `defer: false` uses `<Suspense>` to block the loading of the route before the data has been fetched. Consider using `defer: true` and implementing a loading state instead for a snappier user experience.
This helper only works with:
- a component defined with `defineNuxtComponent`
- `<script setup nuxt>` syntax block
### Example
```vue
@ -39,3 +43,17 @@ export default defineNuxtComponent({
</script>
```
When using with the `<script setup>` syntax, an addition attribute `nuxt` is required
```vue
<script setup nuxt>
import { asyncData } from '@nuxt/app'
const { data } = asyncData('time', () => $fetch('/api/count'))
</script>
<template>
Page visits: {{ data.count }}
</template>
```

View File

@ -0,0 +1,5 @@
import { defineNuxtConfig } from '@nuxt/kit'
export default defineNuxtConfig({
// vite: true
})

View File

@ -0,0 +1,12 @@
{
"name": "example-async-data-setup",
"private": true,
"devDependencies": {
"nuxt3": "latest"
},
"scripts": {
"dev": "nu dev",
"build": "nu build",
"start": "node .output/server"
}
}

View File

@ -0,0 +1,12 @@
<script nuxt setup lang="ts">
import { asyncData } from '@nuxt/app'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { data } = asyncData('time', () => $fetch('/api/count'))
</script>
<template>
<div>
Page visits: {{ data.count }}
</div>
</template>

View File

@ -0,0 +1,3 @@
let ctr = 0
export default () => ({ count: ++ctr })

View File

@ -4,6 +4,7 @@ import vitePlugin from '@vitejs/plugin-vue'
import { cacheDirPlugin } from './plugins/cache-dir'
import { replace } from './plugins/replace'
import { ViteBuildContext, ViteOptions } from './vite'
import { transformNuxtSetup } from './plugins/transformSetup'
export async function buildClient (ctx: ViteBuildContext) {
const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
@ -23,7 +24,8 @@ export async function buildClient (ctx: ViteBuildContext) {
plugins: [
replace({ 'process.env': 'import.meta.env' }),
cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'),
vitePlugin(ctx.config.vue)
vitePlugin(ctx.config.vue),
transformNuxtSetup()
],
server: {
middlewareMode: true

View File

@ -0,0 +1,31 @@
import type { Plugin } from 'vite'
import { getQuery } from 'ufo'
import MagicString from 'magic-string'
const DEFINE_COMPONENT_VUE = '_defineComponent('
const DEFINE_COMPONENT_NUXT = '_defineNuxtComponent('
export function transformNuxtSetup () {
return <Plugin> {
name: 'nuxt:transform-setup',
transform (code, id) {
const query = getQuery(id)
if (!(id.endsWith('.vue') || (query.nuxt && query.setup && query.type === 'script'))) {
return
}
const index = code.indexOf(DEFINE_COMPONENT_VUE)
if (index < 0) {
return
}
const s = new MagicString(code)
s.overwrite(index, index + DEFINE_COMPONENT_VUE.length, DEFINE_COMPONENT_NUXT)
s.prepend('import { defineNuxtComponent as _defineNuxtComponent } from "@nuxt/app"\n')
return {
code: s.toString(),
map: s.generateMap()
}
}
}
}

View File

@ -7,6 +7,7 @@ import consola from 'consola'
import { ViteBuildContext, ViteOptions } from './vite'
import { wpfs } from './utils/wpfs'
import { cacheDirPlugin } from './plugins/cache-dir'
import { transformNuxtSetup } from './plugins/transformSetup'
export async function buildServer (ctx: ViteBuildContext) {
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
@ -41,7 +42,8 @@ export async function buildServer (ctx: ViteBuildContext) {
},
plugins: [
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
vuePlugin()
vuePlugin(),
transformNuxtSetup()
]
} as ViteOptions)

View File

@ -3,7 +3,8 @@ import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
declaration: false,
entries: [
'src/index'
'src/index',
'src/loaders/nuxt-setup'
],
dependencies: [
'@nuxt/kit',

View File

@ -0,0 +1,10 @@
const DEFINE_COMPONENT_VUE = '_defineComponent('
const DEFINE_COMPONENT_NUXT = '_defineNuxtComponent('
export default function NuxtSetupLoader (code: string) {
if (code && code.includes(DEFINE_COMPONENT_VUE)) {
// TODO: Add sourcemap hints
code = 'import { defineNuxtComponent as _defineNuxtComponent } from "@nuxt/app"\n' + code.replace(DEFINE_COMPONENT_VUE, DEFINE_COMPONENT_NUXT)
}
return code
}

View File

@ -0,0 +1,17 @@
import { getQuery } from 'ufo'
export default class NuxtSetupTransformerPlugin {
apply (compiler) {
compiler.options.module.rules.push({
include (id) {
const query = getQuery(id)
return id.endsWith('.vue') || (query.nuxt && query.setup && query.type === 'script')
},
enforce: 'post',
use: [{
ident: 'NuxtSetupTransformerPlugin',
loader: require.resolve('@nuxt/webpack-builder/dist/nuxt-setup')
}]
})
}
}

View File

@ -1,6 +1,7 @@
import { resolve } from 'path'
import VueLoaderPlugin from 'vue-loader/dist/pluginWebpack5'
import { DefinePlugin } from 'webpack'
import NuxtSetupTransformerPlugin from '../plugins/transform-setup'
import VueSSRClientPlugin from '../plugins/vue/client'
import VueSSRServerPlugin from '../plugins/vue/server'
import { WebpackConfigContext } from '../utils/config'
@ -26,6 +27,8 @@ export function vue (ctx: WebpackConfigContext) {
}))
}
config.plugins.push(new NuxtSetupTransformerPlugin())
// Feature flags
// https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags
// TODO: Provide options to toggle

View File

@ -5957,6 +5957,14 @@ __metadata:
languageName: node
linkType: hard
"example-async-data-setup@workspace:examples/async-data-setup":
version: 0.0.0-use.local
resolution: "example-async-data-setup@workspace:examples/async-data-setup"
dependencies:
nuxt3: latest
languageName: unknown
linkType: soft
"example-async-data@workspace:examples/async-data":
version: 0.0.0-use.local
resolution: "example-async-data@workspace:examples/async-data"