简简单单的 spa + vercel + serverless function 居然有这么多坑

手上没活不太行,所以我想整个活。

整个小网站吧,要有路由,要有域名,要有接口,要有数据库。还要 不花钱

我看中了 vercel

天下没有免费的午餐,vercel 有,不花钱的 Hobby Plan 足够整活用了,但是不要效仿"Java之父"把自己的站点交给网友压测。

Feature Hobby Pro
Projects 200 Unlimited
Bandwidth 100 GB 1 TB
Edge Functions execution units 500,000 1 million
Edge Middleware invocations 1 million 2 million
Serverless Function maximum duration 10 seconds 15s (default) - configurable up to 300s
Serverless Function execution 100 GB-hours 1,000 GB-hours
Build execution minutes 6,000 24,000
Image Optimization source images 1,000 5,000
Team collaboration features - Yes
Remote Cache artifact downloads 10 GB 20 GB
Domains per project 50 Unlimited
Deployments per day 100 6,000
Analytics - Limited
Email support - Yes

它提供了 templates

各种用途风格框架的项目模板都有

点进去可以预览 demo,也可以一键部署

支持多种 Git Provider

后面就是傻瓜式操作,选个 Provider,选个账号,选个项目名,Create 就完了,你会得到一个代码仓库和 vercel 白给的域名,点进去就可以预览你的网站了。

它支持了 serverless function

vercel 支持用 Node.js, Go, Python, Ruby 写 serverless function,具体请参考文档。

同时还提供了一些模板

它集成了 storage

在这里选个喜欢的,连接到项目就行了,文档在这里 👉🏻 vercel.com/docs/storag...

它连接了 GitHub

在 dashboard 里 create new project,会被引导创建项目,可以轻松地连接到 GitHub 项目并部署

它还整了 持续集成

当你 project settings 中监听的分支有变更时,vercel 会自动构建,这里可以查看构建历史

我遇到了 坑

看了文档和模板示例,我说快整吧,我感觉我现在强得不行。然而,事情并不顺利,最简单的起步,让我踩了仿佛两年半的坑。

线上非默认路由刷新 404 了

我用的 React 的 BrowserRouter,想必开发过 spa 的鸽鸽们都遇到过,本地没问题,到线上刷新非默认路由 404.

这种情况如果没有 nginx 反向代理之类的配置,通常访问默认路由没问题,跳转非默认路由也没问题,但是当在非默认路由页面刷新时就会 404,是因为请求不到资源,spa 中就一个 html,没其他页面,请求不到也正常。

我们要想办法把这个请求定向到 index.html,这就妥了。

Stack Overflow 和 vercel 文档上都有方法,不赘述了,直接贴解决办法

在项目根目录中创建 vercel.json,甚至可以在这个配置文件里设置反向代理。然后部署项目就好了

json 复制代码
{
  "rewrites": [
    {
      "source": "/(.*)",
      "destination": "/index.html"
    }
  ]
}

本地调接口进到路由里了

到这里先说怎么加接口,整个最简单的 Hello World 接口,参考 👉🏻 Quickstartexample 原来加个接口这么简单。

加了接口之后,为什么线上能访问,本地接口一访问就进路由了呢?

原来不能用 pnpm dev 启动服务,需要用 vercel dev,它才会把 serverless function 跑起来,同时还要改造下 vercel.json,用负向前行断言或叫否定前瞻断言 告诉 vercel 把别的路由定向到 /index.html 得了,api 开头的是接口,不要这么处理。

json 复制代码
{
  "rewrites": [
    {
      "source": "/((?!api\/.*).*)",
      "destination": "/index.html"
    }
  ]
}

这么一来在本地也把 api 开头的请求当做接口处理了。

调接口 FUNCTION_INVOCATION_FAILED

但是,为啥一调用接口就报错了呢?

查了一大圈,Stack Overflow、GitHub issues、discussions、vercel 文档都看遍了,配置也是一顿改,没用。然后问了 GPT,它最没用,说你看日志去吧,问题是根本没日志。

然后我想,既然本地和线上环境是有差异的,保不齐线上能行呢?发了下线上,调接口,还不行。但是,不同的是线上有日志,在这个地方

没想到从最没用的回答中找到了最朴素的解决方法。按照日志提示,在 package.json 中设置 type: module 解决问题。

调接口返回内容异常了

此时应该没有什么幺蛾子了,继续调试,发现线上好的,本地访问接口也能访问,但是返回的响应有点不对劲。

