mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-16 21:58:19 +00:00
feat(nuxt3): remove <script setup>
transform and prefer top level await (#579)
Co-authored-by: Pooya Parsa <pyapar@gmail.com>
This commit is contained in:
parent
a5f446ca6a
commit
e13baf9867
@ -20,37 +20,14 @@ 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.
|
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
|
### Example
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<script setup nuxt>
|
<script setup>
|
||||||
const { data } = asyncData('time', () => $fetch('/api/count'))
|
const { data } = await asyncData('time', () => $fetch('/api/count'))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
Page visits: {{ data.count }}
|
Page visits: {{ data.count }}
|
||||||
</template>
|
</template>
|
||||||
```
|
```
|
||||||
|
|
||||||
If you don't want to use the `<script setup>` syntax, you need to use the `defineNuxtComponent` method to define your component:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
Page visits: {{ data.count }}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default defineNuxtComponent({
|
|
||||||
setup () {
|
|
||||||
const { data } = asyncData('time', () => $fetch('/api/count'))
|
|
||||||
return { data }
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { defineNuxtConfig } from '@nuxt/kit'
|
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
|
||||||
// vite: true
|
|
||||||
})
|
|
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "example-async-data-setup",
|
|
||||||
"private": true,
|
|
||||||
"devDependencies": {
|
|
||||||
"nuxt3": "latest"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"dev": "nuxt dev",
|
|
||||||
"build": "nuxt build",
|
|
||||||
"start": "node .output/server/index.mjs"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<script nuxt setup lang="ts">
|
|
||||||
// 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>
|
|
@ -1,3 +0,0 @@
|
|||||||
let ctr = 0
|
|
||||||
|
|
||||||
export default () => ({ count: ++ctr })
|
|
@ -1,13 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<nuxt-page />
|
|
||||||
</div>
|
|
||||||
<nuxt-link to="/">
|
|
||||||
Home
|
|
||||||
</nuxt-link>
|
|
||||||
<nuxt-link to="/options">
|
|
||||||
Options
|
|
||||||
</nuxt-link>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
@ -1,4 +1,5 @@
|
|||||||
import { defineNuxtConfig } from '@nuxt/kit'
|
import { defineNuxtConfig } from '@nuxt/kit'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
// vite: true
|
||||||
})
|
})
|
||||||
|
@ -4,14 +4,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script setup>
|
||||||
export default defineNuxtComponent({
|
const { data } = await asyncData('time', () => $fetch('/api/count'))
|
||||||
setup () {
|
|
||||||
const { data } = asyncData('time', () => $fetch('/api/count'))
|
|
||||||
|
|
||||||
return {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
About
|
|
||||||
{{ path }}
|
|
||||||
<button @click="path = 'newpath'">
|
|
||||||
Update path
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default defineNuxtComponent({
|
|
||||||
fetchKey: 'custom',
|
|
||||||
asyncData ({ route }) {
|
|
||||||
return {
|
|
||||||
path: route.path
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup () {
|
|
||||||
// This will get overwritten with asyncData
|
|
||||||
const path = ref('original path')
|
|
||||||
|
|
||||||
return {
|
|
||||||
path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
@ -1,7 +1,6 @@
|
|||||||
import { getCurrentInstance, onBeforeMount, onUnmounted, ref, unref } from 'vue'
|
import { onBeforeMount, onUnmounted, ref, unref } from 'vue'
|
||||||
import type { UnwrapRef, Ref } from 'vue'
|
import type { UnwrapRef, Ref } from 'vue'
|
||||||
|
|
||||||
import { NuxtComponentPendingPromises } from './component'
|
|
||||||
import { ensureReactive, useGlobalData } from './data'
|
import { ensureReactive, useGlobalData } from './data'
|
||||||
import { NuxtApp, useNuxtApp } from '#app'
|
import { NuxtApp, useNuxtApp } from '#app'
|
||||||
|
|
||||||
@ -23,7 +22,6 @@ export type AsyncDataResult<T> = AsyncDataState<T> & Promise<AsyncDataState<T>>
|
|||||||
|
|
||||||
export function useAsyncData (defaults?: AsyncDataOptions) {
|
export function useAsyncData (defaults?: AsyncDataOptions) {
|
||||||
const nuxt = useNuxtApp()
|
const nuxt = useNuxtApp()
|
||||||
const vm = getCurrentInstance()
|
|
||||||
const onBeforeMountCbs: Array<() => void> = []
|
const onBeforeMountCbs: Array<() => void> = []
|
||||||
|
|
||||||
if (process.client) {
|
if (process.client) {
|
||||||
@ -66,6 +64,7 @@ export function useAsyncData (defaults?: AsyncDataOptions) {
|
|||||||
state.pending.value = true
|
state.pending.value = true
|
||||||
nuxt._asyncDataPromises[key] = Promise.resolve(handler(nuxt)).then((result) => {
|
nuxt._asyncDataPromises[key] = Promise.resolve(handler(nuxt)).then((result) => {
|
||||||
for (const _key in result) {
|
for (const _key in result) {
|
||||||
|
// @ts-expect-error
|
||||||
state.data[_key] = unref(result[_key])
|
state.data[_key] = unref(result[_key])
|
||||||
}
|
}
|
||||||
return state.data
|
return state.data
|
||||||
@ -105,11 +104,6 @@ export function useAsyncData (defaults?: AsyncDataOptions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto enqueue if within nuxt component instance
|
|
||||||
if (nuxt._asyncDataPromises[key] && vm[NuxtComponentPendingPromises]) {
|
|
||||||
vm[NuxtComponentPendingPromises].push(nuxt._asyncDataPromises[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = Promise.resolve(nuxt._asyncDataPromises[key]).then(() => state) as AsyncDataResult<T>
|
const res = Promise.resolve(nuxt._asyncDataPromises[key]).then(() => state) as AsyncDataResult<T>
|
||||||
res.data = state.data
|
res.data = state.data
|
||||||
res.pending = state.pending
|
res.pending = state.pending
|
||||||
|
@ -1,37 +1,17 @@
|
|||||||
import { toRefs } from '@vue/reactivity'
|
import { toRefs } from '@vue/reactivity'
|
||||||
import { defineComponent, getCurrentInstance } from 'vue'
|
import { defineComponent, getCurrentInstance } from 'vue'
|
||||||
import type { ComponentInternalInstance, DefineComponent } from 'vue'
|
import type { DefineComponent } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import type { LegacyContext } from '../legacy'
|
import type { LegacyContext } from '../legacy'
|
||||||
import { useNuxtApp } from '../nuxt'
|
import { useNuxtApp } from '../nuxt'
|
||||||
import { asyncData } from './asyncData'
|
import { asyncData } from './asyncData'
|
||||||
|
|
||||||
export const NuxtComponentIndicator = '__nuxt_component'
|
export const NuxtComponentIndicator = '__nuxt_component'
|
||||||
export const NuxtComponentPendingPromises = '_pendingPromises'
|
|
||||||
|
|
||||||
export interface NuxtComponentInternalInstance extends ComponentInternalInstance {
|
|
||||||
[NuxtComponentPendingPromises]: Array<Promise<void>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCurrentNuxtComponentInstance (): NuxtComponentInternalInstance {
|
|
||||||
const vm = getCurrentInstance() as NuxtComponentInternalInstance
|
|
||||||
|
|
||||||
if (!vm || !vm.proxy.$options[NuxtComponentIndicator]) {
|
|
||||||
throw new Error('This method can only be used within a component defined with `defineNuxtComponent()`.')
|
|
||||||
}
|
|
||||||
|
|
||||||
return vm
|
|
||||||
}
|
|
||||||
|
|
||||||
export function enqueueNuxtComponent (p: Promise<void>) {
|
|
||||||
const vm = getCurrentNuxtComponentInstance()
|
|
||||||
vm[NuxtComponentPendingPromises].push(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<string, any>>, fn: (context: LegacyContext) => Promise<Record<string, any>>) {
|
async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<string, any>>, fn: (context: LegacyContext) => Promise<Record<string, any>>) {
|
||||||
const nuxt = useNuxtApp()
|
const nuxt = useNuxtApp()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const vm = getCurrentNuxtComponentInstance()
|
const vm = getCurrentInstance()
|
||||||
const { fetchKey } = vm.proxy.$options
|
const { fetchKey } = vm.proxy.$options
|
||||||
const key = typeof fetchKey === 'function' ? fetchKey(() => '') : fetchKey || route.fullPath
|
const key = typeof fetchKey === 'function' ? fetchKey(() => '') : fetchKey || route.fullPath
|
||||||
const { data } = await asyncData(`options:asyncdata:${key}`, () => fn(nuxt._legacyContext))
|
const { data } = await asyncData(`options:asyncdata:${key}`, () => fn(nuxt._legacyContext))
|
||||||
@ -42,6 +22,7 @@ export const defineNuxtComponent: typeof defineComponent =
|
|||||||
function defineNuxtComponent (options: any): any {
|
function defineNuxtComponent (options: any): any {
|
||||||
const { setup } = options
|
const { setup } = options
|
||||||
|
|
||||||
|
// Avoid wrapping if no options api is used
|
||||||
if (!setup && !options.asyncData) {
|
if (!setup && !options.asyncData) {
|
||||||
return {
|
return {
|
||||||
[NuxtComponentIndicator]: true,
|
[NuxtComponentIndicator]: true,
|
||||||
@ -53,26 +34,20 @@ export const defineNuxtComponent: typeof defineComponent =
|
|||||||
[NuxtComponentIndicator]: true,
|
[NuxtComponentIndicator]: true,
|
||||||
...options,
|
...options,
|
||||||
setup (props, ctx) {
|
setup (props, ctx) {
|
||||||
const vm = getCurrentNuxtComponentInstance()
|
|
||||||
let promises = vm[NuxtComponentPendingPromises] = vm[NuxtComponentPendingPromises] || []
|
|
||||||
|
|
||||||
const res = setup?.(props, ctx) || {}
|
const res = setup?.(props, ctx) || {}
|
||||||
|
|
||||||
|
let promises: unknown[] | undefined = []
|
||||||
|
promises = promises || []
|
||||||
if (options.asyncData) {
|
if (options.asyncData) {
|
||||||
promises.push(runLegacyAsyncData(res, options.asyncData))
|
promises.push(runLegacyAsyncData(res, options.asyncData))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!promises.length && !(res instanceof Promise)) {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve(res)
|
return Promise.resolve(res)
|
||||||
.then(() => Promise.all(promises))
|
.then(() => Promise.all(promises))
|
||||||
.then(() => res)
|
.then(() => res)
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
promises.length = 0
|
promises.length = 0
|
||||||
promises = null
|
promises = null
|
||||||
delete vm[NuxtComponentPendingPromises]
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} as DefineComponent
|
} as DefineComponent
|
||||||
|
@ -6,7 +6,6 @@ import vitePlugin from '@vitejs/plugin-vue'
|
|||||||
|
|
||||||
import { cacheDirPlugin } from './plugins/cache-dir'
|
import { cacheDirPlugin } from './plugins/cache-dir'
|
||||||
import { replace } from './plugins/replace'
|
import { replace } from './plugins/replace'
|
||||||
import { transformNuxtSetup } from './plugins/transformSetup'
|
|
||||||
import { wpfs } from './utils/wpfs'
|
import { wpfs } from './utils/wpfs'
|
||||||
import type { ViteBuildContext, ViteOptions } from './vite'
|
import type { ViteBuildContext, ViteOptions } from './vite'
|
||||||
|
|
||||||
@ -37,8 +36,7 @@ export async function buildClient (ctx: ViteBuildContext) {
|
|||||||
plugins: [
|
plugins: [
|
||||||
replace({ 'process.env': 'import.meta.env' }),
|
replace({ 'process.env': 'import.meta.env' }),
|
||||||
cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'),
|
cacheDirPlugin(ctx.nuxt.options.rootDir, 'client'),
|
||||||
vitePlugin(ctx.config.vue),
|
vitePlugin(ctx.config.vue)
|
||||||
transformNuxtSetup()
|
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
middlewareMode: true
|
middlewareMode: true
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
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 "#app"\n')
|
|
||||||
return {
|
|
||||||
code: s.toString(),
|
|
||||||
map: s.generateMap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,7 +5,6 @@ import consola from 'consola'
|
|||||||
import { ViteBuildContext, ViteOptions } from './vite'
|
import { ViteBuildContext, ViteOptions } from './vite'
|
||||||
import { wpfs } from './utils/wpfs'
|
import { wpfs } from './utils/wpfs'
|
||||||
import { cacheDirPlugin } from './plugins/cache-dir'
|
import { cacheDirPlugin } from './plugins/cache-dir'
|
||||||
import { transformNuxtSetup } from './plugins/transformSetup'
|
|
||||||
|
|
||||||
export async function buildServer (ctx: ViteBuildContext) {
|
export async function buildServer (ctx: ViteBuildContext) {
|
||||||
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
|
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
|
||||||
@ -50,8 +49,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
|
cacheDirPlugin(ctx.nuxt.options.rootDir, 'server'),
|
||||||
vuePlugin(),
|
vuePlugin()
|
||||||
transformNuxtSetup()
|
|
||||||
]
|
]
|
||||||
} as ViteOptions)
|
} as ViteOptions)
|
||||||
|
|
||||||
|
@ -3,8 +3,7 @@ import { defineBuildConfig } from 'unbuild'
|
|||||||
export default defineBuildConfig({
|
export default defineBuildConfig({
|
||||||
declaration: true,
|
declaration: true,
|
||||||
entries: [
|
entries: [
|
||||||
'src/index',
|
'src/index'
|
||||||
'src/loaders/nuxt-setup-loader'
|
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
'@nuxt/kit',
|
'@nuxt/kit',
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
const DEFINE_COMPONENT_VUE = '_defineComponent('
|
|
||||||
const DEFINE_COMPONENT_NUXT = '_defineNuxtComponent('
|
|
||||||
|
|
||||||
export default function NuxtSetupLoader (code: string, map: any) {
|
|
||||||
if (code && code.includes(DEFINE_COMPONENT_VUE)) {
|
|
||||||
code = 'import { defineNuxtComponent as _defineNuxtComponent } from "#app";' + code.replace(DEFINE_COMPONENT_VUE, DEFINE_COMPONENT_NUXT)
|
|
||||||
}
|
|
||||||
this.callback(null, code, map)
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import { normalize } from 'pathe'
|
|
||||||
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: normalize(require.resolve('@nuxt/webpack-builder/dist/nuxt-setup-loader.cjs'))
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,6 @@
|
|||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import VueLoaderPlugin from 'vue-loader/dist/pluginWebpack5'
|
import VueLoaderPlugin from 'vue-loader/dist/pluginWebpack5'
|
||||||
import { DefinePlugin } from 'webpack'
|
import { DefinePlugin } from 'webpack'
|
||||||
import NuxtSetupTransformerPlugin from '../plugins/transform-setup'
|
|
||||||
import VueSSRClientPlugin from '../plugins/vue/client'
|
import VueSSRClientPlugin from '../plugins/vue/client'
|
||||||
import VueSSRServerPlugin from '../plugins/vue/server'
|
import VueSSRServerPlugin from '../plugins/vue/server'
|
||||||
import { WebpackConfigContext } from '../utils/config'
|
import { WebpackConfigContext } from '../utils/config'
|
||||||
@ -27,8 +26,6 @@ export function vue (ctx: WebpackConfigContext) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
config.plugins.push(new NuxtSetupTransformerPlugin())
|
|
||||||
|
|
||||||
// Feature flags
|
// Feature flags
|
||||||
// https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags
|
// https://github.com/vuejs/vue-next/tree/master/packages/vue#bundler-build-feature-flags
|
||||||
// TODO: Provide options to toggle
|
// TODO: Provide options to toggle
|
||||||
|
@ -6717,14 +6717,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"example-async-data@workspace:examples/async-data":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "example-async-data@workspace:examples/async-data"
|
resolution: "example-async-data@workspace:examples/async-data"
|
||||||
|
Loading…
Reference in New Issue
Block a user