使用现有函数将 Next.js 应用部署到 Firebase Functions

Posted

技术标签:

【中文标题】使用现有函数将 Next.js 应用部署到 Firebase Functions【英文标题】:Deploy Next.js app to Firebase Functions with existing functions 【发布时间】:2021-10-11 07:22:17 【问题描述】:

按照一些教程 like this one,我能够将我的 Next.js 应用程序部署到 Firebase 托管/功能。这是我的项目的简化文件夹结构:

myApp
  node_modules // Next.js app dependencies
  .next // destination of npm run build of the Next.js project
  pages // Next.js pages
  functions
    lib // this is where the functions compile
    node_modules // functions' dependencies
    src
      app.ts
      index.ts
      other.ts
    package.json
  public // Next.js app public files
  firebase.json
  package.json
  ...

当我运行firebase deploy 时,它会将functions 编译到其lib 文件夹中并成功部署所有内容。

但是,当我尝试访问我的网络应用程序的 url 时,我得到了Error: could not handle the request。当我查看 Firebase 函数日志时,我可以看到一条错误消息 Error: Cannot find module 'react'。所以,我又成功地将react 添加到functions/package.jsonnpm installfirebase deploy。然而,另一个错误,但这次是Cannot find module 'react-dom'

我的理解是,这样做的原因是我的 Next.js 应用程序依赖于我的 package.json 在我的 root 文件夹中列出的所有依赖项(下面的简化列表):

"dependencies": 
    "babel-plugin-prismjs": "^2.1.0",
    "firebase": "^8.8.1",
    "firebase-admin": "^9.11.0",
    "formik": "^2.2.9",
    "js-cookie": "^3.0.0",
    "next": "11.0.1",
    "next-i18next": "^8.5.5",
    "nookies": "^2.5.2",
    "prismjs": "^1.24.1",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-ga": "^3.3.0",
    "react-select": "^4.3.1",
    "react-spinners": "^0.11.0",
    "react-transition-group": "^4.4.2",
    "recharts": "^2.0.10",
    "styled-components": "^5.3.0",
    "yup": "^0.32.9"
  

而我的functions/package.json 显然要短得多:

"dependencies": 
    "@sendgrid/mail": "^7.4.4",
    "firebase-admin": "^9.8.0",
    "firebase-functions": "^3.14.1",
    "next": "^11.0.1",
    "react": "^17.0.2"
  

我假设错误会要求我将所有依赖项复制到 functions/package.json 文件中,这显然是我不想要的。

或者,我可以将firebase-functions 和其他相关依赖项从functions/package.json 添加到root/package.json,并在根文件夹中设置下一个服务器。但是,如何包含其他现有的云功能?

我的functions/src 文件的内容如下所示:

index.ts

import * as admin from 'firebase-admin'

admin.initializeApp()

export * from './app' // next.js
export * from './other'

other.ts

import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

exports.onUserCreate = functions.database.ref...
// my other cloud functions, such as db triggers, https functions, etc.

app.ts

import next from 'next'
import https from 'firebase-functions'

const server = next(
  dev: process.env.NODE_ENV !== 'production',
  conf: distDir: '.next'
)

const nextjsHandler = server.getRequestHandler()

exports.app = https.onRequest(async (req, res) => 
  await server.prepare()
  return await nextjsHandler(req, res)
)

另外,这是我的firebase.json


  "functions": 
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint",
      "npm --prefix \"$RESOURCE_DIR\" run build"
    ]
  ,
  "hosting": 
    "target": "myApp",
    "public": "public",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      "source": "**",
      "function": "app"
    ]
  

如何正确部署Next.js 应用并确保我的其他云功能继续工作?

谢谢!

【问题讨论】:

【参考方案1】:

虽然您可以将 /functions 目录提升到项目的根目录,但这意味着您的其他函数会因您的所有 Next.js 应用程序的依赖项而不必要地膨胀。

当您部署云函数时,配置的部署目录(默认为/functions)中的所有内容都会被部署 - 即使您不使用它。它也为每个功能部署一次。因此,如果您的函数目录中有一个 10MB 的文件,则每个函数的部署大小将增加 10MB。这就是我所说的“膨胀”函数的意思。

仅供参考:您可以通过将以下内容添加到您的 firebase.json 文件 (docs) 来更改部署的函数目录:

"functions": 
  "source": "." // <- the project's root directory

相反,您应该使用partial deployment。在这种情况下,您需要将 app.ts 移动到 Next 项目的目录中,并将不依赖于 Next 部署的其他功能留在 /functions 文件夹中。如果您部署整个项目目录,即使您不使用 /functions,您最终也会部署它,因此您应该将 Next 前端和函数移动到它自己的文件夹中。

