Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

比较 SSR 框架 Next.js 和 Nuxt.js 的语法 #288

Open
yanyue404 opened this issue Nov 23, 2024 · 0 comments
Open

比较 SSR 框架 Next.js 和 Nuxt.js 的语法 #288

yanyue404 opened this issue Nov 23, 2024 · 0 comments

Comments

@yanyue404
Copy link
Owner

yanyue404 commented Nov 23, 2024

SSR: Next.js & Nuxt.js,Next.js > v13.0.0,Nuxt.js 2 & 3

Basic-Routes

Next.js

<!-- (.js .jsx .tsx)  -->
|- pages/
  |- index.js      → href="/"
  |- blog/index.js   → href="/blog"

Nuxt.js

|- pages/
  |- index.vue       → href="/"
  |- blog/index.vue  → href="/blog"

Dynamic-Routes

Next.js

|- pages/
  |- blog/[slug].js           → href="/blog/:slug" (eg. /blog/hello-world)
  |- [username]/[option].js   → href="/:username/:option" (eg. /foo/settings)
  |- post/[...all].js         → href="/post/*" (eg. /post/2020/id/title)

Nuxt.js

|- pages/
  |- blog/[slug].vue         → href="/blog/:slug" (eg. /blog/hello-world)
  |- _username/_option.vue   → href="/:username/:option" (eg. /foo/settings)

Link

Next.js

import Link from "next/link"; // https://nextjs.org/docs/app/api-reference/components/link

function Home() {
  return (
    <Link href="/">
      <a>Home</a>
    </Link>
  );
}

Nuxt.js

<template>
  <nuxt-link to="/">Home page</nuxt-link>
</template>

Layout

Next.js

./pages/_app.js: automatically apply to all pages

export default function MyApp({ Component, pageProps }) {
  return (
    <React.Fragment>
      <MyHeader />
      <Component {...pageProps} />
      <MyFooter />
    </React.Fragment>
  );
}

Nuxt.js

v2

layouts/with-header-footer.vue: create layout

<template>
  <div>
    <MyHeader />
    <nuxt />
    <MyFooter />
  </div>
</template>

pages/index.vue: apply layout

<template>
  <!-- Your template -->
</template>
<script>
  export default {
    layout: "with-header-footer",
  };
</script>

v3

layout/default.vue

<template>
  <div>
    <slot />
  </div>
</template>

~/app.vue

<template>
  <NuxtLayout>
    <NuxtPage></NuxtPage>
  </NuxtLayout>
</template>

Error-Page

Next.js

app/global-error.tsx 意外运行错误 UI

"use client"; // Error boundaries must be Client Components

export default function GlobalError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
  return (
    // global-error must include html and body tags
    <html>
      <body>
        <h2>Something went wrong!</h2>
        <button onClick={() => reset()}>Try again</button>
      </body>
    </html>
  );
}

app/not-found.tsx 路由未找到 UI

import Link from "next/link";

export default function NotFound() {
  return (
    <div>
      <h2>Not Found</h2>
      <p>Could not find requested resource</p>
      <Link href="/">Return Home</Link>
    </div>
  );
}

Nuxt.js

v2

layouts/error.vue

<template>
  <div class="container">
    <h1 v-if="error.statusCode === 404">Page not found</h1>
    <h1 v-else>An error occurred</h1>
    <nuxt-link to="/">Home page</nuxt-link>
  </div>
</template>

<script>
  export default {
    props: ["error"],
    layout: "blog", // you can set a custom layout for the error page
  };
</script>

v3

通过在应用程序的源目录中添加 ~/error.vue(与 app.vue 并排)来自定义默认错误页面。

<template>
  <div>
    <h2>{{ error.statusCode }}</h2>
    <h3>{{ error.message }}</h3>
    <h4 v-html="error.stack"></h4>
    <button @click="handleError">Clear errors</button>
  </div>
</template>
<script setup lang="ts">
  import type { NuxtError } from "#app";

  const props = defineProps({
    error: Object as () => NuxtError,
  });

  const handleError = () => clearError({ redirect: "/" });
</script>

Meta-Tag

Next.js

import Head from "next/head";

function IndexPage() {
  return (
    <div>
      <Head>
        <title>My page title</title>
        <meta name="viewport" content="initial-scale=1.0, width=device-width" />
      </Head>
      <p>Hello world!</p>
    </div>
  );
}

Nuxt.js

v2

  • 全局默认

nuxt.config.js

export default {
  /*
   ** Headers of the page
   */
  head: {
    title: prdConfig.productName,
    meta: [
      {
        name: "description",
        content: prdConfig.description,
      },
      {
        name: "keywords",
        content: prdConfig.keywords,
      },
    ],
  },
};

~/app.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />
    <style></style>
    {{HEAD}} {{ ENV.PATH_TYPE === "production" ? `` : `
    <script defer src="//cdn.com/vconsole.min.js"></script>
    `}}
  </head>
  <body>
    {{APP}}
  </body>
</html>
  • 页面维度
<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    data() {
      return {
        title: "Hello World!",
      };
    },
    head() {
      return {
        title: this.title,
        meta: [
          // To avoid duplicated meta tags when used in child component, set up an unique identifier with the hid key
          {
            hid: "description",
            name: "description",
            content: "My custom description",
          },
        ],
      };
    },
  };
</script>

v3

全局默认

export default defineNuxtConfig({
  app: {
    head: {
      meta: [
        { charset: "utf-8" },
        {
          name: "viewport",
          content:
            "width=device-width,height=device-height,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover",
        },
      ],
      link: [{ rel: "icon", type: "image/x-icon", href: "favicon.ico" }],
      style: [],
      script: scripts,
      title: "主标题",
    },
  },
});

页面维度

可以使用 useHead 或 useSeoMeta 钩子 自定义:

<script setup lang="ts">
  useHead({
    title: "My App",
    meta: [{ name: "description", content: "My amazing site." }],
    bodyAttrs: {
      class: "test",
    },
    script: [{ innerHTML: "console.log('Hello world')" }],
  });

  useSeoMeta({
    title: "My Amazing Site",
    ogTitle: "My Amazing Site",
    description: "This is my amazing site, let me tell you all about it.",
    ogDescription: "This is my amazing site, let me tell you all about it.",
    ogImage: "https://example.com/image.png",
    twitterCard: "summary_large_image",
  });
</script>

Assets

Next.js

public

/*
|- public/
|-- my-image.png
*/
import Image from "next/image"; // https://nextjs.org/docs/app/api-reference/components/image

export default function myImage() {
  return (
    <Image
      src="/my-image.png"
      alt="Picture of the author"
      // width={500} automatically provided
      // height={500} automatically provided
      // blurDataURL="data:..." automatically provided
      // placeholder="blur" // Optional blur-up while loading
    />
  );
}

assets

/*
|- assets/
|-- images/
|-----open-source.png
*/
import Image from "next/image"; // https://nextjs.org/docs/app/api-reference/components/image
import openSource from "/assets/images/open-source.png";

export default function myImage() {
  return (
    <Image
      src={openSource}
      alt="Picture of the author"
      // width={500} automatically provided
      // height={500} automatically provided
      // blurDataURL="data:..." automatically provided
      // placeholder="blur" // Optional blur-up while loading
    />
  );
}

Nuxt.js

assets

By default, Nuxt uses vue-loader, file-loader and url-loader for strong assets serving.

<!--
|- assets/
  |- image.png
-->
<img src="~/assets/image.png" alt="image" />

在 css 文件中,如果需要引用 assets 目录,请使用~assets/your_image.png (不带斜杠)

background: url("~assets/banner.svg");

处理动态图像时,您需要使用 require

<img :src="require(`~/assets/img/${image}.jpg`)" />

static

automatically served

<!--
|- static/
  |- image.png
-->
<img src="/image.png" alt="image" />

CSS

Next.js

全局样式

在  pages/_app.js  文件中导入(import)CSS 文件。

/* styles.css */
body {
  font-family: "SF Pro Text", "SF Pro Icons", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
  padding: 20px 20px 60px;
  max-width: 680px;
  margin: 0 auto;
}

