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

art*_*ras 5 firebase google-cloud-functions next.js

在学习了一些像这样的教程之后,我能够将我的Next.js应用程序部署到 Firebase Hosting/Functions。这是我的项目的简化文件夹结构:

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
  ...
Run Code Online (Sandbox Code Playgroud)

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

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

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

"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"
  }
Run Code Online (Sandbox Code Playgroud)

而我的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"
  }
Run Code Online (Sandbox Code Playgroud)

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

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

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

索引.ts

import * as admin from 'firebase-admin'

admin.initializeApp()

export * from './app' // next.js
export * from './other'
Run Code Online (Sandbox Code Playgroud)

其他.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.
Run Code Online (Sandbox Code Playgroud)

应用程序.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)
})
Run Code Online (Sandbox Code Playgroud)

另外,这是我的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"
    }]
  }
}
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

sam*_*man 4

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

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

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

"functions": {
  "source": "." // <- the project's root directory
}
Run Code Online (Sandbox Code Playgroud)

相反,您应该使用部分部署。在这种情况下,您需要移至app.tsNext 项目的目录,并将不依赖于 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
    ...
Run Code Online (Sandbox Code Playgroud)

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

{
  "functions": {
    "source": ".",
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint",
      "npm --prefix \"$RESOURCE_DIR\" run build"
    ]
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

"scripts": {
  "deploy": "firebase deploy --only functions:otherFunc1,functions:otherFunc2",
  ...
}
Run Code Online (Sandbox Code Playgroud)

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

// index.ts
import * as otherFunctions from './other.ts';
export { otherFunctions };
Run Code Online (Sandbox Code Playgroud)
// package.json
"scripts": {
  "deploy": "firebase deploy --only functions:otherFunctions",
  ...
}
Run Code Online (Sandbox Code Playgroud)

您的/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"
    }]
  }
}
Run Code Online (Sandbox Code Playgroud)

将以下内容(或类似内容)添加到/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"
}
Run Code Online (Sandbox Code Playgroud)

通过上述更改,放入适当的目录并npm run deploy在需要使用时使用firebase deploy


注意:不要忘记,Cloud Functions会自动使用请求的正文,因此,请确保在 Next 项目中使用正文解析器并使用 进行本地测试时考虑到这一点next start