文件结构类似于(详情如下):

/myApp
  /functions
    /lib            // this is where the functions compile
    /node_modules   // functions' dependencies
    /src
      index.ts
      other.ts
    firebase.json   // configuration for this folder's contents
    package.json    // configuration for this folder's contents
  /next
    /node_modules   // Next.js app dependencies
    /.next          // compiled Next.js project
    /pages          // Next.js pages
    /public         // Next.js app public files
    /functions-lib
      app.js        // compiled Next function
    /functions-src
      app.ts        // source of Next function
    firebase.json   // configuration for this folder's contents
    package.json    // configuration for this folder's contents
    ...

您的/functions 文件夹的firebase.json 文件类似于:


  "functions": 
    "source": ".",
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint",
      "npm --prefix \"$RESOURCE_DIR\" run build"
    ]
  

要使上述结构正常运行,您需要调整 firebase.jsonpackage.json 文件的配置。

将以下内容添加到/function 文件夹的package.json 文件中:

"scripts": 
  "deploy": "firebase deploy --only functions:otherFunc1,functions:otherFunc2",
  ...

您也可以将这些函数导出为单个组以便于部署,但函数将命名为 otherFunctions-otherFunc1otherFunctions-otherFunc2 等:

// index.ts
import * as otherFunctions from './other.ts';
export  otherFunctions ;
// package.json
"scripts": 
  "deploy": "firebase deploy --only functions:otherFunctions",
  ...

您的/next 文件夹的firebase.json 文件类似于:


  "functions": 
    "source": ".",
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint",
      "npm --prefix \"$RESOURCE_DIR\" run build"
    ]
  ,
  "hosting": 
    "target": "myApp",
    "public": "public",
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "rewrites": [
      "source": "**",
      "function": "app"
    ]
  

将以下(或类似内容)添加到 /next 文件夹的 package.json 文件中:

"scripts": 
  "build": "npm run build:next && npm run build:functions",
  "build:functions": "tsc",
  "build:next": "next build",
  "lint": "npm run lint:next && npm run lint:functions",
  "lint:functions": "eslint --ext .js,.ts ./functions-src",
  "lint:next": "...",
  "deploy": "firebase deploy --only hosting,functions:app"

通过上述更改,放入适当的目录并使用npm run deploy,无论何时使用firebase deploy


注意:不要忘记 Cloud Functions consume the bodies of requests automatically,因此在 Next 项目中使用正文解析器并使用 next start 进行本地测试时,请务必考虑到这一点。

【讨论】:

哇,感谢您如此详尽的回复!我会试试这种方法。仅当我有两个带有功能的单独文件夹时,我才假设我需要分别从每个文件夹中进行部署?但是,这不会覆盖彼此的功能吗?通常,当我部署已删除的功能时,firebase-cli 会检测到该功能并在云中删除该功能。或者也许使用部分部署可以避免这种确切的事情? @artooras 部分部署避免了确切的事情。它不会检查所有函数的本地函数列表是否与所有函数的部署列表不匹配,它只关心你命名的那些。这也是您将npm run deploy 与相应的package.json 文件一起使用的原因。 顺便说一句,感谢您指出指定函数目录中的所有内容都已为每个函数部署,我在youtube.com/watch?v=rCpKxpIMg6o 看到了这一点。我没有意识到这一点。 所以,我得到了以下差异。 (1) 我将所有文件都保存在项目根目录中,然后我简单地将 functions 文件夹包含在根目录中 firebase.json 的 ignore 部分中。 (2) 我需要在 functions 文件夹中添加 .firebaserc 文件来指示我的项目。 (3) 我正在部署 app.js 而没有显式构建它,我只是使用 require 而不是 import 对其进行了编辑,那就是足够的。所以,再次感谢您的帮助! @samthecondingman 你介意看看我的后续问题吗?我不想在这里继续讨论 cmets,所以我将它创建为一个单独的问题。 ***.com/questions/68711863/…

以上是关于使用现有函数将 Next.js 应用部署到 Firebase Functions的主要内容,如果未能解决你的问题,请参考以下文章

将使用 Yarn 的 Next.JS 网络应用程序部署到 Google App Engine

错误:使用 Next.js 应用程序部署到 Azure 时出现“需要域选项”

如何将带有 Mongodb 的 Next.js 应用程序部署到 Heroku?

将 Next.js 部署到 Apache 服务器

使用 getStaticProps() 将 Next.js 部署到 Vercel 的速率限制问题

Heroku 将 Next.js React 客户端应用程序 http 重定向到 https