首先创建一个  pages/_app.js  文件(如果不存在的话)。 然后  import 该  styles.css  文件。

import "../styles.css";

// 新创建的 `pages/_app.js` 文件中必须有此默认的导出(export)函数
export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

这些样式 (styles.css) 将应用于你的应用程序中的所有页面和组件。 由于样式表的全局特性,并且为了避免冲突,你应该 只在  pages/_app.js文件中导入(import)样式表

从 node_modules 目录导入(import)样式

对于全局样式表(例如 bootstrap 或 nprogress),你应该在 pages/\_app.js 文件中对齐进行导入(import)。

对于导入第三方组件所需的 CSS,可以在组件中进行。

// components/ExampleDialog.js
import { useState } from "react";
import { Dialog } from "@reach/dialog";
import VisuallyHidden from "@reach/visually-hidden";
import "@reach/dialog/styles.css";

function ExampleDialog(props) {
  const [showDialog, setShowDialog] = useState(false);
  const open = () => setShowDialog(true);
  const close = () => setShowDialog(false);

  return (
    <div>
      <button onClick={open}>Open Dialog</button>
      <Dialog isOpen={showDialog} onDismiss={close}>
        <button className="close-button" onClick={close}>
          <VisuallyHidden>Close</VisuallyHidden>
          <span aria-hidden>×</span>
        </button>
        <p>Hello there. I am a dialog</p>
      </Dialog>
    </div>
  );
}

组件级 CSS

Next.js 通过 [name].module.css 文件命名约定来支持 CSS 模块 。

CSS 模块通过自动创建唯一的类名从而将 CSS 限定在局部范围内。 这使您可以在不同文件中使用相同的 CSS 类名,而不必担心冲突。

此行为使 CSS 模块成为包含组件级 CSS 的理想方法。 CSS 模块文件 可以导入(import)到应用程序中的任何位置。

例如,假设 components/ 目录下有一个可重用 Button 组件:

首先,创建 components/Button.module.css 文件并填入以下内容:

/* 您不必担心 .error {} 与任何其他 `.css` 或 `.module.css` 文件发生冲突! */
.error {
  color: white;
  background-color: red;
}

然后,创建 components/Button.js 文件,导入(import)并使用上述 CSS 文件:

import styles from "./Button.module.css";

export function Button() {
  return (
    <button
      type="button"
      // 请注意如何将 “error”类作为导入的`styles`对象的属性进行访问。
      className={styles.error}
    >
      Destroy
    </button>
  );
}

CSS 模块是一项 可选功能,仅对带有 .module.css 扩展名的文件启用

对 Sass 的支持: Next.js 允许你导入(import).module.scss.module.sass 扩展名来使用 Sass。

Nuxt.js

v2

全局设置 CSS 文件/模块/库(包含在每个页面中)

export default {
  css: [
    // Load a Node.js module directly (here it's a Sass file)
    "bulma",
    // CSS file in the project
    "~/assets/css/main.css",
    // SCSS file in the project
    "~/assets/css/main.scss",
  ],
};

v3

全局样式

<!-- pages/index.vue-->
<script>
// Use a static import for server-side compatibility
import "~/assets/css/first.css";

// Caution: Dynamic imports are not server-side compatible
import("~/assets/css/first.css");
</script>
<style lang="scss">
@use "~/assets/scss/main.scss";
</style>
  • 使用预处理器
export default defineNuxtConfig({
  css: ["~/assets/scss/main.scss"],
});
  • 在预处理的文件中注入代码
export default defineNuxtConfig({
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: '@use "~/assets/_colors.scss" as *;',
        },
      },
    },
  },
});

组件级别 CSS

<template>
  <div class="example">hi</div>
</template>

<style lang="scss" scoped>
  /* 单独设置组件的样式 */
  .example {
    color: red;
  }
</style>
<style lang="scss">
  /* 全局 的 css */
  .dialog {
    color: red;
  }
</style>

Fetch-On-Server

Next.js

getInitialProps can only be used in the default export of every page

>= v9.3

SWR

推荐的客户端请求 React Hook:

import useSWR from "swr";

const fetcher = (url) => fetch(url).then((res) => res.json());