仔细一看,没有返回 Hello World,而是把 api/hello.ts 中的 file content 返回出来了,这也没用啊,肯定是配置问题。

配置改来改去,用过 rewrites, redirects, functions 等等都没用。

又找来找去,找到个方法,说在 vercel.json 中用 routes 字段替换 rewrites 字段,虽说文档不建议使用这个字段,但是没办法,试试吧

json 复制代码
{
  "routes": [
    {
      "src": "/((?!api\/.*).*)",
      "dest": "/index.html"
    }
  ]
}

结果改成这样还真行,欢欢喜喜部署到线上。

本地好了,线上不行了

又出问题了,本地行了,线上不行了,访问页面加载不出来了,一看是加载 js 资源文件出问题了,响应头 Content-Type 变成 text/html 了,怪不得不行了,记得文档上有在 vercel.json 中设置 headers 的选项。

针对 dist/assets/**/*.js 资源设置了正确的响应头,线上倒是能访问页面了,但是样式没了,项目中用了 unocss。这怎么行?

我们已经知道用 vercel 运行项目时需要 vercel.json 配置文件,能不能像加载 .env 文件似的在不同环境加载不同的配置?查了下没有这种写法,但是从 vercel help dev 命令中找到了希望。

bash 复制代码
➜ ~ vercel help dev
Vercel CLI 33.4.1

  ▲ vercel dev [dir] [options]

  Starts the `vercel dev` server.                                           

  Options:

   --listen <uri>  Specify a URI endpoint on which to listen [0.0.0.0:3000]  


  Global Options:

       --cwd <DIR>            Sets the current working directory for a      
                              single run of a command                       
  -d,  --debug                Debug mode (default off)                      
  -Q,  --global-config <DIR>  Path to the global `.vercel` directory        
  -h,  --help                 Output usage information                      
  -A,  --local-config <FILE>  Path to the local `vercel.json` file          
       --no-color             No color mode (default off)                   
  -S,  --scope                Set a custom scope                            
  -t,  --token <TOKEN>        Login token                                   
  -v,  --version              Output the version number                     


  Examples:

  - Start the `vercel dev` server on port 8080

    $ vercel dev --listen 8080

  - Make the `vercel dev` server bind to localhost on port 5000

    $ vercel dev --listen 127.0.0.1:5000 

可以看到我们能使用 -A 参数指定配置文件,默认是 vercel.json,那好,我在项目根目录准备了两个文件:

vercel.json

json 复制代码
{
  "rewrites": [
    {
      "source": "/((?!api\/.*).*)",
      "destination": "/index.html"
    }
  ]
}

vercel.local.json

json 复制代码
{
  "routes": [
    {
      "src": "/((?!api\/.*).*)",
      "dest": "/index.html"
    }
  ]
}

在本地启动时用命令 vercel -A vercel.local.json dev,本地没问题了。

部署到线上,测试也没问题。

问题解决了

到现在问题是解决了,我明白,虽然这么整有点丑,但是我还没找到更体面的方法,先这么地吧。

希望能帮大家在整活路上踩坑时间少一点,有经验一起分享。

最后祝各位鸽鸽回家路上平平安安

相关推荐
码农超哥同学6 小时前
Python知识点:如何使用OpenFaaS与Python进行无服务器边缘计算
python·面试·serverless·编程·边缘计算
problc2 天前
服务架构的演进之路:从单体应用到Serverless
java·云原生·架构·serverless
行十万里人生3 天前
信号处理: Block Pending Handler 与 SIGKILL/SIGSTOP 实验
c++·后端·深度学习·ubuntu·serverless·信号处理·visual studio code
阿里云大数据AI技术4 天前
云栖实录 | 开源大数据全面升级:Native 核心引擎、Serverless 化、湖仓架构引领云上大数据发展
大数据·阿里云·开源·serverless·云栖大会
_.Switch6 天前
Python Web 开发中的DevOps 实践与自动化运维
运维·开发语言·前端·python·架构·serverless·devops
techdashen6 天前
Serverless and Go
云原生·golang·serverless
全栈若城8 天前
AI驱动TDSQL-C Serverless 数据库技术实战营-融合智能体与TDSQL-C技术,高效实现二手房数据查询与分析应用
c语言·人工智能·serverless·腾讯云tdsql-c
huaqianzkh13 天前
了解云容器实例云容器实例(Cloud Container Instance)
云原生·架构·serverless
lxr190813 天前
Serverless架构
云原生·架构·serverless
_.Switch14 天前
构建现代应用的Python Serverless架构详解
运维·开发语言·python·云原生·架构·serverless·restful