Vercel应用部署指南

目录:云计算  |  标签:云计算  |  发表于:2024-01-21 16:05:00

Vercel应用部署指南

Vercel是一个能免费托管Web站点的云平台,相比于Github Pages,Vercel的功能要更强大得多,Vercel上不仅可以部署静态站点,它还提供了CI/CD、多环境、Edge Functions,最近还新增了存储服务,也可以免费使用。相比于完整的Linux主机虽然还有诸多限制,但部署一些轻量级的应用程序是完全够用的。

此外Vercel团队对开源社区的贡献也很多,NextJS和TurboPack都是Vercel的知名开源项目。

官方文档:https://vercel.com/docs

部署静态站点

参考文档:https://vercel.com/docs/getting-started-with-vercel

Vercel最基础的使用方式就是部署静态站点,Vercel对大部分常见的前端框架构建均有支持,而且还十分大方的提供了100GB/月的免费流量,足够我们使用了。

在Vercel平台中直接点击Add New -> Project创建新工程,在这里我们需要关联我们的Github、Gitlab或Bitbucket账号,关联的账号是通过OAuth授权的,因此即使私有库也可以在Vercel部署。如果我们还没想好写点什么部署到Vercel,也可以看一看右边的工程模板,里面有很多现成的例子。

在工程配置界面,我们需要填写项目名、所使用的框架以及工程根目录,后面还可以选填工程的构建命令、输出目录之类的信息,对于主流框架这里都已经填好了默认值,但如果使用非Vercel预置的框架可能需要手动填写。

填好所有信息并提交后,Vercel会自动拉取代码和编译构建,我们稍等一会就可以访问部署好的工程了。Vercel对部署的工程提供了默认的子域名,我们也可以在工程设置中绑定自己的域名,在Vercel上配置好我们的自定义域名后在DNS记录中添加CNAME即可。

Edge Functions实现服务端功能

参考文档:https://vercel.com/docs/functions/edge-functions

Vercel不仅仅是个静态站点托管服务,我们还可以用Edge Functions实现完整的服务端功能,不过这里需要用到NextJS框架(如果使用非NextJS框架也可以实现服务端功能,这部分可以参考Serverless Functions相关的文档)。

要使用Edge Functions我们需要在Vercel平台创建工程并和Git仓库关联,然后将一些环境变量拉取到本地。

npx vercel env pull .env.development.local

Edge Functions的用法没有什么要额外介绍的,因为NextJS本身就提供了服务端API的开发能力,我们正常使用NextJS开发,部署到Vercel后,NextJS的RSC、API等其实都是直接运行在Edge Runtime中的。

不过这里要注意Vercel的Edge Runtime和NodeJS运行时有一点区别,我就被小小的坑过一把,当时开发的一个小工具为了省去服务端存Session的麻烦打算用JWT认证鉴权,于是使用了NodeJS的jsonwebtoken库,然而在Vercel上却不能运行,这是因为Edge Runtime缺少一些crypto的实现,jsonwebtoken库用到的一个加密算法不支持。

另外,Vercel的Edge Functions和Serverless Functions使用起来开发体验肯定不如SpringBoot、ASPNetCore、Laravel、Django之类的成熟服务端框架,它有诸多的限制,API设计的也缺少很多人性化的封装,总而言是就是用起来比较“生硬”,我们也完全可以仅用Vercel实现前端,服务端仍然部署在我们自己的服务器上。

使用Storage存储

参考文档:https://vercel.com/docs/storage

Vercel在早先没有提供服务端存储功能,因此通常是配合Firebase、Supabase或是自己的服务器作为后端使用的,但最近Vercel也提供了几种存储组件:KV是一个键值对数据库,基于Redis实现,Postgres是一个Serverless版的PostgreSQL,Blob是一个对象存储系统,Edge Config是一个配置中心。

使用Storage存储我们需要先创建Vercel工程和对应的存储组件实例,然后将它们关联到一起,之后还需要拉取环境变量配置,这样我们本地调试时也能连接到Vercel平台的存储组件上。

npx vercel env pull .env.development.local

这些存储组件都提供了免费使用额度,不过这里Vercel就有点抠门了_(:з)∠)_(尤其是Blob),开发阶段调试时千万别传太大的数据,否则瞬间用光免费额度就没法继续开发了。

Vercel KV

KV的用法非常简单,和大多数Redis客户端类似,我们首先安装相关npm依赖,引入后调用get()set()之类的方法就好。

npm i @vercel/kv

引入kv客户端。

import { kv } from "@vercel/kv";

存储一个值到KV。

await kv.set("tables-inited", true);

在管理界面中,我们可以执行Redis命令对键值对进行操作。

Vercel Postgres

使用Postgres需要编写SQL语句,这里注意我们平时可能MySQL使用的多一些,PostgreSQL的语法和MySQL大部分是兼容的,但也有一些小坑。

