普通视图

发现新文章,点击刷新页面。
昨天以前YeungYeah 的乱写地

低损耗用港卡薅羊毛

手上开了一些港卡账户,除了日常的一些境外消费使用需要外,有些福利还是挺香的,比如众安 ZA Bank,交易 2% 回赠,可以直接换成现金,然后现在推广股票交易后,还可以将回赠转成想要的股票回赠。比较可惜的是,外币交易的话众安会收 1.95% 的货币转换费,无论是直接卡支付还是绑定支付宝微信,所以也不怎么用,只是偶尔在需要消费 HKD 时使用。

最近刷小红书学到了新东西,没有 HKID 的情况下,也有办法开通 Wechat Pay HK,然后通过 Wechat Pay HK 绑定 ZA Bank,这样用 Wechat Pay HK 支付的场景下,众安的卡可以 HKD 入账,这样就免去货币转换费了。同时大部分能用微信支付的场景(商户码)现在也能用 Wechat Pay HK 支付。一天使用下来,支持的范围还是挺全的。

流程如下

  1. 微信搜索 WeChat Pay HK 公众号,通过菜单找到自助开通入口
  2. 选择绑定香港银行账号
    1. 如果没有 HKID 的话,这里限制了只有中银香港等五家银行才能绑定
  3. 输入相应银行的网银账号登陆,进行验证绑定
  4. 绑定后就能开通成功 Wechat Pay HK 了
  5. 开通之后可以再给 Wechat Pay HK 绑卡,这个时候就能帮 ZA Bank 的卡了

Wechat Pay HK 和微信切换起来也比较方便,点进去钱包,切换地区就行了。比较有意思的一个点是,切换了 WeChat Pay HK,给好友发红包和转账都是 HKD,然而如果对方没有开通 Wechat Pay HK 的话,就收不了红包。但是反过来就没有问题,别人发 CNY 的红包,在切换 WeChat Pay HK 后可以直接收下,连切换都不需要。

LeetCode: Make a Square with the Same Color

Problem

source: 3127. Make a Square with the Same Color

difficulty: easy

You are given a 2D matrix grid of size 3 x 3 consisting only of characters 'B' and 'W'. Character 'W' represents the white color, and character 'B' represents the black color.

Your task is to change the color of at most one cell so that the matrix has a 2 x 2 square where all cells are of the same color.

Return true if it is possible to create a 2 x 2 square of the same color, otherwise, return false.

Constraints:

  • grid.length == 3
  • ``grid[i].length == 3`
  • grid[i][j] is either 'W' or 'B'.

Solution

问题可以转化成判断 matrix 是否存在一个 2 * 2 的 matrix,其中包含 3 or 4 个相同的元素。

但是再仔细一看 matrix 只有 3 * 3,因此可以直接分成 4 个 block,分别判断 4 个 block 是否存在就行了。

Kotlin

直接判断每一个点分别在哪些 block 里面,但是这几个 if 判断就写得很丑陋(写了一个之后干脆打开 GitHub Copilot 生成算了)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class Solution {
 fun canMakeSquare(grid: Array<CharArray>): Boolean {
 val cnt = IntArray(4)
 fun Char.value(): Int = if (this == 'B') 1 else -1

 for (i in 0..2) {
 for (j in 0..2) {
 if ( i < 2 && j < 2) cnt[0] += grid[i][j].value()
 if ( i < 2 && j > 0) cnt[1] += grid[i][j].value()
 if ( i > 0 && j < 2) cnt[2] += grid[i][j].value()
 if ( i > 0 && j > 0) cnt[3] += grid[i][j].value()
 }
 }

 return cnt.any() { it in listOf(2, -2, 4, -4) }
 }
}

Scala

最近在学 Scala,于是自然又用 Scala 写一次。相比于此前丑陋的 if 判断,干脆写得更加函数式,试一下 loop free。

直接三行搞定。

1
2
3
4
5
6
7
8
9
object Solution {
 def canMakeSquare(grid: Array[Array[Char]]): Boolean = {
 def getRange = for { i <- 0 to 1; j <- 0 to 1} yield (i, j)

 def check(x: Int, y: Int): Boolean = getRange.count((i, j) => grid(x + i)(y + j) == 'B') != 2

 getRange.count((i, j) => check(i, j)) >= 1
 }
}

手动生成 SSL 证书添加到又拍云

本站一直用又拍云进行 CDN 加速,因为又拍云有一个又拍云联盟的活动,博主只需要在网站放一个 LOGO 即可获取一定的免费额度。唯一的问题就是需要每年重新申请一下这个又拍云的活动。

又拍云还提供 SSL 证书申请与管理,可以一站式管理 HTTPS 的问题。之前一直用他家的 TrustAsia DV SSL 单域名证书,还可以自动续签。然而今年到期后开始收费了,只能转向免费的 Let's Encrypt. 但不知道又拍云还是 Let's Encrypt 这里抽什么风,重复申请好几次都还是失败,提工单来来回回咨询了很多次都没有结果,最后跟我说的厂商 Let's Encrypt 给我限制了。。。

没办法,而且我的旧证书马上就要过期了,只能自己动手,丰衣足食。又拍云支持上传自己的证书,干脆我自己去 Let's Encrypt 申请一个证书上传上去。

我用的是 acme.sh 进行的申请,使用起来也很简单。

  1. 安装 acme.sh

    1. curl https://get.acme.sh | sh -s email=my@example.com
    2. 安装后默认会搞一个每天 0 点运行的 crontab 自动更新过期证书
  2. 我这里用的是 dns 验证域名所有权的方式.

    1. 手动验证(不能够自动更新,每次都需要手动更新)
      1. 生成 dns 验证相关的 dns 配置,生成后需要添加 dns 解析。
        1. acme.sh --issue --dns -d scottyeung.top --yes-I-know-dns-manual-mode-enough-go-ahead-please
      2. 添加 dns 解析后,生成证书。
        1. acme.sh --renew -d scottyeung.top --yes-I-know-dns-manual-mode-enough-go-ahead-please
    2. 自动验证。通过申请 DNS 解析商的 API Key,访问 DNS 解析商来进行验证
      1. 申请解析商相应的 API key,并通过环境变量进行设置。不同的解析商使用不同的变量命名,可以参考 acme.sh/wiki。下面以 DNSPod 为例
        1. export DP_Id="<id>"
          export DP_Key="<key>"
          
      2. 生成证书
        1. ./acme.sh --issue --dns dns_dp -d scottyeung.top
  3. 最终生成的证书和私钥放在 ~/.acme.sh/scottyeung.top_ecc

    1. 证书:fullchain.cer
    2. 私钥:scottyeung.top.key

生成之后把证书和私钥上传到又拍云就可以。唯一的缺点是有效期比较短,只有 90 天,需要 60 天后更新,到时再看看能不能自动化更新。 (三个月已经到了,在更新的过程中意识到就算可以本地更新,还是需要手动上传到 又拍云 去,才能确保又拍云上面的证书不过期)。

Apple Books 复制内容去除版权信息

最近在电脑上使用 Apple Books 看书,体验还可以,但就是做笔记的时候有个很烦人的地方:复制书本内容时,Books 会自动给复制的内容加上双引号,并且加上版权信息。而且这还不能通过引用的设置去除,导致做笔记摘录时每次粘贴后都还要手动去删除,麻烦得很,

“如果有证据表明分配空集合会损害性能,可以通过重复返回相同的不可变空集合来避免分配,因为不可变对象可以自由共享 (条目 17)。下面的代码就是这样做的”

Excerpt From effective java 3rd 中文版 wizardforcel This material may be protected by copyright.

上网搜了一下,还是挺多人吐槽的,大家也只能通过外部的手段来处理,主要看到的解决方案都是用 Automator.

Automator

Automator 是 macOS 上附带的一个自动化工具,可以通过编排里面的 action,实现自动化的操作。

对于我们这个去除版权信息的需求,整体的思路就是

  1. 获取剪贴板中的文本内容
  2. 将文本内容中原始的文本内容取出来。
    1. 可以通过 JavaScript 脚本来实现
    1
    2
    3
    4
    5
    
    function run(input, parameters) {
     // Your script goes here
     let matched = input.join('').match(/“([^”]*)”/);
     return matched[1];
     }
    
  3. 将取出来的内容返回入剪贴板中

听起来很简单,就像将大象放入冰箱一样三步完成。

设定好 Automator 后,就可以在系统的快捷键中,为新设置的服务(Quick Action)添加一个快捷键,然后在应用当中触发。但是我在设置好后,无论换怎样的快捷键,都没有办法能够触发成功,最好放弃了这个办法。

Raycast

反正都是用脚本,不如多走一个流程,干脆直接用 Raycast 写个脚本做一次转换?这样还不用去设置和记忆额外的快捷键。

在复制了 Books 当中的内容后,执行 Raycast 的 script 对剪贴板内容做一次转换。这里主要麻烦的地方是 nodejs 原生其实没有直接访问剪贴板的 API。这里就借鉴了一下 clipboardy 的实现,通过调用命令行命令,访问剪贴板。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/env node

// Required parameters:
// @raycast.schemaVersion 1
// @raycast.title Remove Copyright
// @raycast.mode silent

// Optional parameters:
// @raycast.icon 🤖

// Documentation:
// @raycast.author YeungYeah

const { exec } = require("child_process")
exec(
 "pbpaste",
 {
 env: { ...process.env, LANG: "en_US.UTF-8" },
 },
 (error, stdout, stderr) => {
 if (error) {
 console.error(`exec error: ${error}`)
 return
 }

 // 使用正则表达式匹配中文引号下的内容
 const match = stdout.match(/“([^”]*)”/)
 if (match) {
 const text = match[1]

 // 写入到剪贴板
 exec(
 `echo "${text}" | pbcopy`,
 {
 env: { ...process.env, LANG: "en_US.UTF-8" },
 },
 (error) => {
 if (error) {
 console.error(`exec error: ${error}`)
 return
 }
 console.log("Text has been copied to clipboard.")
 }
 )
 } else {
 console.log("No matching text found.")
 }
 }
)

简单直接,通过 exec 执行命令行命令获取剪贴板内容,然后通过正则表达式抽取其中的文本内容进行抽取,抽取成功后再写入到剪贴板当中。更爽的是,以上的代码还是直接由 ChatGPT 帮我写的。

虽然代码看起来简单,但是调试起来却让我踩坑了很久,一开始写好直接在命令行运行脚本,运行没问题,以后好了,但是从 Raycast 发起,得到的结果确实有问题,怎么都匹配不上。后面通过打日志输出全文才发现,输出居然是乱码,很显然是编码问题,最终人工加上了 env: { ...process.env, LANG: "en_US.UTF-8" } 设置环境变量设置编码,问题解决。看来 AI 还是不够熟悉中文环境 😂

加上这个编码环境变量设置后,问题解决 🛫,又可以继续爽快地看 Books 做笔记了。

Nuxt3 无法初始化项目解决

国内环境下,使用 Nuxt 的项目初始化命令会出现下面错误。

Error: Failed to download template from registry: Failed to download https://raw.githubusercontent.com/nuxt/starter/templates/templates/v3.json: TypeError: fetch failed

原因似乎是在项目初始化时,会到 GitHub 上面下载模版信息,而 raw.githubusercontent.com 的下载网址被 DNS 污染是不能够直接访问的,所以访问失败,初始化项目自然也失败了。

一般这种情况,很自然地就会想是不是因为没走代理的原因,开个全局代理就可以了吧。试了一下不行,以为是终端没开代理,又加了个 proxy 的环境变量,还是不行。上网一搜,直接在官方 repo 搜到了对应的 issue。挺多人都遇到了这个问题,里面提到大多数可行解决方法,是通过修改 /etc/hosts 实现的,但不想改系统文件设置,就没尝试这个办法。

为什么通过环境变量设置代理不行呢?看了一下 issue 的内容,是因为 Nuxt3 使用 giget 来从 GitHub 仓库下载模版,而这个 giget 仓库使用了原生的 node-fetch 来实现下载功能,而这个原生的 fetch 是不支持使用代理的,所以就算设置了环境变量,也无法使用代理下载,导致项目无法初始化。

还好看到 @hzgotb 通过翻源码找到了解决办法: 这个文件的下载链接会先在环境变量 NUXI_INIT_REGISTRY 当中取,所以为了解决下载不了的问题,这个老哥直接 clone 了一个 repo 到 gitee 去,这样就可以确保访问了。

所以只需要指定环境变量来初始化项目,问题就能解决了。

1
2
3
4
5
6
# macos & Linux
export NUXI_INIT_REGISTRY="https://gitee.com/hzgotb/nuxt-starter/raw/templates/templates" && npx nuxi@latest init nuxt-blog

# powershell
$env:NUXI_INIT_REGISTRY="https://gitee.com/hzgotb/nuxt-starter/raw/templates/templates"
npx nuxi@latest init nuxt-blog

2023 年度记录

上周末就有了写年度总结的想法,新建文件后用语音写了一大堆,感觉像是在对自己的一些倾诉一样,只能是写给自己看的,于是搁置了一周后选择新建了一个文件,重新写一版可以对外发布的。

今年其实算是一个重要的里程碑,完成了从学生到打工人的身份转变。身份的转变,角色的转变,所处环境的转变,其实都给自己带来了挺多的思考与想法。不过在平时工作时间也不多,很多想法出现在脑子里想了一下,最后可能也没想出个所以然,或者因为没有记录下来而导致可能想出来也忘掉了。干脆就借着年度记录这个机会,写点总结。

告别校园

今年六月,总算是结束了长达十九年的求学生涯。 因为工作在去年就已经尘埃落定签好了三方,所以剩下的这段校园时光也还算挺轻松

  • 一月在家玩耍;
  • 二月回校开始搞毕设剩下的内容;
  • 三月把工作简单写了个专利以满足毕业要求;
  • 四月冲刺毕业论文;
  • 五月完成答辩;
  • 六月和家人毕业旅行和毕业典礼。

总体而言,最后的校园时光平淡而顺利,顺利毕业,拿到学位,找到工作,也算是心满意足了😌

开始工作

毕业离校没休息多久,就开始入职成为一个正式的打工人了。虽然去年实习的时候其实也工作了几个月,但是正式工作和实习还是不太一样的,毕竟实习可以随时跑路,而正式工作就没有退路了,尤其是作为宝贵的应届生身份,再怎样也得混个几年经验再跑去社招。

第一份工作,选了一个服务端的业务开发,还算是比较符合发展方向的。记得入职的第一天和师兄 one one 的时候就说到,要相信做的事情是有意义的。虽然这话听起来不过像是自我鼓励甚至是自我安慰,但是后面在小红书评论区偶然看到,有人发现并用上了我负责新增的 features,还是觉得工作似乎好像确实还是有点意义的。

然而虽然是一个技术开发,但是日常的工作其实大多数时候都在拉通对齐,以及文档分析。最核心的 coding 反而占比不会很多,甚至某种程度上,交流沟通协作的能力相比于技术水平更加重要,尤其是在业务开发。

为什么作为开发写代码反而不多? 一方面是业务系统大多数其实都已经发展得比较成熟,很多内容其实都建设得差不多,对于新的需求往往能够复用已有的链路,即使有变更也只是在这上面进行建设与修补;另一方面就是公司一直在搞提效,而提效的方向和手段,往往就是把一些代码的开发需求通过抽象的方式转变成通过配置的流程编排,将写代码变成可参考复制的配置,然后把这些工作下发给外包完成 ,能提效多少不好说,终归是个数字,但是一线开发也不见能够闲多少

不过这个也可能刚好是我所在团队,所负责应用的原因,但是正如大老板在初次全员会上面说,其实公司并没有义务帮助大家成长,所以还是得靠自己。没有机会就创造机会,机会少就抓住机会,不写代码就看代码,看文档,看完就变成我的了😆

不过干多了业务需求,其实也会反思技术在里面的作用,技术是否真的这么重要,有时感觉业务知识也是很重要,能创造并拉来业务需求,促成交易才是更重要的。空有一套优秀的系统,但是卖不出去没人用,又能发挥出什么价值?就算是垃圾,只要用的人多了占据了足够的市场地位,一样能够劣币驱逐良币。 这样看来还是销售的能力最强!

编程道路

虽然如此,但是我还是真的发自内心地喜欢写代码的,喜欢看与折腾各种有趣的东西。除了工作的东西以外,今年好好学了下 F#, Rust 和 Kotlin。三个语言都是好东西,写起来各种爽,很好地满足了我对于函数式编程的喜爱与追求。

在学习函数式编程的路上,有时候真的会觉得可能是见识不够,对于函数式的写法有点想像力匮乏了,有些时候是真的不知道应该如何用函数式来写,如何用递归来实现迭代。看了别人写的代码,见识过后,才知道原来可以这样写。

今年也第一次全部完成了 Advent of Code 的挑战,虽然中间遇到不少挑战,比如一些没有见过的定理公式,又或者是中途出去团建了三天回来需要狂补进度,但最后还是磕磕绊绊地在圣诞当天完成了所有挑战,点亮所有图案。

虽然好像学了很多东西,好像会很多东西,但是内心其实还是会时不时地感到焦虑,会担心其实我搞这些东西对于我的工作到底有没有帮助,对我的能力有没有提升。如果真的跳槽,是否能够凭借这些技能帮我找到更好的工作?恐怕未必。这样想好像很功利,但是好像也没有办法,也只能不想那么多,先自己玩爽了再说。

接下来学习(玩耍)的方向应该还是会继续聚焦于这三个语言以及函数式编程上面。至于工作相关的系统设计与中间件,可能还是返回到工作中学习吧,如果工作还有时间还没有被压垮的话。

对于金钱

正式工作之后,开始有比较稳定的金钱收入了,如何管理,如何使用,成为了一个重要的命题。以前常常看到对于财富自由的讨论,说实话对于这个目标很向往,而且我也很现实地明确自己打工的意义,就是赚钱。赚钱嘛,不寒碜。

怎样才算是财富自由呢?

前两个月某晚上去跑步路上经过了一个彩票投注站,看到上面写着刮刮乐彩票中奖金额高达 20w,脑海中突然就在幻想自己中了会怎么样。中了 20w,我会怎么花呢?好像这个数额也没有特别大,真的都想不到要买什么,唯一能想到的可能就是买个车了。是不是 20w 太少,如果换到 50w 呢?好像差别也不大,如果换成 500w 呢?脑子里的第一想法居然是好像在深圳也不够买一套房,但其实如果给一个年薪二十万的公务员足够发二十五年工资了。

似乎一次性得到一笔钱根本满足不了自己,相比之下,自己可能更倾向于获得持续稳定的收入。如何能够保持持续稳定的收入呢?说实话互联网行业并不能保证如此稳定,不知道能待多久,也不知道这个行业还能玩多久。因此单靠打工,并不能够自由。可能还是得从其他方便找到搞钱的方法。

还有什么办法能够赚钱呢?积极探索中。

打造形象

从 18 年开始写自己的博客,到现在也已经有 5 年时间了,期间一直在使用 YeungYeah 这个名字和图像,并且逐渐扩建到各个平台当中,也算是初步搭建出一个自己的形象品牌,虽然估计认识的人也不多,不过总算是有个名字了。

为什么需要有个名字?在互联网中,因为种种的原因,与他人建立起这种不同的 connection,是 web 最具有魅力的地方。而在互联网中,能打造出一个个人的品牌形象,积累到足够的 reputation,增添足够的影响力,会更容易与他人建立起 connection。connection 有什么用?具体的说不上,但是对我意味着更多的机会,更多的可能性。

因此今年来开始更加积极地发推看推互动,甚至开始运营我的小红书,写点 coding 技术相关的内容。数据看起来其实也很一般,不过无所谓,也是抱着玩一玩的心态。

今年参加高中同学婚礼时和高中同学聚了一下,了解到他当前正在小红书等社交平台做编程自媒体,做编程辅导和知识付费,感觉是一条不错的搞钱路子。于是质疑自媒体,了解自媒体,成为自媒体。这也是我开始玩小红书的一部分原因。 当然其实也并不意味着我就要当自媒体然后开始流量变现赚钱,对于自己也有很明确的认知,大概率是干不来的。

希望能够有朝一日 from nobody into somebody.

面对 AI

去年也是差不多十二月这个时候,ChatGPT 开发供外部使用。当时我也马上注册了个帐号开始使用,但是使用体验还是很普通,玩了一会就丢一边了。然后过了几个月到三月后,突然就超进化成为人人都在感叹的划时代技术。

其实 AI 技术已经发展几年了,从 CNN 神经网络开始进入 AI 的时代,科研圈中 AI 技术井喷,各种 AI+ 的文章出现,AI 本身也在快速发展。大家都知道有 AI,各家也喜欢在产品中标注 power by XXAI 技术。但也只是拿 AI 的技术用在已有的产品上面。

然后 ChatGPT 的一出现,彻底火出圈了,出圈到连我爸都来问我有没有方法可以用。ChatGPT 确实是个好东西,现在几乎已经成为许多人离不开的必备工具了。我自己也用 ChatGPT API 搞了一个(感谢 ChatGPT-Next-Web)。

ChatGPT 的出现,为许多人带来了许多机会与流量。尽管可能仍然会有人因为种种的原因和观念而不接受甚至抗拒 AI 的发展,但是我觉得这已经是大势所趋,AI 发展无可避免。 未来的 AI 估计将会成为像水电一样的基础资源无处不在,融合进人们的日常生活当中 (这话听起来怎么这么像以前云计算的宣传口号)


今年作为重新开放后的第一年,同时也是身份转变进入社会成为打工人的第一年。于我而言,所有事情都在起步中,所有事情都具有新希望。可能性就在眼前,得抓紧机会,成了就成,不成也无所谓,尽力而为,顺势而为

Cloudflare 应用开发使用小记

能给大众白嫖使用的 serverless 服务,像 Cloudflare 和 Vercel 就是好的服务商。

最近因为各种原因,突然想把在服务器上运行的 rss-timeline 记录(详情可以看这里 -- 我的动态时间线聚合)的程序换成在云服务上面部署,最终选择了 Cloudflare,并且实现了一版。下面是实现过程中一些记录。

worker

cloudflare 通过 worker 的方式提供服务,worker 有两种触发的方式

  • http trigger. 通过发送 http 请求进行触发,也是最常用的一种,实现 fetch 方法处理请求即可
    • 在 ChatGPT API 刚出的时候,很多人都因为 API 地址不能直连,所以搞一个 worker 请求转发就可以了。比如这个
  • cron trigger. 通过定时任务的方式来触发任务。通过配置 cron job 来声明执行的周期。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export default {
 // The scheduled handler is invoked at the interval set in our wrangler.toml's
 // [[triggers]] configuration.
 async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
 // A Cron Trigger can make requests to other endpoints on the Internet,
 // publish to a Queue, query a D1 Database, and much more.
 //
 // We'll keep it simple and make an API call to a Cloudflare API:
 let resp = await fetch('https://api.cloudflare.com/client/v4/ips');
 let wasSuccessful = resp.ok ? 'success' : 'fail';

 // You could store this result in KV, write to a D1 Database, or publish to a Queue.
 // In this template, we'll just log the result:
 console.log(`trigger fired at ${event.cron}: ${wasSuccessful}`)
 },

 // The event handler is invoked whenever an HTTP request is made to your
 // worker. The request will trigger the event handler regardless of the
 // request method or URL path.
 fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
 return new Response(`Hello from the scheduled worker at ${request.url}`);
 }

};

可以通过在配置文件 wrangle.toml 或者 dashboard 中的设置中声明环境变量。环境变量通过传递进来的 env 参数使用。

免费版主要是限制

  • 每次调用的 CPU 执行时间 10ms
    • 官网中介绍用量很少,基本都不会超
    • 但实际跑下来发现好像超了也没事?
  • 每天的请求执行次数 100, 000

本地调试

本地可以通过 wrangle 来进行开发调试和部署

cron worker 可以通过 --test-scheduled 参数,实现 http 调用 http://localhost:8787/__scheduled?cron=*+*+*+*+* 触发 trigger.

有些时候本地访问一些网站可能因为某些奇怪因素访问不了,可以通过 --remote 参数,实现本地运行远程调试,这样就可以突破网络限制访问某些网站。另外也可以通过这个方法直接操作数据库,这样就可以直接在网站就看到数据库的更新情况。

non-standard Node.js runtime

cloudflare worker 本质上其实是 edge function,其实际的运行时并不是正宗的 node 环境,所以一些对于 node api 的使用,是可能会满足不了的。部分到 API cloudflare 的运行时实现了,在自己的代码中可以直接用。但往往通过 npm install 使用的依赖就容易会出现这种兼容性问题。

对于这些有兼容性问题的 npm 库,建议在本地运行时看看实际跑到的代码是否真的用到这些 node api,如果没有,只是在文件当中导入的话,可以注释掉。动态语言 js 的一个爽点就是,就算代码有问题,但只要没有跑到有问题的地方,也还是能跑,就算这样注释掉一个依赖导入,但只要调用的代码没有使用它们,照样能跑。

如果调用的 node api 在 Cloudflare 中有实现的,通过换个方式导入也可以。

1
2
3
4
5
// Do this:
import { Buffer } from 'node:buffer';

// Do not do this:
import { Buffer } from 'buffer';

D1

cloudflare 提供的关系型数据库

创建数据库后可以在 worker 里面添加绑定,直接访问到数据库

1
2
3
4
[[d1_databases]]
binding = "DB" # available in your Worker on env.DB
database_name = "database"
database_id = "database_id"

实际使用起来就是会在入口处传递 env,然后这个数据库的实例也随着 env 传进来,然后就可以手写 sql 进行数据处理了。

数据库操作可以通过

  • 官网网页可以创建表,已经修改行数据
    • 官网也提供一个 console 进行 sql 语句的执行
  • 也可以在命令后通过 wrangle 执行 sql 语句,从而更新数据库
    • 这种方式还可以实现一些表结构的更新,比如增加一列

免费的用量限制

  • 最多创建 10 个数据库
  • 单个数据库 500MB
  • 每月读 5,000,000 行,写 100,000 行

❌
❌