function Profile() {
  const { data, error } = useSWR("/api/user", fetcher);

  if (error) return <div>failed to load</div>;
  if (!data) return <div>loading...</div>;
  return <div>hello {data.name}!</div>;
}

getStaticProps

Next.js 将在构建时使用 getStaticProps 返回的 props 预渲染此页面 (SSG 静态渲染)

// posts will be populated at build time by getStaticProps()
function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li>{post.title}</li>
      ))}
    </ul>
  );
}

// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries. See the "Technical details" section.
export async function getStaticProps() {
  // Call an external API endpoint to get posts.
  // You can use any data fetching library
  const res = await fetch("https://.../posts");
  const posts = await res.json();

  // By returning { props: { posts } }, the Blog component
  // will receive `posts` as a prop at build time
  return {
    props: {
      posts,
    },
  };
}

export default Blog;

getServerSideProps

Next.js 将使用 getServerSideProps 返回的数据在每个请求上预渲染此页面。(SSR 服务端渲染)

function Page({ data }) {
  // Render data...
}

export async function getServerSideProps() {
  const res = await fetch(`https://.../data`);
  const data = await res.json();

  return { props: { data } };
}

export default Page;

Nuxt.js

v2

<template>
  <div v-if="$fetchState.error">Something went wrong 😭</div>
  <div v-if="$fetchState.pending">Loading...</div>
  <div v-else>
    <h1>{{ post.title }}</h1>
    <pre>{{ post.body }}</pre>
    <button @click="$fetch">Refresh</button>
  </div>
</template>

<script>
  import fetch from "node-fetch";

  export default {
    data() {
      return {
        post: {},
      };
    },
    async fetch() {
      this.post = await this.$http.$get("xxx");
    },
    fetchOnServer: true,
  };
</script>

v3

客户端请求

$fetch 用户纯客户端渲染, 在 SSR 期间,数据会被获取两次,一次在服务器上,一次在客户端上。

<script setup lang="ts">
function contactForm() {
  $fetch("/api/contact", {
    method: "POST",
    body: { hello: "world " },
  });
}
</script>

<template>
  <button @click="contactForm">Contact</button>
</template>

服务端渲染

useFetchuseAsyncData 组合函数用于服务端和客户端同构的环境。

<script setup lang="ts">
  const { data } = await useFetch("/api/data");

  async function handleFormSubmit() {
    const res = await $fetch("/api/submit", {
      method: "POST",
      body: {
        // My form data
      },
    });
  }
</script>

<template>
  <div v-if="data == null">No data</div>
  <div v-else>
    <form @submit="handleFormSubmit">
      <!-- form input tags -->
    </form>
  </div>
</template>

State-Management

Next.js

TODO:

Nuxt.js

v2

Nuxt2 使用 fetch 方法获取数据,存储在 store 中。

store/website.js

export const state = () => ({
  config: {},
});

export const getters = {};

export const mutations = {};

export const actions = {
  async queryWebsiteData({ state, commit, rootState }, params) {
    try {
      const res = await import(`@/assets/json/website.json}`);
      state.config = res;
    } catch (err) {
      console.log(err);
    }
  },
};

pages/index.vue

export default {
  name: "Indexhome",
  data() {
    return {};
  },
  fetch({ app }) {
    return app.store.dispatch("website/queryWebsiteData", "server");
  },
};

v3

https://nuxtjs.org.cn/docs/getting-started/state-management

Nuxt 提供强大的状态管理库和 useState 组合式 API,用于创建响应式且支持服务器端渲染的共享状态。

useState 是一个支持服务器端渲染的 ref 替代方案。它的值将在服务器端渲染后(客户端水合期间)保留,并通过唯一的键在所有组件之间共享。

<!-- 大多数情况下,您希望使用异步解析的数据初始化状态。您可以使用带有 callOnce 工具函数的 app.vue 组件来实现。 -->
<script setup lang="ts">
  const websiteConfig = useState("config");

  await callOnce(async () => {
    websiteConfig.value = await $fetch("https://my-cms.com/api/website-config");
  });
</script>
  • 与 Pinia 结合使用

我们利用 Pinia 模块 创建全局存储并在整个应用程序中使用它。

stores/website.ts

export const useWebsiteStore = defineStore("websiteStore", {
  state: () => ({
    name: "",
    description: "",
  }),
  actions: {
    async fetch() {
      const infos = await $fetch("https://api.nuxt.com/modules/pinia");

      this.name = infos.name;
      this.description = infos.description;
    },
  },
});

app.vue

<script setup lang="ts">
  const website = useWebsiteStore();

  await callOnce(website.fetch);
</script>

<template>
  <main>
    <h1>{{ website.name }}</h1>
    <p>{{ website.description }}</p>
  </main>
</template>

Context

Next.js

如果你导出从页面 async 调用的函数 getStaticProps,Next.js 将在构建时使用返回的 props 预渲染此页面 getStaticProps。

export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  };
}

该 context 参数是一个包含以下键的对象:

  • params 包含使用动态路由的页面的路由参数。例如,如果页面名称为[id].js,则将 params 看起来像{ id: ... }。一般与 getStaticPaths 一起使用 。
  • preview 是 true 页面是否处于预览模式,undefined。
  • previewData 包含 设置的预览数据 setPreviewData。
  • locale 包含活动语言环境(如果您已启用国际化路由)。
  • locales 包含所有支持的语言环境(如果您已启用国际化路由)。
  • defaultLocale 包含配置的默认语言环境(如果您已启用国际化路由)。

getStaticProps 应返回一个具有以下内容的对象:

  • props- 一个可选对象,其中包含页面组件将接收的 props。它应该是一个可序列化的对象
  • revalidate-可选的秒数,在此秒数后可以重新生成页面。默认为 false。revalidate 这 false 意味着没有重新验证,因此页面将缓存为已构建,直到您下次构建。
  • notFound-可选布尔值,允许页面返回 404 状态和页面。
function Page({ data }) {
  // Render data...
}

export async function getStaticProps(context) {
  const res = await fetch(`https://.../data`);
  const data = await res.json();

  if (!data) {
    return {
      notFound: true,
    };
  }

  return {
    props: { data }, // will be passed to the page component as props
  };
}

getStaticProps 只能从页面导出。您无法从非页面文件导出它。

Nuxt.js

v2

nuxt.config.js

export default {
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [
    {
      src: "~/plugins/plugin/main.js",
      ssr: true,
    },
  ],
};

plugin/main.js

export default ({ app, store }, inject) => {
  // Inject $hello(msg) in Vue, context and store.
  inject("hello", (msg) => console.log(`Hello ${msg}!`));

  // vuex缓存
  !isServer() &&
    createPersistedState({
      key: `vuex_store`,
      storage: window.sessionStorage,
    })(store);
};

默认情况下存在的所有上下文:

function (context) { // Could be asyncData, nuxtServerInit, ...
  // Always available
  const {
    app,
    store,
    route,
    params,
    query,
    env,
    isDev,
    isHMR,
    redirect,
    error,
    $config
  } = context

  // Only available on the Server-side
  if (process.server) {
    const { req, res, beforeNuxtRender, beforeSerialize } = context
  }

  // Only available on the Client-side
  if (process.client) {
    const { from, nuxtState } = context
  }
}

pages/index.vue

export default {
  mounted() {
    this.$hello("mounted");
    // will console.log 'Hello mounted!'
  },
  asyncData(context) {
    // Universal keys
    const { app, store, route, params, query, env, isDev, isHMR, redirect, error, $hello } = context;
    // Server-side
    if (process.server) {
      const { req, res, beforeNuxtRender } = context;
    }
    // Client-side
    if (process.client) {
      const { from, nuxtState } = context;
    }

    $hello("asyncData");
    // If using Nuxt <= 2.12, use 👇
    app.$hello("asyncData");

    return { project: "nuxt" };
  },
};

v3

useNuxtApp 访问 Nuxt 应用程序的共享运行时上下文。

<!--
app.vue -->
<script setup lang="ts">
  const nuxtApp = useNuxtApp();

  nuxtApp.provide("hello", (name) => `Hello ${name}!`);

  console.log(nuxtApp.$hello("name")); // Prints "Hello name!"
</script>
// https://nuxt.com/docs/guide/going-further/internals#the-nuxtapp-interface

const nuxtApp = {
  vueApp, // the global Vue application: https://vuejs.org/api/application.html#application-api

  versions, // an object containing Nuxt and Vue versions

  // These let you call and add runtime NuxtApp hooks
  // https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/nuxt.ts#L18
  hooks,
  hook,
  callHook,

  // Only accessible on server-side
  ssrContext: {
    url,
    req,
    res,
    runtimeConfig,
    noSSR,
  },

  // This will be stringified and passed from server to client
  payload: {
    serverRendered: true,
    data: {},
    state: {}
  }

  provide: (name: string, value: any) => void
}

Source Map

Next.js

// next.config.js
module.exports = {
  productionBrowserSourceMaps: true,
};

Nuxt.js

v2

export default {
  build: {
    extend(config, { isClient }) {
      // 为 客户端打包 进行扩展配置
      if (isClient) {
        // config.devtool = "eval-source-map"; //需要时才放开
        // 非生产环境开启 source-map
        if (process.env.PATH_TYPE !== "production") {
          config.devtool = "source-map";
          Object.assign(config.output, {
            devtoolModuleFilenameTemplate: "yanyue404://[resource-path]",
          });
        }
      }
    },
  },
};

v3

export default defineNuxtConfig({
  // or sourcemap: true
  sourcemap: {
    server: true,
    client: true,
  },
});

Environment Variables

Next.js

https://nextjs.org/docs/app/building-your-application/configuring/environment-variables

env.$(NODE_ENV) 来加载环境变量,NODE_ENV 允许有以下几种值。

.env.development
.env.production
.env.test

自动加载

Js 内置支持将环境变量从. env* 文件加载到 process.env

// app/api/route.js

export async function GET() {
  const db = await myDB.connect({
    host: process.env.DB_HOST,
    username: process.env.DB_USER,
    password: process.env.DB_PASS,
  });
  // ...
}

手动加载

npm install @next/env
// envConfig.ts
import { loadEnvConfig } from "@next/env";

const projectDir = process.cwd();
loadEnvConfig(projectDir);

具体使用:

// orm.config.ts
import "./envConfig.ts";

export default defineConfig({
  dbCredentials: {
    connectionString: process.env.DATABASE_URL!,
  },
});

Nuxt.js

v2

package.json

{
  "scrips": {
    "serve": "cross-env PATH_TYPE=development nuxt",
    "generate": "cross-env PATH_TYPE=production nuxt generate"
  }
}

nuxt.config.js

export default {
  mode: "universal",
  env: {
    PATH_TYPE: process.env.PATH_TYPE,
  },
};

在 页面 js 文件中使用:

const isProduction = process.env.PATH_TYPE === "production";

v3

.env.development
.env.production
.env.test
# 指向要加载的另一个 .env 文件,相对于根目录。
npx nuxi dev --dotenv .env.local

package.json

{
  "scripts": {
    "serve": "nuxt dev --dotenv .env.dev",
    "generate:dev": "nuxt generate --dotenv .env.dev",
    "generate:production": "nuxt generate --dotenv .env.production",
    "build": "nuxt build",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  }
}

在开发模式下更新 .env 时,Nuxt 实例会自动重新启动以将新值应用于 process.env。

const env = import.meta.env;

在您的应用程序代码中,您应该使用 运行时配置 而不是普通环境变量。

要将配置和环境变量公开到应用程序的其余部分,您需要在您的 nuxt.config 文件中使用 runtimeConfig 选项定义运行时配置。

// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // The private keys which are only available within server-side
    apiSecret: "123",
    // Keys within public, will be also exposed to the client-side
    public: {
      apiBase: "/api",
    },
  },
});

当将 apiBase 添加到 runtimeConfig.public 时,Nuxt 会将其添加到每个页面有效负载中。我们可以在服务器端和浏览器端普遍访问 apiBase。

const runtimeConfig = useRuntimeConfig();

console.log(runtimeConfig.apiSecret);
console.log(runtimeConfig.public.apiBase);

Reference

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant