iT邦幫忙

2022 iThome 鐵人賽

DAY 18
1
Modern Web

Nuxt 3 學習筆記系列 第 18

[Day 18] Nuxt 3 Runtime Config & App Config

  • 分享至 

  • xImage
  •  

前言

在 Nuxt 3 中提供了兩種可以方式設定環境變數或前端需要使用的共用設定,分別是在 Nuxt 啟動時會在後端載入使用的 Runtime Config 及可以在前端被使用的 App Config 這兩者間的區別,將會在本篇做一些講解。

Runtime Config

在開發網站或部署時,我們總是有一些環境變數需要做設置,dotenv 就是一個很好用的套件,能幫助我們將專案下 .env 檔案載入到 Node.js 的 process.env 之中,尤其在後端伺服器的 API 開發,這些不能公開或敏感的 Key 或設定值,通常不會與整個專案一起進行版本控制,而是針對不同環境與機器,配置於 .env 或環境變數之中。

這個 .env 設定檔內的環境變數,例如,資料庫的帳號密碼、第三方服務的 Token 或 API Key 等,通常只會在伺服器被讀取做使用,也不會洩漏這些設定給使用者知道,我們也稱之為執行時的設定 (Runtime Config)

Nuxt 3 提供了可以設定 Runtime Config 的方式,我們可以很方便的來設定這些環境變數給予伺服器執行時使用。

配置 runtimeConfig

我們可以在 nuxt.config.ts 中添加 runtimeConfig 屬性,就可以來設定只有伺服器端可以使用的環境變數。

例如,在 nuxt.config.ts 檔案中,添加一個 apiSecretruntimeConfig 屬性內:

export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: '怎麼可以讓你知道呢 :P'
  }
})

我們就可以在 Server API 使用 useRuntimeConfig() 獲得執行時的設定,再從中取得 apiSecret 環境變數。

const runtimeConfig = useRuntimeConfig()

export default defineEventHandler((event) => {
  const { apiSecret } = runtimeConfig

  console.log(`接收到了一個 Server API 請求: ${event.req.url}`)
  console.log(`執行時的環境變數 [apiSecret]: ${apiSecret}`)

  return 'ok'
})

因為我們是定義在 Server API,所以可以在測試伺服器啟動的 Terminal 看見 console.log 的結果。
https://ithelp.ithome.com.tw/upload/images/20221003/20152617yr39ko7ewH.png

此外你也可以在插件或 Vue 中使用 useRuntimeConfig() 來取得執行時的設置,但也僅在 setupNuxt Lifecycle Hooks 中有效。

客戶端使用 runtimeConfig

通常一些密鑰或敏感資訊,我們都會定義在 runtimeConfig 僅供伺服器端做使用,而 runtimeConfig 也可以配置一個 public 的屬性,來把一些環境變數於伺服器端或客戶端做使用,例如,API 的 Base URL 這類在伺服器端打 API 時會需要使用,而客戶端的操作流程也會打同樣的 API 位置,我們就可以使用 public 的屬性。

例如,在 nuxt.config.ts 檔案中,添加一個 apiBaseruntimeConfig.public 屬性內:

export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: '怎麼可以讓你知道呢 :P',
    public: {
      apiBase: '/api'
    }
  }
})

新增 ./pages/profile.vue 頁面:

<template>
  <div class="my-24 flex flex-col items-center">
    <span class="mt-4 text-2xl text-gray-600">回傳資料:</span>
    <p class="mt-4 text-3xl font-semibold text-blue-500">{{ data }}</p>
  </div>
</template>

<script setup>
const runtimeConfig = useRuntimeConfig()
const { apiBase } = runtimeConfig.public

console.log(toRaw(runtimeConfig))

const { data } = await useFetch(`${apiBase}/hello`)
</script>

添加在 runtimeConfig.public 屬性的環境變數,在伺服器端與客戶端都可以讀取得到。下圖中可以發現,在瀏覽器中的 Console 所印出的 runtimeConfig 是不會包含僅有在伺服器端能使用的 apiSecret
https://ithelp.ithome.com.tw/upload/images/20221003/20152617oBTjYNmBgT.png

使用 .env 建立環境變數

Nuxt 在開發模式或執行時,已經有內建 dotenv,如果在專案目錄下添加了 .env,Nuxt 會在開發期間、建構時或產生靜態網站時,自動載入 .env 內的環境變數。

例如建立 .env 檔案,內容如下:

NUXT_API_SECRET=api_secret_token
NUXT_PUBLIC_API_BASE=https://nuxtjs.org

這兩個值,將被 dotenv 自動載入至 process.env 中,作為環境變數。

環境變數的覆蓋

不論是透過 dotenv 自動載入 .env 或其他方式配置的環境變數,只要環境變數命名是 NUXT_ 開頭,這個環境變數將會覆蓋 runtimeConfig 的設置。

舉例來說,當我們 runtimeConfig 設置如下:

export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: '怎麼可以讓你知道呢 :P',
    public: {
      apiBase: '/api'
    }
  }
})

建立 .env 檔案或其他方式設置下列環境變數:

NUXT_API_SECRET=api_secret_token
NUXT_PUBLIC_API_BASE=https://nuxtjs.org

那麼 NUXT_API_SECRET 環境變數,將會覆蓋 runtimeConfig.apiSecret,而 NUXT_PUBLIC_API_BASE 將會覆蓋 runtimeConfig.public.apiBase,最終 runtimeConfig 的設定會變成如下:

{
  apiSecret: 'api_secret_token',     // 被 NUXT_API_SECRET 環境變數覆蓋
  public: {
    apiBase: 'https://nuxtjs.org'    // 被 NUXT_PUBLIC_API_BASE 環境變數覆蓋 
  }
}

會有這樣子的特性是因為 Nuxt 會在啟動時,先載入 nuxt.conf.ts 內的 runtimeConfig,建立出呼叫 useRuntimeConfig() 所得到的執行時設定,例如,先建構出了 _runtimeConfig 物件。

const _runtimeConfig = {
    apiSecret: '怎麼可以讓你知道呢 :P',
    public: {
      apiBase: '/api'
    }
}

接著會走訪這個 _runtimeConfig 物件裡面的 key,逐一將 key 的名稱轉換蛇形命名法 (Snake case),並轉成全大寫再加上 NUXT_ 前綴後取得對應的環境變數,如果存在就會以新值來覆蓋 _runtimeConfig 內的屬性。

例如 apiSecret 經過轉換變成 api_secret,接著轉大寫 API_SECRET 最後加上前綴變成 NUXT_API_SECRET,如此環境變數 NUXT_API_SECRET 的值就覆蓋 runtimeConfig.apiSecret

而在 public 下的設置,也會先轉為 public_apiBase 再經過蛇行命名成 public_api_base 等步驟,最後變成 NUXT_PUBLIC_API_BASE 來載入環境變數並覆蓋。

最後小提醒,當建構出生產環境的網站,如 .output 目錄後,dotenv 並不會包含在建構的網站內, 你需要再自己載入或配置環境變數才能正常運作哦,例如在 PM2 配置 env。

App Config

Nuxt 3 提供了一個 App Config 的配置方式,來提供給整個 Nuxt App 使用的響應式配置,並且能夠在生命週期執行之中更新它。

配置 appConfig

nuxt.config.ts 檔案中,可以在 appConfig 屬性內添加設置,例如,通常我們會添加像網站主題的主色等這類可以公開的配置,讓網站可以使用這個設置。

export default defineNuxtConfig({
  appConfig: {
    theme: {
      primaryColor: '#0ea5e9'
    }
  }
})

當建立好 appConfig 後,就可以使用組合式函數 useAppConfig() 來取得設置。

例如,建立 ./pages/config.vue,內容如下:

<template>
  <div class="my-24 flex flex-col items-center">
    <span class="mt-4 text-2xl text-gray-600">theme.primaryColor:</span>
    <p class="mt-4 text-3xl font-semibold text-blue-500">{{ theme.primaryColor }}</p>
  </div>
</template>

<script setup>
const appConfig = useAppConfig()
const { theme } = appConfig
</script>

app.config 檔案

你也可以在專案目錄下建立 app.config.ts 來配置 App Config,這個檔案的副檔名可以是 .ts.js.mjs。

export default defineAppConfig({
  theme: {
    primaryColor: '#3b82f6'
  }
})

當建立了 app.config.ts 檔案,該設定會與 nuxt.config.ts 檔案中的appConfig 屬性結合,如果具有相同的命名,則以 app.config.ts 檔案內的設置為主。

具有響應式的設定

當設定好的 App Config 在使用時,解構出的變數是具有響應性的,也就是說在其他頁面修改主題顏色的設定,可以響應至所有使用這個設定的元件。

舉個例子
新增一個 darkMode 的屬性至 app.config.ts 檔案內的 theme

export default defineAppConfig({
  theme: {
    primaryColor: '#3b82f6',
    darkMode: false
  }
})

建立 ./pages/index.vue,內容如下:

<template>
  <div class="bg-white py-24">
    <div class="flex flex-col items-center">
      <h1 class="text-6xl font-semibold text-gray-800">這裡是首頁</h1>
      <div class="my-4 flex flex-col space-y-4">
        <NuxtLink to="/config">前往 /config</NuxtLink>
      </div>

      <p class="mt-4 text-2xl text-gray-600">theme.darkMode:</p>
      <span class="mt-4 text-3xl font-semibold text-blue-500">{{ theme.darkMode }}</span>
    </div>
  </div>
</template>

<script setup>
const appConfig = useAppConfig()
const { theme } = appConfig
</script>

建立 ./pages/config.vue,內容如下:

<template>
  <div class="my-24 flex flex-col items-center">
    <p class="mt-4 text-2xl text-gray-600">theme.primaryColor:</p>
    <span class="mt-4 text-3xl font-semibold text-blue-500">{{ theme.primaryColor }}</span>

    <p class="mt-4 text-2xl text-gray-600">theme.darkMode:</p>
    <span class="mt-4 text-3xl font-semibold text-blue-500">{{ theme.darkMode }}</span>
    <button
      class="mt-6 rounded-sm bg-blue-500 py-2 px-4 text-base font-medium text-white hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-offset-2"
      @click="theme.darkMode = !theme.darkMode"
    >
      {{ `${theme.darkMode ? '取消' : '啟用'}深色模式` }}
    </button>

    <div class="mt-8">
      <NuxtLink to="/">回首頁</NuxtLink>
    </div>
  </div>
</template>

<script setup>
const appConfig = useAppConfig()
const { theme } = appConfig
</script>

如下圖,我們的首頁取得了 App Config 中的 theme.darkMode 預設為 false,接著我們切換至 /config 頁面,可以將從 useAppConfig() 解構出的 theme 進行變更,我們使用按鈕來設置 theme.darkModetruefalse 表示啟用或取消深色模式。當變更完成後,回至首頁,可以發現 theme.darkMode 發生了響應。
https://i.imgur.com/Jibg0k9.gif

小結

Nuxt 3 提供了 Runtime ConfigApp Config 來讓我們將常用或預設設定應用在不同的情境,使用時,我們僅需記得,不能公開的金鑰或敏感訊息,僅放置在 runtimeConfig 中而且不在 public 屬性內,runtimeConfig.public 通常放的是前後端會使用到且不常修改的常數。而 App Config 則是當伺服器端與客戶端需要使用的設置,如主題顏色、是否啟用深色模式等這類可以被使用者調整變動的且需要具有響應性,就可以放置在 appConfig 之中。


範例程式碼

參考資料


上一篇
[Day 17] Nuxt 3 狀態管理 - Store & Pinia
下一篇
[Day 19] Nuxt 3 串接 Google OAuth 登入
系列文
Nuxt 3 學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言