npm i @vercel/postgres

引入Postgres客户端。

import { sql } from "@vercel/postgres";

引入sql函数后,我们直接执行SQL语句就行了。

const { rows } = await sql`select count(*) from t_messages where user_id = ${user.user_id}`;

注意上面代码中虽然使用了字符串拼接,但Vercel为我们做了一些额外处理,因此上面写法没有SQL注入问题。

在管理界面我们可以执行SQL语句操作数据。

Vercel Blob

Vercel Blob是一个对象存储组件,但Vercel平台下使用方法和一般的OSS有些区别。我们平时用到OSS,上传文件可能是文件先传输到服务端然后再由服务端传输到OSS上,Vercel不推荐这种方式并限制了服务端的最大上传数据大小为4.5MB。

Vercel推荐“客户端上传”,简单来说就是我们的服务端代码只负责给浏览器前端一个Token,浏览器前端用这个Token直接将文件上传到对象存储中,上传完成后由Blob组件回调我们的服务端,通知我们上传完成。

前端代码如下。

app/avatar/upload/page.jsx

"use client";

import { upload } from "@vercel/blob/client";
import { useState, useRef } from "react";

export default function AvatarUploadPage() {
  const inputFileRef = useRef(null);
  const [blob, setBlob] = useState(null);
  return (
    <>
      <h1>Upload Your Avatar</h1>

      <form
        onSubmit={async (event) => {
          event.preventDefault();

          const file = inputFileRef.current.files[0];

          const newBlob = await upload(file.name, file, {
            access: "public",
            handleUploadUrl: "/api/avatar/upload",
          });

          setBlob(newBlob);
        }}
      >
        <input name="file" ref={inputFileRef} type="file" required />
        <button type="submit">Upload</button>
      </form>
      {blob && (
        <div>
          Blob url: <a href={blob.url}>{blob.url}</a>
        </div>
      )}
    </>
  );
}

后端代码如下。

app/api/avatar/upload/route.js

import { handleUpload } from "@vercel/blob/client";
import { NextResponse } from "next/server";

export async function POST(request) {
  const body = await request.json();

  try {
    const jsonResponse = await handleUpload({
      body,
      request,
      onBeforeGenerateToken: async (pathname /*, clientPayload */) => {
        // Generate a client token for the browser to upload the file
        // ⚠️ Authenticate and authorize users before generating the token.
        // Otherwise, you're allowing anonymous uploads.

        return {
          allowedContentTypes: ["image/jpeg", "image/png", "image/gif"],
          tokenPayload: JSON.stringify({
            // optional, sent to your server on upload completion
            // you could pass a user id from auth, or a value from clientPayload
          }),
        };
      },
      onUploadCompleted: async ({ blob, tokenPayload }) => {
        // Get notified of client upload completion
        // ⚠️ This will not work on `localhost` websites,
        // Use ngrok or similar to get the full upload flow

        console.log("blob upload completed", blob, tokenPayload);

        try {
          // Run any logic after the file upload completed
          // const { userId } = JSON.parse(tokenPayload);
          // await db.update({ avatar: blob.url, userId });
        } catch (error) {
          throw new Error("Could not update user");
        }
      },
    });

    return NextResponse.json(jsonResponse);
  } catch (error) {
    return NextResponse.json(
      { error: error.message },
      { status: 400 } // The webhook will retry 5 times waiting for a status 200
    );
  }
}

补充:不过我在实际开发中发现服务端怎么都收不到上传完成的回调onUploadCompleted,本地使用IPV6公网或是部署到公网环境都不行,不知道是不是bug,索性就不处理Blob的回调了,上传完成改为由浏览器通知服务端(这样显然存在安全风险,因为客户端可以伪造一个假的上传完成通知,但我的场景恰好没有这种风险,因此使用这种方式作为替代)。

在管理界面我们可以上传和下载文件。

Edge Config

Edge Config是一个注册中心,简单理解就是个服务端的配置文件,我们可以远程修改其中的内容,应用程序也会读取到热更新的配置,当然实际真正的实现要更复杂一些。

npm i @vercel/edge-config

我们引入用来读取配置的get()函数。

import { get } from "@vercel/edge-config";

然后就可以读取配置了。

const greeting = await get("greeting");

在管理界面我们可以修改配置数据。

例子项目:云剪贴板

这里我用NextJS14写了一个简单的Demo工程作为参考,存储使用的是Vercel的组件。

参考工程:https://github.com/gacfox/vercel-clipboard

它是一个“云剪贴板”,可以发送文本或是上传小文件到服务端,用于在多个设备之间复制黏贴之类的。因为我几乎不用微信、QQ,平时从电脑传一些信息到手机都是黏贴一个文件到局域网的Samba服务上,有点麻烦,因此写了这个小东西。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap