阅读视图

发现新文章,点击刷新页面。

借助AI快速开源了三个小工具: 写代码越来越像做产品了, AI 真把我宠坏了(Vibe Coding)


程序员的未来?Vibe Coding + AI 一起上!

借助 AI 快速开源了三个小工具

最近,我利用 ChatGPT-4o 和 o4-mini 快速开发并开源了三个小工具。起因其实很简单——每次想转换 YAML/JSON 或进行 Base64 编码时,我总是得去 Google 搜索在线工具。时间一久,我不禁开始思考:既然每次都要用,为什么不自己动手实现一个呢?正好我也在学习 React,把这个过程当作练习岂不是一举两得?

于是我开始“扮演”产品经理,向 ChatGPT 提出需求,它很快就为我搭建好了基础框架。包括项目结构、CI 工具(单元测试、ESLint——后来我换成了 Prettier)、README 文档,甚至是 GitHub Actions 上的自动化测试和部署流程,全部一气呵成。

这几个工具都部署在 GitHub Pages 上,开源、带测试、写了文档,虽然小巧,但功能完整,不需要自建服务器就能稳定运行,部署成本几乎为零。

YAML/JSON 转换工具

用于在 YAML 和 JSON 格式之间进行双向转换,支持美化和压缩,简单直观。

yaml-json-converter 借助AI快速开源了三个小工具: 写代码越来越像做产品了, AI 真把我宠坏了(Vibe Coding) ChatGPT (OpenAI) 人工智能 (AI) 小技巧 程序员 程序设计 编程 资讯 软件工程

Yaml/Json数据格式转换React小程序

Github开源库 | Github Pages在线工具

Base64 编码/解码工具

支持文字和文件的编码解码操作,可以将文件内容直接转换为 Base64 字符串,或者反向解析恢复文件。

base64-converter 借助AI快速开源了三个小工具: 写代码越来越像做产品了, AI 真把我宠坏了(Vibe Coding) ChatGPT (OpenAI) 人工智能 (AI) 小技巧 程序员 程序设计 编程 资讯 软件工程

Base64编码解码React小工具

Github开源库 | Github Pages在线工具

ROT47 混淆工具

这是一个轻量级的字符串混淆工具。ROT47 的特点是“加密两次即为原文”,类似于异或操作,适用于简单场景下的信息模糊处理。

rot47-converter 借助AI快速开源了三个小工具: 写代码越来越像做产品了, AI 真把我宠坏了(Vibe Coding) ChatGPT (OpenAI) 人工智能 (AI) 小技巧 程序员 程序设计 编程 资讯 软件工程

ROT47 Cipher文本混淆React小工具

Github开源库 | Github Pages在线工具

Markdown HTML转换

我又弄了一个,这个是把Markdown格式转换成HTML的,转换解释Markdown是基于 marked 库,所以并没有再造轮子。由于HTML转Markdown会比较复杂,这一版本就没有支持。

主要的活都让AI做了(编码/写测试/调CSS),我做的工作就是指导(Prompt Engineering)并把各个零件粘在一起。

markdown-html-converter 借助AI快速开源了三个小工具: 写代码越来越像做产品了, AI 真把我宠坏了(Vibe Coding) ChatGPT (OpenAI) 人工智能 (AI) 小技巧 程序员 程序设计 编程 资讯 软件工程

Markdown/HTML转换工具/React Js WebApp

Github开源库 | Github Pages在线工具

程序员的门槛正在降低

AI 的发展速度太快了,现在生成代码的质量不仅更高,出错率也更低。即便出错,只需将报错信息交还给 AI,它就能分析并修正。程序员的角色正在转变:与其说是手工写代码,不如说是理解需求、与 AI 高效沟通,并拼装整合它给出的解决方案。

对我来说,项目最初的搭建一直是拖延的主要原因。ChatGPT 正好擅长这一环节——自动生成 boilerplate,让我几乎能“秒启动”一个新项目,显著提升了开发效率。

我已经养成每天使用 AI 的习惯,甚至已经很少再使用 Google 搜索。遇到问题,第一反应是直接问 ChatGPT,不仅快,还能一步到位解决问题。

Vibe Coding:AI 驱动的编程方式

Vibe Coding 这个概念今年特别火,核心思想是“用氛围编码”。也就是说,程序员不再执着于每一行代码的细节,而是通过自然语言描述需求,让 AI 来实现代码的生成、重构与调试。

Andrej Karpathy (2025)

TLDR;Vibe 编码是一种新兴的软件构建实践,它用简单的语言描述你想要的内容 – 然后让 AI 处理代码和测试。
TLDR; Vibe coding is the emerging practice of building software by describing what you want in plain language – and letting AI handle the code and test.

我发现自己越来越倾向于这种方式。遇到 bug,第一时间就将错误日志扔给 ChatGPT 处理。虽然效率很高,但也让我逐渐失去了深入思考和调试的乐趣。

现在还有很多支持 Vibe Coding 的工具,比如 Cursor,或者在 VSCode 中配置 AI Agent。你只需告诉它“我要实现什么”,它就能完成代码编写、框架搭建,甚至进行模块拆解和逻辑优化。而你要做的,仅仅是审查和接受它的建议——这不就是产品经理的工作吗?

初级程序员的挑战与机会

AI 工具的普及正在重塑编程的边界,传统“照着教程敲代码”的模式已经远远落后。未来,初级程序员面临的挑战不再是“学不会写代码”,而是“不会用 AI”。

想要不被淘汰,最重要的是掌握如何高效地与 AI 协作,提升自己的系统思考与设计能力。AI 是强大的助手,但人类的洞察、判断和创造,依然无可替代。

未来属于那些善于利用工具、不断精进自己思考能力的人。也许“写代码”的定义正在改变,但“解决问题”的本质永远不会变。

AI大大提高了生产力,执行力和创造力会更加重要。软件工程师需要懂得怎么用AI来干活拧螺丝(开发/调试/解决问题)。感觉程序员的门槛瞬间变得好低,只要懂得看懂AI生成的代码,懂得基本的软件工程就可以了。

ChatGPT 可以拿来做什么?

ChatGPT 通用人工智能

Grok 3

字节/豆包/AI

英文:From Idea to GitHub Pages: Building Tools with AI and Vibe Coding

本文一共 1373 个汉字, 你数一下对不对.
借助AI快速开源了三个小工具: 写代码越来越像做产品了, AI 真把我宠坏了(Vibe Coding). (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c 借助AI快速开源了三个小工具: 写代码越来越像做产品了, AI 真把我宠坏了(Vibe Coding) ChatGPT (OpenAI) 人工智能 (AI) 小技巧 程序员 程序设计 编程 资讯 软件工程
The post 借助AI快速开源了三个小工具: 写代码越来越像做产品了, AI 真把我宠坏了(Vibe Coding) first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. 特朗普加关税的公式竟然是EXCEL里弄的? 这两天中美关税大战越演越烈,据说,特朗普加关税的计算方式竟然是直接在EXCEL电子表格里弄的,具体如下: 其中 I 是 Import,进口;E 是 Export 出口。 优美又实用的公式家族又添新成员 勾股定理: 欧拉恒等式: 牛顿运动定律: 爱因斯坦质能等价公式: 特朗普的“互惠关税”公式:,其中 I...
  2. 测测你的幸运 – Linux Fortune-Teller LINUX 下有很好很好玩的命令,之前已经介绍过: figlet, rig, curl. 现在推荐另一个 命令 fortune 是用来随机显示一段(句)话的.fortune 在英文里就是幸运的意思. 这个命令可以不需要 参数 如果没有 可以通过 apt-get...
  3. 按揭贷款(房贷,车贷) 每月还贷计算器 去年给银行借了17万英镑 买了20万7500英镑的房子, 25年还清. 前2年是定率 Fix Rate 的合同 (年利率2.49%). 每个月大概是还 700多英镑. 有很多种还贷的计算方式, 定率/每月固定 是比较常用的. 简单来说就是 每个月交的钱是...
  4. 推荐一款 CHROME 插件 – Wappalyzer 这个CHROME浏览器插件很方便的可以让你知道当前标签页里的网页 有用到哪些 技术. 你可以用它来检查是否插件正常工作. 1. CLOUD FLARE CDN 2. ADSENSE 3. GOOGLE ANALYTICS 4. Gravatar...
  5. 教娃编程有趣的瞬间 vlog视频集合 教娃700天:写在教娃编程700天, what’s next?。有很多有意思的瞬间记录一下,持续更新。 和孩子一起打闹,学习,成长。 教娃编程孩子有趣的瞬间 vlog(Day 641) 娃说我不够 Senior(Day 701) Bro.. I thought you’re like...
  6. 用 SB2000 的 FBASIC 计算圆周率后80位 英文同步 SB2000 的 FBASIC 是浮点 BASIC 的意思, 就是说可以用在计算一些数值,高级计算机.今天我们还测一下它的计算性能,之前大概知道很慢.. 计算圆周率有很多公式,收敛度不一样, 我们挑一个性价比较高的,比较容易实现的..比如这个: 在SB2000上,80位要算5 小时; 40位1个小时; 20位几分钟, 真得很慢....
  7. 智能手机 HTC One M9 使用测评 虽然我对手机要求不高, 远远没有像追求VPS服务器一样, 但是怎么算来两年内换了四个手机, 先是三星 S4 用了一年多, 然后 Nokia Lumia 635 Windows Phone, 后来又是 BLU, 半年多前换了...
  8. 你要找什么样的老婆? 找媳妇的标准 昨天和网友在剑桥面基, 网友奔现, 他从爱尔兰过来, 小我12岁, 就聊到了找对象的标准. TLDR; 找老婆不要(只)看颜值, 而要注重性格, 为人处事和顾家等更重要的品质, 当然性和谐也很重要. 在当今社会, 人们对于找伴侣的标准有所不同. 有些人认为颜值是最重要的, 因为外貌吸引力可以让人在日常生活中感到愉悦, 这是人的本性,...

是时候在我这里聊一下人工智能了

疯狂的热度也该过了吧。

也是时候冷静的看待这个问题了。

screenshot-downloaded-image

这篇文章写得特别长。懒了,没有拆成多篇文章。


目录


一、个人初接触AI技术

1. waifu2x

我第一次开始接触并使用 AI 技术,应该是 waifu2x 这个图像缩放工具。

确切的说 waifu2x 并不是人工智能技术,只是使用了和目前人工智能技术相同的 「卷积神经网络」

这东西其实不用多介绍了。

这东西实用性能满足日常需求,而且本地部署也只需要不到50MB的空间,方便又整洁。

screenshot_on_b85m_by_flameshot_at_2025-03-18_22-57-18

论清晰度其实 waifu2x 已经很不错了,尤其是平时出的原图的时候不符合各种破视频网站分辨率最低要求的时候,用 waifu2x 糊弄一个高分辨率图,然后再被这些破网站压缩成 320×180 的小图。

唯一想说的就是当时这个技术可是被小鬼滥用到极致,最火爆的期间各种互联网古董图片都被这些小鬼用waifu2x放大之后发到网上,然后大叫“我找到XXX的高清原图了!”。不明真相的人要是跟他讲这是“放大的”,他还会继续跟你解释“这是高清原图!”

你永远叫不醒一个装醒的人。

2. style2paints

接触到的第二个 AI 工具应该是 style2paints 。这是一个线稿上色工具。

最初接触这个软件的时候还是V3版本。

screenshot_EHDsLiyVAAAhjDF

网上的AI线稿上色功能基本已经烂了,但是完成度也是稀烂,可以说根本没法用。

Screenshot_2025-03-18_at_23-06-03

最早那一阵子还因为随便上色了一个线稿,画师直接就把我拉黑了。

screenshot_snap2888-starbreaker

screenshot_snap2930

style2paints 出 V4 的时候我写过这么一篇文章:https://blog.catscarlet.com/202002203604.html

202002203604_play_s2pv4/1046.png

screenshot__ERL3IRbUwAADPz-

后来 style2paints 出完 V4.5 之后,说要出 V5,而且放出了不少预览图,效果很棒,但是再也没有过下文。开发者在 GitHub 上还是活跃状态的,只不过全部都是私有提交了。

网上也再没出现过独立的开源上色工具。各个大厂虽然有各种所谓的线上AI图像处理工具,却基本上没有免费的线稿上色功能。收费的倒是一大堆,但是AI出图的效果本身就稀烂,谁知道输出一次期望中的结果之前得花多少冤枉钱。

3. rife

这算是第三个我使用的AI工具。功能是:补帧

最开始的时候这个项目还是叫做 arXiv2020-RIFE ,后来才改成 ECCV2022-RIFE 的。

但是实际上这玩意放在个人手里意义不是很大。最多就是把互联网早期的GIF表情包改得流畅一点,但是很多情况下 AI 对于补帧和人脑还是两回事。

bilibili:整了个支持Alpha图层GIF动画的放大加补帧转VP9的脚本

在有遮挡性的图像变动的时候,补帧出来的图像就会出错。4.22版本。

screenshot_00000054

(我本地是有更新的模型版本的,但是执行的时候就会出现主机断电的状况。能确定不是电源供电不足,但具体是什么愿意无从得知)

现在这个技术也被滥用了。

  • 网上有一堆原生30fps被补到60fps的动画资源,看着特别的果冻。也有电影,甚至 YouTuBe 上有 60fps 版本的 复仇者联盟1 看着就像游戏过场动画一样。
  • 很多电视台的代理(IPTV)和电视盒上游,会把 30fps 的视频补到 60fps,看着十分的诡异。
  • 最烂的当然还是显卡厂家,直接搞出 DLSS 这种帧生成的恶心玩意。

我自己看这种全局的恶意补帧视频时觉得就挺恶心的,头晕得很快。但是后来发现大多数人都是瞎子,别说补帧了,帧错误亮度错误对比度错误在他们眼珠子里都没有区别的,盯着一个坏掉的显示器看上几个小时愣没察觉到自己显示器是坏的旁边显示器是好的,瞎。

4. 其他

其他的基本上就没有接触过了。最多就是个 rembg 去背景工具,我也用不上。

再往前还有 AlphaGo,谷歌在打完围棋之后跑去打星际争霸,然后难产了好一阵子。最后不了了之,在网上搜谷歌AI打星际的视频,只能搜到一个播放列表,是 ArtosisTV 解说的 AlphaStar vs Serral,但是解说内容是基于非星际争霸玩家的,AI的每个行为以及星际争霸的每个兵种和技能都要解说一遍,中间夹杂着各种暂停和战术解释,节奏超级慢,12分钟的游戏解说了半个多小时,根本看不下去。这其间谷歌的AI就彻底凉了。

那几年 AI 爆火的时候 Python 从个只是底层干活的脚本语言一跃成为 不学 python 你就落伍了 这种骗术主流。我是懒得去学,这玩意个人学了也是屁用没有。AI 这种技术注定要被大厂捏得死死的。


二、爆火的 DeepSeek 把国际 AI 大厂的股票打废了

国产的 DeepSeek 把以 Nvidia 为首的国际AI大厂打废了,这是事实。但理由并不像国内鬣狗自媒体和阿Q人一样,因为鬣狗阿Q的思路就是成功啦崛起啦疯狂嗷嗷叫,谁叫得声音大谁有理。

Nvidia和其他大厂之所以市值蒸发,是因为他们一直声称的 需要更多硬件更多算力扯蛋

确切的说, Nvidia 摆烂也不是有 AI 才开始的。先是在挖矿潮期间摆烂,后又开搞各种与游戏算力不相关的各种糊弄人的技术。其他各厂基本上也是一样,微软在那搞的 onnx 的也是稀烂(之前我参与修复的 rembg 不能正确使用硬件加速的 bug 就是 微软 onnxruntime 和 Nvidia-CUDA 联手造成的,可见其代码是有多烂)。

而一个名不见经传的 DeepSeek 却用了很普通的过时硬件做出来一个捅了行业技术天花板的产品,这直接戳破了 英伟、微软、英特尔 这么多年来一直给市值吹肥皂的核心理念。 算力不是最重要的 不需要什么大厂技术也能做 一直吹牛逼的玩意其实屁都不是 ,这乐子直接打得 Nvidia 的50系显卡一点都不香,微软的 Win12 不知道该怎么塞烂功能,英特尔的 Ultra 系列定位完全翻车。 活该!

至于 DeepSeek 究竟怎么样?看着那个推理过程好似挺有趣的,但其实推理流程过于营销号,复杂一点的问题就开始胡说,网上难搜到的东西就开始瞎编,就和其他 AI 一样。

screenshot_on_b85m_by_flameshot_at_2025-03-19_00-42-48

screenshot_on_b85m_by_flameshot_at_2025-03-19_00-43-22

screenshot_on_b85m_by_flameshot_at_2025-03-19_00-43-43

screenshot_on_b85m_by_flameshot_at_2025-03-19_00-43-48

screenshot_on_b85m_by_flameshot_at_2025-03-19_01-06-00

screenshot_on_b85m_by_flameshot_at_2025-03-19_01-06-10

screenshot_on_b85m_by_flameshot_at_2025-03-19_01-06-17

screenshot_on_b85m_by_flameshot_at_2025-03-19_01-06-28

(部分错误引用和内容均用红框标注)

这表现实在是太像一个初中没毕业结了婚有了娃,娃娃问你一些客观存在但你完全不知道的知识时,就在那瞎引用瞎推理瞎解释,着实一个爹味浓厚的鬣狗阿Q,着实让本来就生活在这种恶劣环境的人反胃。


三、目前的 AI 到底能做些什么?

首先这个得分场景考虑,也就是,AI 这项技术在谁的手里时才有什么用。

1. 大型IT企业

首先一个前提得讲清楚:

目前的 AI 技术就是 给一个算法喂数据养模型,当相应的数据是 由人类创作的 情况下就十分有争议,说难听点就是 抄袭 。编程AI抄代码,绘图AI抄绘画,医疗AI抄病例,这也是目前相当多一部分人反AI的核心理由。

我的个人观点是:

  • 以一定的目的使用特定的数据训练AI并以此谋私利,那就是 抄袭
  • 将整个互联网数据全部用来训练AI,并公开数据模型,供所有人无偿使用,那不叫抄袭,那叫 人类的知识库

很可惜 人类的知识库 在目前的商业环境下实现不了。想使用 将整个互联网数据 的就只有大型IT企业,而目前的这些企业都是清一色的自私自利行业毒瘤,他们的存在多数都是对行业弊大于利,唯一的那点利也知识为了保住他们的自身地位罢了。(举例的话就是微软英特尔之流目前在开源界贡献的代码排在首位,但是他们贡献的全他妈的是他们自家用的驱动代码和调用代码,对整体行业有个屁用啊)

2. 中小型IT行业企业

首先中小型企业很少能有能力自己研究算法,在有限的资金和精力之下无法对行业技术作出突破性的贡献。你也不能怪罪他们,很多公司本身为了能正常运营就已经很努力了。

这才是行业真实现状。他们的产出才是老百姓真正能接触到和获益的。

目前据我能看到的,超市电子秤 可以说是一个不错的例子:传统的大型商超的果蔬摊位基本上都是要有个 负责称重的功能,有些是有专人上秤打标签,有些则是自主称重。现在很多超市都是AI电子秤,把果蔬放上去之后,能直接识别到品名(可能会识别到多种,再人工选择),这个工作量要比纯手选低多了(一个果蔬商超一般都有几十种甚至上百种标签)。当然,具体是如何实现的,我不知道,可能是电子秤公司自己训练模型,也可能傻屌公司每次都得调用一次大厂的云服务进行图像识别。

当然了!

也可能反过来,因为这些企业的领导人可能就不是IT行业出身的,很可能就是个投机人。

那么,答案只有一个了:《Employment for computer programmers in the U.S. has plummeted to its lowest level since 1980—years before the internet existed》

你甚至可以尝试阅读这篇文章的中文AI总结:《财富》:程序员就业降 – 豆包

20250415更新:豆包应该是玩不起了,AI阅读功能被大幅度阉割了,原链接虽然貌似还能用,但是没办法看新链接了。也可以看翻译

事实上国内中小型IT行业企业的产品质量也已经进一步滑坡了,你可以看一下这个 用户端验证时间 的神奇逻辑:《海航888元的随心飞偷跑了!》,基本就是什么人要写个需要验证时间的代码结果直接丢给AI就复制粘贴了,人脑里连产品设计的逻辑基础都没有。

screenshot_on_b85m_by_flameshot_at_2025-03-19_19-59-14

3. 非 IT 行业的企业

首先摆烂的一定不会是普通老百姓。

screenshot_on_b85m_by_flameshot_at_2025-03-12_14-22

https://weibo.com/2645861077/Pi3jJBv1w

普通老百姓摆烂都抢不到热乎的。

更别说还有人人喊打的 AI 客服了。


四、目前的 AI 在个人手中到底能做些什么?

1. 作为问答式搜索引擎

因为目前的AI就是一个特速算法的搜索引擎,所以当搜索引擎用是最基本的功能。尤其是在如今搜索引擎(不论中外)的搜索结果质量都暴跌,很多人都期望能在AI上得到好一些的结果。

只不过AI也是从搜索引擎拿结果的……

screenshot_on_b85m_by_flameshot_at_2025-03-16_20-38-25

啊不是,我要是自己愿意自己逐像素框选的话那我还问你干球?

screenshot_on_b85m_by_flameshot_at_2025-03-17_21-49-05

这是怎么把一个街机游戏识别成 999 和 11eyes 的?还把俩游戏标题合并到一起了?

screenshot_on_b85m_by_flameshot_at_2025-03-21_23-29-24

Jay Smith 的 Bad Romance 与 Lady Gaga 的 Bad Romance 是什么关系?你这 DeepSeek 这是要往哪里推啊?

screenshot_on_b85m_by_flameshot_at_2025-02-25_20-44_1

我已经无语了。

不得不说很多问题有时候答案是烂得不行。

2. 写垃圾文案

简直是官僚主义形式主义的辉煌!废话文学的巅峰!狗屁不通高考满分作文上树!

screenshot_on_b85m_by_flameshot_at_2025-03-22_00-03-52

当然我也找到了点正经有点用的功能,那就是语法检测。

screenshot_on_b85m_by_flameshot_at_2025-03-22_00-20-26

这玩意对于非母语用户还行,就是也很喜欢在有得没得的地方反复胡扯。没有中文版是个遗憾。国内的产品会拿你的文档做训练,所以千万别用。

3. 编程

・项目

作为一个差不多快被社会淘汰的程序员,说实话,我到现在 IDE 还是在用 Pulsar (原 GitHub 被微软收购后被抛弃的 Atom 的社区派生版本)。什么AI插件我都没安装。

我对于 AI 编程不是很了解,我也不知道这玩意现在到底能干啥。反正大家都吹得天花乱坠的。于是找了某之名 AI 编程 IDE ,cursor,在本地试了一下。

我这手里有一些对我来说解决起来很麻烦的问题,就是不是我自己写的旧代码不兼容新环境的问题,工程量太大,业务逻辑完全不知道,我也不想去研究这些项目的具体实现。

我就想着,AI工具既然可以导入项目,那可不可以直接让AI把代码过一遍,然后针对新环境的版本把旧代码中不兼容的地方全找出来改掉。

想得美!现在的AI编程工具和搜索引擎一样,还是问答式的。

试着导入了一个项目,然后 cursor 一直不停的有得没得一直在废话一些毫不相关的东西,对于因新环境和新框架不兼容的问题一个都没发现。

screenshot_on_b85m_by_flameshot_at_2025-03-05_20-14

不得不说,很多人活得非常的 人机

・功能片段

那既然,AI编程这玩意仍然是问答式的,那就干脆继续当搜索引擎用就罢了,根本不需要什么编程插件或IDE编辑器,更不需要什么按量付费编程接口。

事实上,AI在问答式编程输出的结果还挺不错,尤其是在很多 理论上可行且应该有人实现过,但周围所有认识的人都没接触过对应技术 的场景下极为有效,而作为一个腐朽的程序员,我没事就会遇到这种 技术上应该能实现但是我不知道对应技术的入口在哪,而身边完全更是没一个比我更了解响应技术领域的人 ,这时要是在像文中 《我是救兵请来的猴子》 提到过的环境下,至少身边人都会想办法帮你找对应的资源,但是事实上现在国内大部分公司,同事人人自危根本顾不上你,甚至有人巴不得你翻车然后下次被优化的就是你,领导更是觉得「员工遇上瓶颈」乃是天大的 职场PUA 机会,绝对不会放过。

比如 《从豆包下载无水印图片》 这个用户脚本的核心理念是 跨域下载并重命名图片 。本身下载并重命名图片是 HTML5 协议中<a>的的一部分,但是掺合上跨域后就报废了。理论上这是个安全无风险的功能,但是实际上就是做不到,封死了。绕路办法,如果在网上搜,大部分资源都是告诉你使用 <canvas> 对图片进行跨域下载,但实际上却有更好更干净的办法。这个的确在AI中可以搜到,而在传统搜索引擎搜索的时候,搜索结果却会被 <canvas> 的结果淹没。

・繁琐复杂又没技术含量的代码

在编程开发这一块中,有很大一块工作内容都是 繁琐复杂又没技术含量的代码,尤其是前端开发,经常要计算渲染后图像的位置,或者单个元素的样式。这一块基本没人愿意写,但是很多时候不写不行。

这种工作交给 AI 却是非常的巴适。

screenshot_on_b85m_by_flameshot_at_2025-03-22_00-41-37

screenshot_on_b85m_by_flameshot_at_2025-03-15_20-49-15

有些时候国内AI编程的确会给出点惊艳的地方,比如需要临时文件或文件夹的时候知道 mktemp 。微软这种背靠 GitHub 的 Copilot 甚至都只会原地拉屎。

screenshot_on_b85m_by_flameshot_at_2025-03-06_14-18

但是却并不 100% 可靠。

screenshot_on_b85m_by_flameshot_at_2025-03-26_01-36-48

screenshot_on_b85m_by_flameshot_at_2025-03-26_01-38-28

・复杂逻辑的编程

上一点复杂逻辑,AI编程就很容易挖坑。

比如AI会犯最经典 初级程序员错误 ,第二次循环时忘记把标志位重置。

screenshot_on_b85m_by_flameshot_at_2025-03-06_14-23

(原链接找不到了只剩下文件了)

以及基础命令经常想当然,上下文对应不上。

screenshot_on_b85m_by_flameshot_at_2025-03-17_06-36-16

红框位置的 type -f 对应的是 文件 ,但是上下文里写得清清楚楚的是在处理 软链接,应该改使用的是 type -l 才对。

只要上一点逻辑,那出错的情况就特别多。而且由于这代码不是你亲自写的,所以你要重新读一遍所有代码才能理清其逻辑,之后才能找到 Bug 所在。

经典的 编程即是10%的时间写代码,90%的时间找bug,那 10% 才是编程的真正乐趣。而大范围引入AI进行编程后,编程是1%的时间写代码,99%的时间找bug

把编程唯一的快乐送给AI,把屎留给自己吃。

4. 绘画

这一块既是最有争议的一块,亦是我用到的最多的一块。

说来也挺可乐的,程序员在AI领域用到最多的功能不是AI编程……但话又说回来,我自己会编程我为什么还要找一个不靠谱的AI给我添麻烦?没上过班没被同事坑过还是怎么回事?

其实2022年那阵我就接触过 dalle-mini ,然而当时AI绘图基本上只能到这个程度:

screenshot_on_b85m_by_flameshot_at_2022-06-08_15-24-18

screenshot_on_b85m_by_flameshot_at_2022-06-08_14-22-06

screenshot_on_b85m_by_flameshot_at_2022-06-08_14-08-33

screenshot_on_b85m_by_flameshot_at_2022-06-08_14-04-19

怎么说呢,有些输出的确是带着灵魂的,但是整体上一塌糊涂。你可以看到每个输出中都或多或少带着核心细节。

现在AI出图已经今非昔比了。

screenshot-生成特定风格图片-2136008435344130-20250322221645

screenshot-生成特定风格图片-2134056610409986-20250322215006

・尝试本地化

首先当然是尝试在本地部署这玩意。在网上搜了下,基本都在说 comfyui 。这玩意我看了一下,支持 Docker ,那就毫不犹豫部署了。

screenshot_on_b85m_by_flameshot_at_2025-02-19_17-50-18

然而实际使用效果不太乐观。

comfyui 的默认模型出图基本是这样的:

screenshot_on_b85m_by_flameshot_at_2025-02-19_19-56-15

离实际能使用差太多了。

而目前可以在线上无限次数使用的AI生成图像工具,我找到了两个:豆包和百度,他们的出图效果是这样的。

豆包:

screenshot_on_b85m_by_flameshot_at_2025-02-19_20-07-18

百度:

screenshot_on_b85m_by_flameshot_at_2025-02-19_20-06-59

百度英文解析:

screenshot_on_b85m_by_flameshot_at_2025-02-19_20-07-33

(豆包不支持纯英文解析)

百度的出图效果要比豆包差一些,但是在关键字很少的情况下精度比豆包要高,但但是关键字超过一个限度之后百度的精度就急转直下彻底崩了。

我检查了一下,comfyui 应该是需要手动添加模型。但是我尝试了几个例子看起来很不错的模型,在执行到 VAE Decode 时,主机就会直接断电重启。

screenshot_on_b85m_by_flameshot_at_2025-03-12_01-14

我个人认为应该不是电源供电不足的问题,因为在 KSampler 阶段,显卡就已经跑满 200w 了。同样的问题也在 rife 上出现过(前文也提到了),使用 4.22 模型就正常,使用 4.25 模型就会断电重启。虽然我也怀疑是显存不够的问题,但是 comfyui 比较小的模型只有 2G 多就会导致我断电重启,而 rife 4.25 模型与 4.22 模型都只有不足 20M ,这就不太像是显存不够的情况。而且我的显卡是 GTX1080 8G 版本的,实测最多也只用掉 6G 多一点。

只能怀疑是驱动问题了,毕竟 GTX1080 这个老显卡是诞生得比挖矿潮还早。但没有办法了,我之前把系统升级后(从 Linux Mint 20.3升级到 Linux Mint 22.1,基于Ubuntu20.04到Ubuntu24.04),驱动版本还是550,CUDA也只是从12.2升级到了12.4而已,而最新版本貌似是12.8。我也不清楚到底是 GTX1080 只能装这么高,还是有什么其他限制。Nvidia 官网倒是有一个 575版本,Linux Mint官方没说正式支持,我也不想试。毕竟即使跑 comfyui 的默认模型,显卡的噪音都超大,温度直达 70 度以上(风扇57%转速,再提高那噪音就真的难以忍受了),而仍然要等很久才能出一张图。

本地化部署这事只能暂时放弃,等有新电脑新显卡再说吧。

・放大图片工具

出图只能用线上工具,那顺带看看线上工具都有哪些功能,结果发现百度放大图片的功能效果竟比我本地的 waifu2x 要好。

screenshot_on_b85m_by_flameshot_at_2025-02-22_20-26-52

(左图:百度放大。右图:本地 waifu2x 放大)

这我可接受不了。立刻把本地的图片放大工具换成 Real-ESRGAN ,测试一下。

把图片缩小,然后再用 Real-ESRGAN 放大,然后离谱的就来了。

screenshot_on_b85m_by_flameshot_at_2025-02-23_00-11-47

(左图:Real-ESRGAN animevideov3 模型。右图:原图)

animevideov3 不知道为何图片变得有点鲜艳了?

screenshot_on_b85m_by_flameshot_at_2025-02-23_00-12-01

(左图:Real-ESRGAN x4plus 模型。右图:原图)

x4plus 放大后竟然比原图还清晰!太离谱了。

这让我想起了网上传闻的「断崖上未融化的雪块被相机AI补全成坠崖的白色尸体」的乐子了。

・去背景工具

本地化了 rembg ,因为命令行真的太好用了,比起线上工具实在太方便。然而 rembg 的代码质量稀烂,依赖的环境(onnxruntime,cuda-cudnn)更是烂到彻底。为此做了不少工作。我根本就不是个写 Python 的。

反正最后调通了,而且把模型更新到了 birefnet ,效果比默认的 u2net 好非常多。

screenshot_on_b85m_by_flameshot_at_2025-02-20_15-32-10

(左图:u2net 模型。右图:birefnet 模型)

screenshot_on_b85m_by_flameshot_at_2025-02-20_15-32-20

(左图:u2net 模型。右图:birefnet 模型)

・线上出图工具

前文也讲了,我找到的可以在线上无限次数使用的AI生成图像工具有两个:豆包和百度。豆包出图效果比百度强一点。

只不过除了模型比较强大以外,豆包仍然有着所有AI图像生成工具都有的毛病:不听话

screenshot_on_b85m_by_flameshot_at_2025-03-03_11-27

screenshot_on_b85m_by_flameshot_at_2025-03-03_11-28

这完全不日系赛璐璐芳文社好吧,这都给干到韩国去了。

screenshot_on_b85m_by_flameshot_at_2025-03-10_17-12

特别喜欢给妹子穿过膝袜。裤袜搞错就已经很烦了,裤子都给你忽略了。

screenshot-生成动漫风格图片-1804857296308738-20250310150611_realesrgan2x-1

我之前想搞一个倒霉OL妹子的插画想做个有连续剧情的系列,比如妹子加班后下班下大雨,顶着雨去地铁站,结果豆包不会画折叠雨伞,我总不能让妹子淋雨吧?这个就算是情节也实在太不礼貌了,结果发现豆包也不会画「湿衣服」,可乐。

百度却非常的会画「湿衣服」,不仅如此,貌似百度画出来的衣服全都是湿的。

screenshot_on_b85m_by_flameshot_at_2025-03-10_19-00

screenshot_on_b85m_by_flameshot_at_2025-03-12_01-15

发觉百度的输出都特别的色情……

然后是如果遇到AI完全不能理解的内容,出图就特别的诡异。

screenshot_on_b85m_by_flameshot_at_2025-03-09_22-43

screenshot_on_b85m_by_flameshot_at_2025-03-09_20-50

screenshot_on_b85m_by_flameshot_at_2025-03-09_20-50_1

(开车很困难)

有时候干脆就是精神污染……

screenshot_on_b85m_by_flameshot_at_2025-03-12_23-52

不仅如此,线上工具还有敏感词屏蔽词和谐词汇,而且理由也过于欧亨利了。

screenshot_on_b85m_by_flameshot_at_2025-03-12_22-06

screenshot_on_b85m_by_flameshot_at_2025-03-23_23-29-39

这是有多怕有人用AI生成「铁链女」的照片啊?

你就只能靠中国人的聪明才智绕过屏蔽词。

screenshot_on_b85m_by_flameshot_at_2025-03-12_22-08

另一大问题就是让图像上拥有超过1个的关键性事物。

screenshot_on_b85m_by_flameshot_at_2025-03-13_01-39

screenshot_on_b85m_by_flameshot_at_2025-03-13_11-35

一个人拿着一个有特性的事物,或者两个人,就会出错。

貌似这个问题是AI的通病,AI不听话乃是日常,因为人类一句话能讲讲明白一件事情哪个关键词轻哪个关键词重是主观的,两个人能顺利交流那是电波对上了,不能顺利交流那至少一个人看另一个人是傻逼。AI 不一样,AI 没得交流,现在的 AI 只是「训练」出来的。

网上传闻 FLUX 就要靠谱很多,但是本地部署的要求则高得更加离谱。本来在研究这玩意期间发现需要研究的内容就不仅仅是 ComfyUI,还有 LoRA、ControlNet、IP-Adapter 这一堆看着就乱七八糟的玩意就烦了,我又不是专门画图的画师,专门画图的画师也不会用这种完全是另一个领域的工具坑自己。烦了烦了,不研究了。

・线上改图工具

这算是出图工具的子集。

screenshot-赛璐璐风格少女绘制-1843589592669954-20250312154607

screenshot_on_b85m_by_flameshot_at_2025-03-12_15-32

百度的出图效果就巨烂,改图效果更是差劲。事实上百度的那个AI图像编辑废柴得不行。

而豆包则超可笑的只要看到特朗普就生成失败。

最后单独生成了个图片然后用 rembg 去掉背景后再用 GIMP P上去。

screenshot-生成动漫风格图片-1845130723445762-20250312162334

screenshot_-Part-GTY-2202534763-1-1-1_realesrgan2x-1


五、目前我的态度

1. 对个人

我目前个人认为,AI 在个人手里最多只是个大玩具。

讲了这么多工具类的产品,没有多少是能在生产生活中切实获益的。或许像抠背景这种工具在艺术照或者COS照方面能有点用途,或者黑白老照片上色(但是能本地化的上色工具我又没找到)。

可能做创作时可以用 AI 做一些单页插画。但是做漫画就很困难,因为内容的连续性基本无法维持,很容易崩坏。

编程方面,虽然有牛逼吹说有人完全靠AI开发了APP,我觉得那是真吹牛逼。仅是编程环境搭建这一块在任何人面前都是令人生畏的第一座大山,大部分人根本走不到 Hello World 这一步就早早崩溃了。

剩下的就只能用来做坏事了。

2. 对各个企业

・国际

英伟达微软英特尔,仨破玩意早崩早开心。就是不清楚先被 DeepSeek 揭开丑陋真面目后又被川普背后捅刀子的股票崩溃后,到底能对这仨死玩意有多大伤害。

其实我觉得 英伟达 内心是害怕的。当年挖矿潮,捅他刀子的并不是显卡商,而是 ASIC 。我觉得再过一段时间他就得挨真刀子,所以他得把 CUDA 这一块跟 微软 绑得死死的。

screenshot_on_b85m_by_flameshot_at_2025-03-25_23-05-41

screenshot_on_b85m_by_flameshot_at_2025-03-15_20-16-44

screenshot_on_b85m_by_flameshot_at_2025-03-15_20-17-04

screenshot_on_b85m_by_flameshot_at_2025-03-15_20-18-12

(至今我们仍然不知道 ASIC 将如何跨过 AI运行环境 这个门槛)

就是不知道其他大厂到底和英伟达是不是一条心。

screenshot-生成特定风格图片-1932475492982018-20250315210448

而微软的 onnx 烂得曾让我想干脆把看到的 AI 小项目都改写成 腾讯 ncnn 的。这玩意如果要做对比的话:

  • onnxruntime 和 cuda 组合起来的运行环境一般需要 2GB 到 几十GB 的硬盘空间(不包含模型文件),启动速度超级慢,安装各种依赖包很可能搞崩溃你的系统。跨平台安装有完全不同的安装依赖。在日常主机上安装高风险高困难,只有使用 Docker 这种容器才能安全的保证主机不被污染。
  • 腾讯的 ncnn 则完全不需要用户安装任何依赖包,只要操作系统的驱动正确就可以了,整套环境是基于 Vulkan API 实现的,。整个运行环境大概在 几十MB 左右(不包含模型文件),非常的轻量。跨平台只要预编译对应的二进制文件即可。纯绿色。

然而一个巨大的门槛挡住了我,ncnn需要的编程语言基础是C系,对我来说完全是另一个领域,搞不定。

・国内

百度风投 和 DeepSeek 一栋楼。

腾讯有着最好的 ncnn 但是学习门槛略高,而且很明显国际老流氓们都提防着这个山寨王。

阿里?谁他妈的为了个问答系统还要专门安装一个浏览器,甚至产品主页都没适配PC端的桌面浏览器。烧钱疯狂投广告搞得各个平台都跟得了癣病似的。能滚多远滚多远。

screenshot_on_b85m_by_flameshot_at_2025-03-24_00-35-27

国家超算互联网中心?我跟你讲要不是因为秋风于渭水写的这一篇DeepSeek R1 可免费/白嫖网页版一览,我都不知道这玩意存在。这玩意是来搞笑的吧,自己建了一个 DeepSeek 服务但是这鸡巴什么玩意?

screenshot_on_b85m_by_flameshot_at_2025-03-24_00-56-25

screenshot_on_b85m_by_flameshot_at_2025-03-24_00-57-46

互联网的里程碑在这破玩意嘴里被贬成了 在国际上的影响力相对较小


六、结论

个人观点,仅供参考。

这篇文章写到中途的时候发觉内容太多太长。想过从中间拆开,分成多篇文章发布,但想了半天,去他妈的。

The post 是时候在我这里聊一下人工智能了 first appeared on 石樱灯笼博客.

特朗普2.0: 乌克兰的命运由美俄决定, 欧洲被卖了


2025年1月,美国总统唐纳德·特朗普第二次入主白宫,上任没多久就兑现了一个竞选承诺:结束俄乌战争。不过,这场停战的达成方式,恐怕让不少人大跌眼镜——谈判桌上只有美国和俄罗斯,乌克兰这个直接当事国没份儿,欧洲那些出了力气的盟友也没捞到一张椅子。结果呢?俄美两国一拍即合,停战协议新鲜出炉:土地归俄罗斯,乌克兰的5000亿矿场开发权打包送给美国,债务甩给欧洲,至于荣耀嘛,留给乌克兰自己慢慢品味。

这协议一公布,乌克兰总统泽连斯基的脸估计比冬天还冷。他对外抱怨说,美国其实只给了670亿美元援助,压根不到之前吹嘘的1500亿。可协议上白纸黑字,5000亿矿场开发权已经划给了美国。他试图争取点什么,比如让乌克兰上桌谈条件,至少要求美国保障乌克兰的安全,再不济也给欧洲分一杯羹——毕竟欧洲这些年没少支援战争。可特朗普的回应简单粗暴:签,或者不签,协议一个字都别想改。泽连斯基还想退一步谈谈,特朗普直接笑他是个“小丑”,说当年演喜剧的水平不错,现在支持率却跌到4%,再不同意就赶紧大选,换个听话的上台。

这事儿说白了就是“弱国无外交”的教科书案例。乌克兰夹在俄美之间,既没实力还牌面,又被盟友卖了个干净。欧洲这边也好不到哪儿去,一盘散沙的他们只能眼睁睁看着美国独吞好处,自己还得背上债务的锅。特朗普上台后更是摆明了态度:拜登那摊子事儿(民主党?左派政策?)他不管,他只关心自己的利益。甚至还放话让乌克兰赶紧还钱,不然“你的国家可能就不存在了”。

这场停战,看似结束了炮火,实则暴露了大国博弈的冷酷逻辑。俄罗斯拿回了土地,美国吃下了资源,欧洲当了冤大头,乌克兰则成了最大的输家。泽连斯基或许还想挣扎,但现实已经给了答案——在强国面前,弱国的声音,连上桌的资格都没有。

懂王骂泽连斯基

trump-diss-zelenskyy 特朗普2.0: 乌克兰的命运由美俄决定, 欧洲被卖了 见闻 资讯

特朗普对着乌克兰总统泽连斯基一顿骂,说他就是个喜剧演员,战争就是他的错。

想想看,一个只是勉强算得上成功的喜剧演员——弗拉基米尔·泽连斯基,竟然让美国花费了 3500 亿美元,卷入了一场不可能赢得的战争——一场本不该开始的战争,而这场战争,如果没有美国和“特朗普”,他永远无法解决。

美国比欧洲多花了 2000 亿美元,而欧洲的钱是有保障的,而美国却什么都拿不回来。为什么“瞌睡乔”拜登不要求公平呢?这场战争对欧洲来说比对我们重要得多——我们之间隔着一片广阔美丽的海洋。

除此之外,泽连斯基还承认,我们送给他的一半钱“不翼而飞”。他拒绝举行选举,在乌克兰的民调支持率极低,他唯一擅长的事情就是把拜登“耍得团团转”。

一个没有选举的独裁者,泽连斯基最好快点行动,否则他很快就不会再有国家可言。与此同时,我们正在成功地谈判结束与俄罗斯的战争,所有人都承认,只有“特朗普”和特朗普政府能做到这一点。拜登从未尝试过,欧洲也未能带来和平,而泽连斯基可能只是想让“提款机”继续运转。

我热爱乌克兰,但泽连斯基做得一团糟,他的国家满目疮痍,数百万人不必要地死去——一切仍在继续……

打了三年的俄战争终于要结束了

2025年2月,俄乌战争突然迎来了戏剧性的转折。 这次不是乌克兰的反攻,也不是欧洲的调停,而是美国和俄罗斯直接达成了一份停战协议——乌克兰和欧洲竟然都没有上桌谈判。

美国与俄罗斯的交易:乌克兰被“交易”了?

特朗普上台后,承诺迅速结束俄乌战争。他确实做到了——但过程让全世界震惊。美国与俄罗斯私下谈判,直接敲定停战条件:

  • 乌克兰割让土地归俄罗斯,不再提收复失地的问题;
  • 乌克兰5000亿的矿产资源开发权归美国,但美国只承认给了670亿(泽连斯基声称应是1500亿);
  • 乌克兰的战争债务全部归欧洲承担,无论是军援、贷款还是重建费用;
  • 乌克兰获得”荣耀”——至少官方叙述上它是“为自由而战的英雄”。

至于乌克兰总统泽连斯基,他被要求要么签字,要么下台。他想在协议上稍作修改,例如希望美国提供安全保障,并让欧洲分一部分矿产资源(毕竟欧洲为战争付出了最多),但美国冷冷地拒绝了:“你只能签,不能改。”

特朗普甚至笑称泽连斯基仍然是个喜剧演员,选民对他的支持率已经跌到了可怜的4%,如果不签,那就换一个愿意签的人来做总统。

欧洲被美国出卖了

这次谈判不仅乌克兰被边缘化,连欧洲也被狠狠地“卖”了一次。战争期间,欧洲投入了大量的经济和军事援助,但现在,美国和俄罗斯一纸协议就决定了战后秩序,欧洲的声音完全被忽视。

曾经自诩为“民主价值捍卫者”的欧盟,现在像是一盘散沙,除了抗议之外,根本没有任何实质性的影响力。德国、法国等国家想争取权益,但美国一句话就让他们哑口无言:“你们要么接受,要么自己去和俄罗斯谈。”

特朗普的新外交格局:美国优先,乌克兰自生自灭

MAGA: Make American Great Again,让老美再次伟大!

对于特朗普而言,拜登时期的乌克兰政策是个**“无底洞”**,他上台后第一件事就是要求乌克兰偿还债务:“如果不还钱,乌克兰这个国家可能就不会存在了。”

他不在乎乌克兰的独立与安全,也不关心欧洲的利益,他要的只是美国的回报。而这个协议,正是“美国优先”的最好体现:

  • 俄罗斯得到了领土;
  • 美国拿到了资源;
  • 欧洲付出了代价;欧洲要完,俄乌冲突,欧洲就属于打肿脸充胖子那种,这几年啥都贵,一大原因就是欧洲不买俄罗斯的天然气,制造业成本就上去了。
  • 乌克兰的未来被决定了,但没有发言权。
  • 弱国无外交,乌克兰和欧洲的尴尬现实

这一切,仿佛是历史的重演。乌克兰被迫接受现实,欧洲再次成为大国博弈的牺牲品,而美国继续按照自己的节奏推进全球战略。特朗普成功地兑现了他“终结战争”的承诺,但代价是乌克兰的主权与欧洲的信誉。

这场战争的真正赢家,似乎只剩下美国和俄罗斯。美国可谓是赢的盆满钵满,远离战场,又卖武器。俄罗斯也赢得了比战前更多的土地,也是个大赢家,输家只有乌克兰和欧洲。

乌克兰自废武功

乌克兰把自己的命运寄托于美国是错误的,做美国的敌人是危险的,做美国的盟友是致命的。

1991年苏联解体后,乌克兰继承了约1900枚战略核弹头,成为当时世界第三大核武国家。然而,乌克兰在1994年12月5日签署了《布达佩斯安全保障备忘录》,同意放弃核武器,以无核国家身份加入《不扩散核武器条约》。作为回报,美国、俄罗斯和英国承诺尊重乌克兰的主权和领土完整。然而,近年来,乌克兰的安全形势引发了对当初弃核决定的反思。

特朗普2.0的开局,不可谓不“精彩”。接下来,他会怎么玩这盘棋?欧洲会不会咽下这口气?乌克兰又能不能翻身?时间会告诉我们答案。

朝鲜黑客盗取世界第二大交易所ByBit 15亿美元的ETH。

happy-time-trump-elon-putin-and 特朗普2.0: 乌克兰的命运由美俄决定, 欧洲被卖了 见闻 资讯

美俄朝鲜最近都很Happy

新闻/实事/经济

本文一共 2326 个汉字, 你数一下对不对.
特朗普2.0: 乌克兰的命运由美俄决定, 欧洲被卖了. (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c 特朗普2.0: 乌克兰的命运由美俄决定, 欧洲被卖了 见闻 资讯
The post 特朗普2.0: 乌克兰的命运由美俄决定, 欧洲被卖了 first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. 智能手机 HTC One M9 使用测评 虽然我对手机要求不高, 远远没有像追求VPS服务器一样, 但是怎么算来两年内换了四个手机, 先是三星 S4 用了一年多, 然后 Nokia Lumia 635 Windows Phone, 后来又是 BLU, 半年多前换了...
  2. 按揭贷款(房贷,车贷) 每月还贷计算器 去年给银行借了17万英镑 买了20万7500英镑的房子, 25年还清. 前2年是定率 Fix Rate 的合同 (年利率2.49%). 每个月大概是还 700多英镑. 有很多种还贷的计算方式, 定率/每月固定 是比较常用的. 简单来说就是 每个月交的钱是...
  3. 在英国给孩子换学校的经历: 孩子离开了村里的小学 由于搬了家, 孩子上学得提前半小时出门了, 因为早上堵, 也得开车半小时才能到. 之前在 Fen Drayton 村庄上小学, 早上8:45学校门开, 9点敲钟孩子排队依次进入教室, 我们由于在村里, 只需要提前5分钟出门和孩子一起走路就可以了. 现在一下子早上变得很匆忙, 得叫孩子起床, 做早饭,...
  4. 同一台服务器上多个WORDPRESS站点的一些设置可以移出去 我自从把所有网站都挪到一处VPS服务器上 就发现很多事情省事很多 可以同时管理多个网站 包括 WORDPRESS博客. 比如我有四个WORDPRESS博客 然后我就把通用的一些资料给移出去 移到 HTTP或者HTTPS都不能直接访问的文件夹里这样就更安全许多. 文件 wp-conn.php 存储了 相同的数据库资料. 1 2...
  5. 公司请的专业摄影师 公司来了新的CEO管理之后,很多事情都不一样了, 特别是一些公司对外形象的事情就特别的在意, 比如公司网站用上SSL.现在公司还有空闲的位置,请速来(钱多人不傻). 一月份出差回LUTON,刚好公司请来摄影师给高层管理照像放网站上的,于是我也凑了凑热闹(但是却还不够资格被放在公司网站上),不过没关系,放这里也差不多. 人到中年, 沧桑感强了些. 更新更新: 同事用他NB的单反给谢菲尔得办公室的人也拍了一组这样的照片.看起来很不错, 很专业,灯光,道具应有尽有.我已经用在了LINKEDIN页面上,立马高大上. 本文一共 230 个汉字, 你数一下对不对. 公司请的专业摄影师. (AMP...
  6. 力扣 Leetcode 的刷题利器: 在线调试器和自动代码提示完成 力扣代码调试器 Debugger 最近 leetcode 刷题网站出了一个在线调试器. 个人感觉非常好用. 因为我平时是用 IPAD+蓝牙键盘来刷题, 而在 ipad 上是没有集成的IDE的, 对于调试来说, 只能很原始的让函数退出一个值, 然后尝试不同的输入来发现问题. leetcode在线调试器的好处...
  7. 比特币最近波动有点大: 一天牛市一天熊 比特币10万美金以内都是最后上车的机会! 比特币近期的价格波动可以归因于多个关键因素,包括地缘政治动态、监管变化以及加密行业内的重大安全事件。其中一个主要影响因素是美国前总统唐纳德·特朗普对乌克兰和加密货币监管的立场变化。据报道,特朗普再次当选,他可能会推动减少美国对乌克兰的支持,这可能会影响全球金融市场和风险偏好。同时,特朗普正在将自己塑造为亲加密货币的候选人,表示有意让美国成为一个更加友好的加密货币环境。这一立场引发了市场对监管政策可能发生变化的猜测,导致市场情绪在乐观和不确定性之间波动。 特朗普对俄乌战争的态度 美国第43届总统唐纳德·特朗普已经在2025年1月当选并正式上任(第二次),那么他的政策可能会对比特币价格的波动产生更加直接和显著的影响。他政府对乌克兰和加密货币监管的立场已经不再是猜测,而是正在实际塑造市场的关键力量。 特朗普(Donald Trump)减少美国对乌克兰的支持,全球投资者可能会预期地缘政治稳定性发生变化,从而增加对比特币作为避险资产的需求。同时,他的亲加密货币立场可能正在推动市场的乐观情绪。如果他的政府推出有利于加密行业的监管政策,例如明确的合规指南或减少监管审查,可能会吸引更多机构投资者进入市场,并促进更广泛的加密货币采用。然而,政策的快速变化也可能导致短期市场剧烈波动,因为市场需要时间来消化新的政策动向。 朝鲜黑客盗取Bybit交易所15亿美元的ETH 另一个显著影响比特币价格的事件是近期涉及朝鲜黑客组织“Lazarus”的15亿美元以太坊被盗案件。据报道,Bybit交易所(全球第二)这些被盗的ETH已经被清洗,此次大规模黑客攻击引发了人们对加密行业安全性的担忧。此类安全事件不仅会削弱投资者信心,还可能引发更严格的监管审查,导致短期市场动荡。此外,被盗资金的大规模流动和出售可能对市场流动性造成冲击,进一步加大价格波动。随着这些事件的持续发酵,比特币价格正受到政治决策、监管预期以及安全挑战等多重因素的影响。 与此同时,与朝鲜黑客组织 Lazarus 相关的 15 亿美元以太坊被盗事件仍在影响加密市场。由于这些被盗 ETH 已被清洗,人们对加密行业安全漏洞的担忧持续存在,同时也可能引发更严格的监管审查。政治、监管和安全等多重因素交织在一起,共同导致了比特币近期的剧烈价格波动。...
  8. 优化设计 个人主页 并且 PageSpeed Insights 双项 100分 坛子的个人主页 www.tanzhijun.com 不错 很适合个人主页的模板. 而且是手机友好. 于是我照着把 我的主页改了改. https://steakovercooked.com 并且做了几点修改: 0. 使用 google mod_pagespeed 把 JS,...

Delphi编程语言三十周年了!


delphi-30-years Delphi编程语言三十周年了! Delphi / Turbo Pascal 程序设计 计算机 资讯

Delphi最初的版本, Delphi被称为是VB Killer

知道Delphi这个编程语言的估计暴露年龄了。很多人以为Delphi这门程序已经挂了,但实际上没有:IsDelphiDead.com

我最后一次用Delphi是2018年,在我离开一个剑桥初创企业,当时我从2009年到2018年,用Delphi写了超过40万行Delphi代码。其中包括了一些C++还有WIN32内链汇编。

Delphi 30周年纪念日

2025年,Delphi迎来了其发布30周年纪念日。自1995年首次亮相以来,Delphi凭借其高效、稳定的特性,成为众多开发者的首选工具。在这30年间,Delphi经历了辉煌、低谷,如今在全球范围内依然拥有忠实的用户群体。

Delphi的诞生与辉煌

Delphi最初由Borland公司于1995年推出,作为Windows平台下的快速应用程序开发工具(RAD),其前身是DOS时代盛行的“Borland Turbo Pascal”。Delphi以其可视化组件库(VCL)和高效的编译器,使开发者能够快速构建复杂的应用程序。在1999年发布的Delphi 5版本中,Delphi达到了用户数量的巅峰,一度超越了Visual Studio,成为开发者的首选工具。

挑战与低谷

然而,好景不长。在推出Delphi 7之后,Borland公司因内部管理问题和市场竞争激烈,逐渐走向衰败。Delphi的创始人Anders Hejlsberg离开公司,转投微软并开发了C#语言,这对Delphi造成了沉重打击。同时,Java和.NET等新技术的兴起,进一步挤压了Delphi的市场空间。高昂的定价策略也使得许多开发者转向其他工具,Delphi的用户群体急剧萎缩。

重生与发展

尽管经历了低谷,Delphi并未消失。在被Embarcadero公司收购后,Delphi不断进行技术革新,推出了支持多平台开发的版本,如Delphi XE系列。这些版本支持Windows、Android、iOS和Linux等平台,满足了现代开发的需求。在全球范围内,Delphi在巴西、中国、德国、俄罗斯和非洲等地仍然拥有广泛的用户基础。特别是在中国,Delphi在教育领域和企业级应用中依然发挥着重要作用。

展望未来

随着技术的不断发展,Delphi也在积极适应新的趋势。从最初的单一平台开发工具,到如今支持多平台、多设备的综合性开发环境,Delphi始终在追求创新。在未来,Delphi将继续优化其跨平台能力,提升开发效率,为全球开发者提供更强大的支持。

在这30周年之际,让我们向Delphi致敬,感谢它为全球开发者带来的卓越贡献。期待Delphi在未来继续引领技术潮流,为开发者创造更多可能。

聪明的程序员用Delphi

“真正的程序员用C++,聪明的程序员用Delphi”这句话曾在IT界广为流传,体现出Delphi简单、高效等优点。 Delphi最初的版本, Delphi被称为是VB Killer, 在Windows下可以快速开发Native的WIN32/64代码。代码执行效率高因为是编译(Compiled)的而不像VB解释式的(Interpreted)。

Delphi作为Windows平台下的快速应用程序开发工具(RAD:Rapid Application Development),以其简单、高效和功能强大的特点,深受开发者喜爱。与C++相比,Delphi更易于上手,开发效率更高;而与Visual Basic相比,Delphi功能更为强大。因此,使用Delphi的程序员被认为是“聪明的程序员”。

聪明的程序员用Delphi

Delphi(VB Killer)是一种编程语言,它是由Borland公司于1995年推出的。Delphi是一种基于Pascal的高级编程语言,它是一种面向对象的编程语言,它是一种结构化编程语言,它是一种事件驱动的编程语言。也被称为Object Pascal。

英文:Delphi is 30 Years Old!

本文一共 922 个汉字, 你数一下对不对.
Delphi编程语言三十周年了!. (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c Delphi编程语言三十周年了! Delphi / Turbo Pascal 程序设计 计算机 资讯
The post Delphi编程语言三十周年了! first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. 假期送娃去管弦乐队速成班 (Holiday Orchestra) 两个孩子除了学习钢琴外,还各自学习了第二种乐器:老大学的是大提琴,老二学的是小提琴。这个圣诞节假期,我们为他们报名参加了为期4天的管弦乐队速成班,从早上9点半到下午4点半,中间休息一小时,总费用为229英镑,第二个孩子享受了折扣。 在4天的速成班中,孩子们第一天觉得非常累,特别是下午的2小时以上的排练,他们不停地拉琴,手感非常酸。刚开始他们看谱较慢,一不小心就错过了几个音符,但他们都坚持下来,并在最后一天进行了完美的表演。 管弦乐队分为Strings和Bands等部分。其中,Strings包括大提琴、小提琴和BASS(一种体型较大的乐器,外形类似大提琴)。Bands则包括各种吹奏的管乐器。要加入Upper Strings,需要达到或超过五级水平,而Lower Strings则是面向未达到五级的孩子。因此,参加考级不仅是对学习音乐的一种监督,也能解锁更多的可能性。 在表演当天,孩子们穿着帅气,这是他们的高光时刻。唯一的遗憾是我们坐在正中间,但孩子们被其他孩子挡住了,所以拍照和录像的角度都不理想。表演的厅很大,下次我们应该尝试到二楼从上往下拍摄。 PS:照片是我用单反(加能 EOS 6D Mark II 加大三白70-200mm中长焦红圈镜头拍的) 本文一共 443 个汉字,...
  2. 高大上 CloudFlare Pro CloudFlare CDN 的免费帐号已经很不错了 很多免费的功能, 最主要是 可以无限添加站点 每个站点都可以用上免费的 Universal SSL 还提供网站节流 安全 提速等功能. 往往只需要1个按键就可以 对于大部分用户来说是非常有用和省心的. 我又败家了...
  3. 智能手机 HTC One M9 使用测评 虽然我对手机要求不高, 远远没有像追求VPS服务器一样, 但是怎么算来两年内换了四个手机, 先是三星 S4 用了一年多, 然后 Nokia Lumia 635 Windows Phone, 后来又是 BLU, 半年多前换了...
  4. 按揭贷款(房贷,车贷) 每月还贷计算器 去年给银行借了17万英镑 买了20万7500英镑的房子, 25年还清. 前2年是定率 Fix Rate 的合同 (年利率2.49%). 每个月大概是还 700多英镑. 有很多种还贷的计算方式, 定率/每月固定 是比较常用的. 简单来说就是 每个月交的钱是...
  5. 微博帐号被盗 昨天想上微博 才发现 密码怎么都不对 密码不可能记错的 就那几个试来试去没一个对的. 本以为是系统暂时的问题 今天 还是这样于是就用忘记密码找回登陆信息. 改了一个比较复杂的密码. 登陆上 发现 有一私信: 果真 帐号被盗了 也许是暴力破解...
  6. bidvertiser的广告收入: 2年10美元 2013年申请了一个 bidvertiser 的广告 类似 google adsense, 不过是 Pay Per Click 按次点击付费的. 这个月收到了最低付费标准的 10美元 Paypal 并没有扣手续费....
  7. 老婆代购 – 畅购英伦 更新: 全新的 代购网站 畅购英伦 上线了! 求推广, 求转发, 求收藏! 多多使用 多多提建议! 我因为懒 而且还有其它原因 是不愿意做代购的. 我老婆最近在计划做代购的事情 因为老大下个月就上幼儿园了...
  8. 快通过 PAYPAL ME 捐点给我吧 PAYPAL在西方国家 很是流行 国内的山寨PAYPAL就是支付宝. PAYPAL 的好处就是 在英国和银行转帐 免费的. 转帐的速度很快. 几乎是几秒就转到了我的HALIFAX银行. PAYPAL和EBAY还真是一伙的 之前在EBAY上卖 需要交 10%的费用 PAYPAL费用 还有EBAY的上架费....

如何轻松创建并托管你的 GitHub Pages 站点 (无服务器静态应用)


创建一个 GitHub Pages 站点是一个简单的过程,可以免费为你的个人、项目或组织创建网站/博客。按照此指南开始。

步骤 1:创建 GitHub 仓库

  • 登录你的 GitHub 账户。
  • 点击“新建”来创建仓库。
  • 对于个人站点,命名为 .github.io。
  • 对于项目站点,使用任何有效的名称。

步骤 2:添加你的站点文件

使用以下命令克隆仓库到本地机器:

git clone https://github.com/<username>/<repository-name>.git

创建一个 index.html 文件,包含你想要的内容。这里是一个例子:

<!DOCTYPE html>  
<title>我的 GitHub 页面</title>
<h1>欢迎访问我的站点</h1>
<p>这是我的第一个 GitHub Pages 站点。</p>

或者,你可以使用 README.md(Markdown)作为首页。

## 我的 Github 页面
### 欢迎访问我的站点
THis is my first Github Pages site.

将你的更改提交并推送到 GitHub:

git add .
git commit -m "Initial commit"
git push origin main
how-to-setup-github-pages-static-apps 如何轻松创建并托管你的 GitHub Pages 站点 (无服务器静态应用) Github 学习笔记 小技巧 程序设计 网站信息与统计 计算机 计算机

Github 上启用 Github Pages 的步骤

步骤 3:启用 GitHub Pages

  • 转到你的仓库设置。
  • 在侧边栏点击“Pages”。
  • 在“源”下选择分支(例如,main)和文件夹(如果适用)。
  • 点击“保存”。

步骤 4:查看你的站点

  • 对于个人站点:访问 https://<username>.github.io/。
  • 对于项目站点:访问 https://<username>.github.io/<repository-name>/。

步骤 5:自定义你的站点

要添加主题,请转到 Pages 设置并选择“选择一个主题”。

你还可以上传额外的 HTML、CSS 和 JavaScript 文件以进行进一步的自定义。

为什么 GitHub Pages 是“无服务器静态应用” / Serverless Apps

GitHub Pages 作为无服务器静态应用运行,因为它们直接向用户提供预构建的静态 HTML、CSS 和 JavaScript 文件,而不依赖后端服务器或运行时动态内容生成。相反,这些文件托管在 GitHub 的全球内容分发网络(CDN)上,确保快速有效的交付。

优点:

  • GitHub 提供的免费托管。
  • 不需要服务器维护。
  • 由于 CDN 分布,加载速度快。
  • 简化了静态站点的部署和扩展。

缺点:

  • 仅限于静态内容;不支持像数据库这样的服务器端功能。
  • 高级工作流的自定义需要技术知识。
  • 依赖于 GitHub 的基础设施。

GitHub Pages 的总结

GitHub Pages 是一个令人赞叹的免费托管网站的工具。只需几步,你就可以为你的项目、作品集或个人使用创建一个站点。通过利用无服务器模型,你可以构建轻量、高效且维护最小的站点。

英文:How to Setup and Create GitHub Pages (Serverless Static Apps)

本文一共 587 个汉字, 你数一下对不对.
如何轻松创建并托管你的 GitHub Pages 站点 (无服务器静态应用). (AMP 移动加速版本)

扫描二维码,分享本文到微信朋友圈
75a5a60b9cac61e5c8c71a96e17f2d9c 如何轻松创建并托管你的 GitHub Pages 站点 (无服务器静态应用) Github 学习笔记 小技巧 程序设计 网站信息与统计 计算机 计算机
The post 如何轻松创建并托管你的 GitHub Pages 站点 (无服务器静态应用) first appeared on 小赖子的英国生活和资讯.

相关文章:

  1. 英国房子的EPC节能报告(Energe/Efficiency Performance Certificate) EPC (Energe/Efficiency Performance Certificate) 是英国房子的节能报告, 法律上规定, 每个房子都必须要有一个EPC报告, 报告的有效期为十年. 房东在把房子出租或者想卖房的时候, 这个EPC就必须有效, 在一些情况下 比如出租房子的时候, 这个EPC报告还必须符合一些最低标准, 比如房子必须满足 F档(类似及格线)...
  2. 按揭贷款(房贷,车贷) 每月还贷计算器 去年给银行借了17万英镑 买了20万7500英镑的房子, 25年还清. 前2年是定率 Fix Rate 的合同 (年利率2.49%). 每个月大概是还 700多英镑. 有很多种还贷的计算方式, 定率/每月固定 是比较常用的. 简单来说就是 每个月交的钱是...
  3. 最好的发财策略就是忘记它 在群里我和 @nationalpark 说到我用STEEM换了3000多个 YOYOW的时候我还有点沾沾自喜, 谁知道他一针见血: 听说你是steem换的我顿时心理平衡了 才突然想起来, 我半个月前是拿 500 个 STEEM 换了 将近3300个YOYOW. 今天 STEEM的价格8美元,...
  4. 智能手机 HTC One M9 使用测评 虽然我对手机要求不高, 远远没有像追求VPS服务器一样, 但是怎么算来两年内换了四个手机, 先是三星 S4 用了一年多, 然后 Nokia Lumia 635 Windows Phone, 后来又是 BLU, 半年多前换了...
  5. 英国TESCO超市有偿回收瓶子 前不久, 英国剑桥 Bar Hill 的的 TESCO乐购超市推出了一个回收瓶子的机器, 这机器就在超市入入口出. 只需要把瓶子放进去, 机器自动识别, 然后每个瓶子10P也就是相当于1块人民币. 完成后会给个小票, 买东西可以拿来当折扣用. 如果嫌麻烦, 在回收完成还可以选择捐给慈善机构. 机器非常人性化,...
  6. 面试刷题更像是一种服从性测试 在许多求职者看来,面试中的刷题环节似乎是对技术能力的直接考察。然而,从另一个角度来看,这一过程或许更像是一场服从性测试。 想象一下,当一个公司要求候选人解决复杂的算法问题或设计一个系统架构时,真正的目的并不仅仅是判断候选人是否能完成这项任务。许多时候,公司更关心的是你是否愿意花时间和精力去迎合他们的流程和标准。 为什么这样说呢?从实际工作中来看,绝大多数岗位并不需要员工每天都在解决高难度的算法问题或从零设计分布式系统。相反,工作中的任务更偏向于理解业务逻辑、维护现有系统以及团队协作。那么,为什么刷题依然是很多公司青睐的筛选方式? 答案或许在于,刷题能有效筛选出那些愿意投入大量时间准备,并在面试中展现高度配合精神的候选人。这种配合精神,是公司希望在未来的工作中看到的品质。一旦你通过了这些测试,公司就会认为你是一个值得信赖的员工,一个能遵从流程、适应规则并在需要时投入额外努力的人。 还有就是以前说的:面试刷力扣是大公司过滤的成本较低的方法之一:为啥有名的IT公司的程序员面试都这么难?,可以过滤掉大部分 False Positive,当然也有可能过滤掉人才(胜任工作的求职者)。不过对于大厂来说,招进来一个False Positive的损害要远远比错过一个False Negative大。 此外,随着AI技术的飞速发展,刷题的技术价值正在逐渐降低。像ChatGPT这样的AI工具,已经可以轻松解决LeetCode或Codeforces上的许多高难度问题,并给出清晰的解题过程。这表明,算法题不再是人类独有的技术优势,而是可以被高效地自动化处理的任务。这种变化进一步凸显了刷题在实际工作中的局限性,因为日常工作更多依赖于沟通、业务理解和对系统的长期维护,而不是短时间内解决某个抽象问题。 OpenAI推出o3大语言AI模型。 Codeforces 评分:2727——相当于全球人类程序员编码竞赛中,排名第 175 位。...
  7. 英国 NatWest 银行推出 Reward Current Account 用来交水电费等帐单可以省钱 英国 NatWest 银行推出 Reward Current Account 银行帐号, 每个月需要交会费 3 英镑, 一年则需要36英镑 并不便宜, 但是好处是 只要通过该帐号以 Direct...
  8. 离开 NPower – 在英国选择便宜的电气公司 extraenergy 在彻底离开 谢菲尔得的那一天 和房东一起读了 电表, 公寓里只用电 没有媒气 所以做饭也用电 特别贵. 虽然在 13年底装了 两个电表. 白天一个 晚上 12点到早上7点另一个表, 所以两个计费....

尝试升级 WordPress 版本 和 PHP 版本,然后失败了

尝试把 WordPress 从 5.2 版本 升级到 6.6 版本,把 PHP 从 7.4 版本升级到 8.3 版本。

任务艰巨得完成不了。

非常的崩溃。


前提

我现在的线上的博客,以及线下的开发环境,以及(如果还在不为我知的某个角落存在的话)商用用环境,都是 PHP 7.4 的。

最早些时候,应该算是2015年,正式入坑 PHP ,那时候基本是公司用啥,我用啥。PHP 5.2 5.4 5.6 都用过了,尤其是 5.2 大坑一大堆,摔过很多次。然后到 2016 年的时候终于用上了革新的版本 PHP 7.0。真爽。再后来开始自己独立开发环境,直接开了 DAMP 的坑,最初也是用的 7.0 版本,然后就很随意的升到 7.3 版本,主要是当时开发负担少。然后博客站也都升级到了 PHP 7.3。

再后来 2021 年接了一个外包项目。虽然当时 PHP 8.0 已经发布了,但是貌似周边支持都不怎么地。为了保证开发速度,直接用了 PHP 7.4 版本。当时还涉及到前端开发,Node环境也是大更新,很早之前一直用的各种热门前端库基本都死绝了。博客和本地开发环境也都升级到了 PHP 7.4,博客的 WordPress 程序倒是没变,还是 5.2 版本(这里埋了个大坑),只是偶尔会从官网下载代码包然后手动更新对应的文件,以修复些潜在的安全问题。

现在是2024年年末,就连PHP 8.0 都已经停止维护快满一年了,最新版本 PHP 8.3 也已经发布快满一年了。WordPress 都到了 6.6 版本了,一堆 WordPress 插件都已经停止 5.2 版本的支持了。

想趁着有时间有机会,把 PHP 和 WordPress 都升级了。

天真了。


第一天

首先是把线上的代码全备份一遍。这个毫无工作量,我之前写了个备份代码,直接就把自己的整站扒下来。然后把备份的站再部署到本地的开发环境上,再改几个数据库字段,就完事了。打开后台,先禁用所有插件,以便升级之需。

Apache2 和 MySQL 不用动,因为用的一直都是 Docker 的最新版本,这几年也没什么巨大的兼容性变化。

PHP 这块我不知道算不算麻烦。我把 PHP 从 7.4 到 8.3 的所有 不向后兼容变更废弃功能 全都看了一遍。没多少,就几页,几分钟就看完了。个人感觉这么多变更,只有一条能实际影响到我日常开发工作,就是自 8.0 起不再支持 带有默认值的参数后面跟着一个必要的参数

screenshot_on_b85m_by_flameshot_at_2024-10-24_22-51-45.png

其实个人工作中也很少这么用,因为易读性有点烂。论性能来讲的话这么写性能也很差,只不过以前工作过的公司里就有很多人这么写,主要是为了防止其他同事调用函数忘记传参(空参也是参)。

其余的改动基本影响不到我。我使用的基本都是 PHP 的官方建议用法,最多也就是会遇到某些外部库被遗弃然后有个平替的情况,比如数据库接口啥的,即使出错了立刻就能发现。

于是很自信的先把 PHP 环境升级到了 8.3 。因为是基于 Docker 的所以也完全不用担心环境污染的问题。

然后就崩了,WordPress 就打不开了。

这倒是意料之中,毕竟当年 WordPress 4.3 版本当年连 PHP 7.0 都不支持。我现在用的是 5.2 版本,最高能支持的 PHP 版本也才 7.3 ……

Screenshot_2024-10-24_at_23-02-46.png

什么?7.3?但我已经用 PHP 7.4 跑了 WordPress 满 3 年了啊???

什么兼容性测试……

下载了个 wordpress-6.6.2 的包,按官方文档手动安装。 崩得一塌糊涂

全部删除了重新来,这回用官方的自动升级功能,直接从 5.2 升级 6.6。真神奇,WordPress 官网被墙了这么久了竟然能秒下 WordPress 的安装包,我也不知道他是走的什么渠道。PHP 就这点恶心,前台所有操作你都看不到任何细节,就像是在用 Windows 一样。

成功安装。然后 崩得一塌糊涂 。这回不仅崩得稀烂,而且由于没手工删除后台的旧文件,新旧文件混在一起,更是手足无措。

就这么搞了超过6个小时,一直干到后半夜三点多,搞不定。

放弃,睡觉。

然后严重失眠,抽搐。

screen_IMG_2148.png


第二天 白天

首先考虑下到底是 PHP 的问题还是 WordPress 的问题。

按理来讲我已经把 PHP 的升级文档都看完了,并没有什么会天塌一般的变更,但是 WordPress 这边的确天塌了。

先在 PHP 8.3 环境下运行一下我的其他项目看看,结果我的个人主页就崩了。

screenshot_on_b85m_by_flameshot_at_2024-10-24_23-33-27.png

我用的是 2023 年 3.1.48 版本 的 Smarty,其基础版本是给 PHP 7.0 做的,可能旧了吧。

下载了最新版本的 smarty-5.4.1 ,然后

screenshot_on_b85m_by_flameshot_at_2024-10-24_15-36-49.png

什么玩意? implode is Deprecated ,我怎么不知道?

又去重新看了一遍 PHP.net 官网的 implode 文档和 PHP 升级文档。根本没有 Deprecated 。

然后在网上搜了一下,发现是 Smarty 的锅。而且 Smarty 还在 join 和 implode 之间反复横跳。更恶心的是,implode 和 join 在 PHP 7.4/8.0 中已经声明并废弃了 先数组后分隔符 的用法,但是在 Smarty 中却是强制要求 先数组后分隔符 ?甚至官方在 issue 里来来了句 Smarty is Smarty and PHP is PHP. 有病吧。而且你就算有病,你特么连个文档都没有,谁知道你有这种抽风的设计啊。

你要知道之所以我还用 Smarty 这种超级古董,就是因为这是一种 靠谱的、前后端分离、完全后端渲染、仅需要 HTTP 和 PHP 环境、不需要臃肿框架和特定语法,的网页渲染模式,是提供给搜索引擎最靠谱的传统模式。没有网页模板系统的话,想写这种纯后端渲染前端显示的页面,就只能 php 和 html 代码混写,非常的恶心。

现在 Smarty 抽风了,真就不知道以后还怎么不依赖框架写这种页面。我搜了下,Laravel 的 Blade ,和 Symfony 框架下的 Twig,貌似也可以独立使用,但是我对 Laravel 和 Symfony 基本一无所知,作为一个 PHP 开发者真是有点丢人。

话题扯远了,回到刚才。

我目前是不太想动我的个人主页的,这 WordPress 是大头,是主要内容。主页只是个入口。虽然主页改动起来并不困难,代码量少,Smarty 抽风的部分比较好找。但主页这一块我其实并不满意,主要是多语言这块用的传参而不是独立页面,很受搜索引擎嫌弃,基本上没收录,收录的也搜不到。但我的确没有精力和欲望去重做。

另一方面,WordPress这边,崩溃得最多的部分其实是插件。WordPress的代码质量本来就很堪忧了,第三方插件更是三脚猫,各种天花乱坠的不规范语法,可以说基本上都不能在 PHP 8.3 上运行。

另外我虽然看了 不向后兼容变更废弃功能 ,但是 PHP 新版新增的语法糖也有点天花乱坠。我用的是最新版的 php-cs-fixer_v3.64.0 ,没配置好的话就会把那些插件的奇葩写法转写成 8.3 的语法糖,有时结果更是瞎眼,基本没有易读性。

综上考虑,先放弃 PHP 8.3 的升级。先把能通的条通。PHP 的升级难度应该不是最高的,但是底下这些撇不掉的小垃圾目前是必须要保且支持不到 PHP 8.3,没精力做修改。

放弃 PHP 8.3 继续用 PHP 7.4 。


第二天 晚上

看兼容性列表,WordPress 6.6 也是支持 PHP 7.4 的。

但是实际上 API 改动实在太大了,而 PHP 前端应用最恶心的一点,就是 出错了,不报错

即使开启了 define('WP_DEBUG', true); define( 'WP_DEBUG_LOG', true ); define( 'SAVEQUERIES', true ); ,也经常是

  • 功能好像开了但是没开
  • 功能好像崩了但是啥日志都没有
  • 功能正常使用但是页面上打了一堆不知道哪里来的错误日志

面对如此大的一个工程而大部分代码逻辑都是不可靠的。升级到 6.6 实在是消受不起。

放弃 WordPress 6.6 ,只升级到比较近的版本。

要不然试试 5.5 吧,毕竟我在用的一款插件,作者自评最高支持到 5.5。

下了个 WordPress 5.5 的安装包,装完了。崩,但是崩得没有 6.6 多。

主题 graphene

不开插件,只看主题,首先就是文章的评论显示不出来。检查后发现是评论的API变了,而我的主题 graphene 是 1.9.4.3 版本的,不支持 5.5 。

screenshot_on_b85m_by_flameshot_at_2024-10-25_00-48-04.png

有时候看其他人的代码就是折磨,代码里写法五花八门,空格TAB混着,一会拼接字符串,一会替换字符串。单引号双引号混着用,左边括号有空格,右边括号换行了。

说实话我都不知道这算不算改好了,反正现在是能显示出来。我更担心的是其实还有哪个不知道的角落还有错,但是看不到,毕竟 PHP 前端 出错了 不报错

官方倒是有个新版的 graphene,但是我现在用的这个主题就是我大量改动过的,因为 graphene 原版的代码实在是, 太错了 。好多代码完全不符合前端的理念。然后是颜色和界面也是要一点一点从设置里调,那复杂和麻烦程度,说真的我更乐意重写 HTML 和 CSS 。

我真的想过很多次自己做一个 WordPress 主题,这个想法可是足够老了。但是当时 WordPress 3 版本的主题文档就恶心到我了,真的超级麻烦。而到如今就 WordPress 现在的代码质量,我估计开发主题会更困难更恶心。

也是怪不得其他更轻量的博客程序能后追直上。

插件 Disable WordPress Core Updates

接下来是 Disable WordPress Core Updates 这个插件。

这个插件是为了禁用 WordPress 的界面更新的。但是其实只有一行有意义的代码:

add_filter( 'pre_site_transient_update_core', create_function( '$a', "return null;" ) );

首先 create_function 这个函数在 PHP 7.2 废弃,在 8.0 中删除,所以我改写成了。

add_filter('pre_site_transient_update_core', function ($a) { return null; });

然后就见证奇迹了。能用是能用,但是 pre_site_transient_update_core 这个字段我在整个 WordPress 代码中都没找到。为什么做一个 pre_site_transient_update_core add_filter 就能抑制 WordPress 界面提示升级? 魔法啊?

然后开启这个插件的时候,更新页面是崩的。

screenshot_on_b85m_by_flameshot_at_2024-10-24_21-14-55.png

魔法。

插件 NIX Gravatar Cache

接下来是 NIX Gravatar Cache 插件。

这是个把 Gravatar 头像缓存到本地服务器的插件,只不过早就死透了。我当时随便改了点代码对付着用,大部分时间没出错也就那样了。

首先是这段代码。(红色部分是我添加的改动)

screenshot_on_b85m_by_flameshot_at_2024-10-25_01-04-29.png

不能直接执行 wp_enqueue_scriptwp_enqueue_style ,要先执行个 add_action('wp_enqueue_scripts', 引用能调用那俩玩意的函数); 。外国人看这种超长的单词时不会眼花吗?

然后是这么一段代码。

screenshot_on_b85m_by_flameshot_at_2024-10-25_01-00-47.png

WordPress 的 register_啥啥啥_hook(__FILE__, array($this, '函数名')); 写法全都作废了,要改成 register_啥啥啥_hook(__FILE__, array($this, '函数名'));

接下来底下那一节:

  • 假如 路径不可写 且 路径为目录,报错

否则

  • 假如 创建目录(权限777)失败 且 路径不为目录,报错

就这烂判断条件看得我脑子都快炸了也没弄明白为什么这破玩意能在我线上服务器上跑几年没报错,而我本地开发环境却根本跑不通。

screenshot_on_b85m_by_flameshot_at_2024-10-25_01-19-42.png

而且这玩意讨厌就讨厌在于,确在我的测试环境下报错了,但是 WordPress 只是多了个 .php-error 的样式并且高出来 2em 的一节,但是一点错误日志都没有!!! 出错了 不报错

最后还是靠自己写 debug 代码定位的问题。

这 TM 都是些不该是问题的问题,竟然多得到处都是。

2天,一点有效进展都没有。唯一有效收获就是这些屎山不碰就没事,一碰能崩得全身是屎。

反而自己写的没有引用那些垃圾玩意的插件和程序,没发现啥大毛病。


第三天

2天没啥进展,给我干懵了。

俗话说没事别升级,升级必出事。

原本的想法是先试着升级,如果不能平滑升级的话,大不了全摧毁了,然后把整个博客文章用导入的方法塞回去重建。如果插件出问题了,大不了找找看是否有新的替代品。结果搜了一下,靠谱的插件基本没有,一大堆商业推广的插件,和一大堆复合性插件,而且这些插件无论对 5.x 版本 还是 6.x 版本的兼容性都乱七八糟,问题解决不了,还有可能引入更多的问题。主题这边则是更不想换,一方面本身现在用的主题就是我大量修改过的,因为网上的各种主题,仅安全性就一塌糊涂,更别说 HTML 标准了。我这主题还是专门针对 1366×768 分辨率优化过的,能在 小屏幕 150% 比例正常显示。让我再去改个新的,工作量也是太大。

现在基本上没辙了。

PHP 这边其实还行,而且版本活跃度比较稳定。

screenshot_on_b85m_by_flameshot_at_2024-10-25_22-09-00.png

https://packagist.org/php-statistics 能看出各版本使用占比都跟维护相关。但 7.4 版本 比 8.0 版本还受欢迎 属实乐了。

但是周边应用真的是质量山体滑坡。WordPress自 3.0 版本就开始崩,后面很多发展都很魔幻,就连编辑器都是靠社区兜底,到现在已经想不清楚这玩意的产品路线是啥了。

其他生态我也不清楚,毕竟作为一个 PHP 开发,我连 Laravel 和 Symfony 都没用过,商用产品都是用 ThinkPHP 应付的,当然最爽不过不依赖框架没有条条框框自己从头写。

但是像 Smarty 这种原行业标志都走奇葩路线了。可以说整个IT行业,基本上,正常的元老人物都退出舞台了,剩下的这些,刨去臭鱼烂虾,就只有偏执而扭曲了,假若走向歪路,那就没得旧,而这一点在开源社区上也极为明显(因为闭源商业的死不死没人关心),Godot 基金会开搞政治正确炮轰特朗普和动画头像用户,Linux 基金会直接开踢俄罗斯的代码贡献者。IT 行业现在就像是一个患了早期癌症的癌细胞轻微扩散病人,看似有救但却是谁都不想救。


结论

给我干哑火了,懵逼了,现在不知道咋整了。

The post 尝试升级 WordPress 版本 和 PHP 版本,然后失败了 first appeared on 石樱灯笼博客.

[Frightening Dream] 梦见求职怀旧「003」

「部分噩梦是因为本人在现实中遭受精神上的打击,使得心情低落,导致创伤后心理压力紧张综合征后,会在梦中体现。这种噩梦可能会引发一些疾病。」

——摘自维基百科「恶梦」词条。

声明:「本故事是做梦梦到的,如有雷同那你精神有问题」


章节一

找工作,出门去面试。

大下午,刚下完大暴雨,北京天黑得跟深夜一样。积水没过鞋。

面试地点在一个小区的四层小楼里。

天黑,小区里一个路人也没有。

到了楼下,从一个入口找到了一个特巨大的电梯,看着像货运电梯,但是保养得特干净,应该是被载人载货混用了。

电梯外边墙上一个大彩色海报,上面写着各楼层的用途:

  • 1楼,美食城(请从正门进入)
  • 2楼,酒吧(此电梯不到2楼)
  • 3楼,用棕色的胶带贴住了
  • 4楼,XXX幼儿园

我要去的是4楼,应该是个IT行业的公司,怎么是幼儿园?

我看看手机,离约定的面试时间还有一个多小时。不如直接上去看看是不是走错地方了。

坐电梯上到4楼,出电梯一看,楼层空无一人,有几个房间亮着灯,往里面看,装修的确像是个幼儿园,只不过里面都是空的。地上有些幼儿园家具和玩具,看起来以前是个幼儿园,只不过没在运营。

我继续往里走,看看能不能遇到个人,问问是什么情况,这是我等会要面试的地点吗?


章节二

突然从走廊对面呼啦啦上来好多人,挨个进房间开灯,好像在规划什么似的。我走进一看,领头的那个不是我以前的前同事W(代名)嘛!W也看到我了,赶紧互相打招呼。

W后面跟过来的人,我一看,这不都是以前我在公司时比我先离职的老前同事嘛!

我问他们这是在干啥,W说,老公司打算用个新名字开个子公司,把这里三楼四楼都租下来当公司了,人都是以前的老人,直接在这简单收拾一下就要开干了,今天就是过来看看四楼怎么分配工位比较合适,大家都过来看看,看完了之后在这团建,玩一下午,然后楼下应该是个美食城,还有酒吧,晚饭也直接在这解决,反正公司报销。

W问我:咋这么巧你咋上这来了?

我:我是来面试的,说是在这四楼。

W:那要是知道是你那还面个啥啊,直接来就行了。等会别走啊团建玩游戏直接带你一个。

随便看了一下,都是以前的老同事,互相寒暄一下,发觉之前出国的几个前同事都没来,还是满遗憾的。


章节三

不大一会,前同事们把最大的那个房间收拾出来了。原本应该是幼儿园的给小孩玩耍的活动教室,现在一堆大人在地上坐了一圈有说有笑。

我进屋,有的前同事看到我,一愣,「哎呦,老猫(代名)咋也来了」,我说我本来是来面试的,「那还面试啥啊你想要啥岗位等完事了直接跟我们说就行了。等会做游戏你可得在我们这一组啊。」

W:「那可不行,我先遇上的老猫,他就得跟我们一组了」

另一个前同事:「唉W你不能这样,你看我们组人少,你得给我们匀几个」

W:「那我把最后进屋那几个人匀给你!」

得,我还是个抢手货。

前同事M(代名。M以前是W的项目组组长)进屋,看到我,「呦,老猫来啦,你分哪个组了?」

W:「分咱们组了。」

M:「你给他手环了吗?」

W:「还没有呢。」

说罢,W拿出来个浅绿色的手环,直接套我手腕上了。「这是咱们公司的手环,也是门禁卡,你进门的时候刷这个手环,门就开了。另外今天做游戏咱们一个组的,手环颜色是一样的,都是浅绿色的。」

我看了一下周围,所有人手上都带着个手环,颜色一共四种,看来今天做游戏是分四组。


章节四

开始团建,做游戏。

第一个游戏,一个前同事竟然捧出来一摞 3DS 出来,第一个游戏竟然是要在 3DS 的一个排队游戏里比赛的。

然后果然我们组首推我第一个参赛……果不其然就拿了第一。怪不得每个组都想争取我当组员。

自己玩了第一个游戏,然后又看着老同事们继续玩。

我突然一愣,想起以前大家还在老公司的时候,开会啊,团建做游戏啊,都是有零食有饮料的,怎么现在没有了?

W:「这附近貌似还真没看到有超市,没买着。」

我:「我下楼去一楼那个美食城看看有没有吧。」

W:「你手环拿着呢吗?等会从3楼进来直接刷手环就能开门。」


章节五

我从货运电梯下楼,直接到了楼外,绕着整栋楼走了半圈,才看到整栋楼的正门。

天仍旧很黑,像深夜一般。

从正门进入,映入眼帘的,是一排排餐饮用的桌子,而大部分椅子则倒扣在桌子上,看起来像是很久没有用过一样。

继续往里走,看到有亮灯的柜台,有个人弯着摇拿着拖把在柜台后面拖地,也不像是在营业的样子。听到我的脚步后也没有站起来看我一眼的意思,弯着腰就回应了一句「还没到营业时间呢」,说罢就进里面的屋子了。

我四处看了一下。基本所有柜台都没有亮灯或者营业,都没有人在。

有个通向二楼的楼梯,但是整个楼梯都被椅子堆死了,根本不可能从那里上到二楼。

最里面有个柜台则是有个人坐在那玩手机,不过那个柜台连牌子也没有,看起来更像是值班的保安。

我过去问保安哪里能买东西,保安:「不营业。」

我又问从哪里能上楼,保安:「都说了不营业。」

我示意我其实是要去3楼4楼,保安竟然用嫌弃的眼光瞟了我一眼:「3楼4楼早就黄了好久了根本没东西,你上去干啥!」


章节六

没办法只能出大楼,然后走到大楼后门坐电梯再上去。

到了4楼,电梯打开,却发现整个楼层一丝光亮都没有。没有房间开灯,也没有人。我往里面走了一点,整个楼层除了我脚步声的回音之外,什么都没有。没开灯,房间里有什么也看不到。

很伤心的从电梯下楼,到楼下,外边又下起来大暴雨了。

门前积水很快就涨了上来,水深得能划船。


章节 Nostalgic PTSD

想拿手机看看几点了,却发现自己手腕上还套着手环,只不过在这乌七八黑的大黑天里,手环不是浅绿色的,是橙棕色的。

醒了。

The post [Frightening Dream] 梦见求职怀旧「003」 first appeared on 石樱灯笼博客.

如何在 VSCode Remote 里监控远程资源

快速开始

在使用 VSCode 的过程中,你可以通过使用 Remote-SSH 插件来连接到远程服务器,然后使用 Monitor Pro 插件对远程服务器的资源进行监控。

  1. 安装和配置 Remote-SSH 插件

    • 打开 VSCode,点击左侧栏的扩展按钮。
    • 在扩展商店搜索并安装“Remote-SSH”插件。
  2. 连接到远程服务器

    • 安装完成后,在 VSCode 的终端中输入相关命令来连接到远程服务器。例如,可以使用ssh 用户名@远程服务器地址命令进行连接。
  3. 使用 Monitor Pro 插件进行资源监控

    • 安装 Monitor Pro 插件。这个插件可以帮助你实时跟踪重要的系统指标,并提供直观的展现方式。
    • 安装后,该插件会自动启用,并且你可以通过 VSCode 的状态栏查看当前的资源使用情况,如 CPU、内存、磁盘等。
  4. 刷新设置

    • Monitor Pro 插件允许你设置更新资源指标的刷新间隔,确保能够及时获取最新的资源使用数据。

如何安装和配置 Monitor Pro 插件?

安装 Monitor Pro 插件

在 VSCode 界面的左上角,点击“扩展”(或按 Ctrl + Shift + X),然后在搜索框中输入“Monitor Pro”。

找到“Monitor Pro”插件后,点击“安装”按钮即可完成安装。

配置 Monitor Pro 插件

安装完成后,会在 VSCode 的活动栏中看到一个新图标,点击它以启动 Monitor Pro 插件。

根据具体需求,可以在 Monitor Pro 的设置页面中进行相应的配置,比如调整监控项等。

部分支持的远程资源监控指标

  1. CPU 使用率:监控 CPU 的利用百分比。
  2. 内存使用率:监测系统的内存使用情况。
  3. 网络使用率:跟踪网络的使用情况。
  4. 文件系统使用率:监控文件系统的使用情况。
  5. 电池百分比和充电状态:监测设备的电池百分比和充电状态。

最佳实践

  1. 实时跟踪关键指标:Monitor Pro 是一个全面的资源监控工具,可以帮助你实时跟踪重要的系统指标。因此,建议定期检查这些指标以确保系统的稳定性和性能。

  2. 直观展现方式:Monitor Pro 提供直观的展现方式,这有助于快速识别问题所在。利用这一点,可以及时发现并解决潜在的性能瓶颈。

  3. 内存优化:虽然 Monitor Pro 主要关注的是资源监控,但结合其他工具如 Memory Monitor 和 Allocation Tracker,可以进一步优化内存使用。例如,通过 Memory Monitor 查看整个应用所占用的内存,并注意 GC(垃圾回收)操作的频率和时机,以避免内存抖动等危险信号。

  4. 定制化配置:根据你的具体需求,对 Monitor Pro 进行定制化配置。例如,可以选择监控特定的资源,从而更有效地管理和优化资源使用。

This message is used to verify that this feed (feedId:42331815237783574) belongs to me (userId:55156152962822144). Join me in enjoying the next generation information browser https://follow.is.

银弹飞过先锋大厦

TL;DR 本文13200+字,全文阅读约需20分钟。其中2680字的示例部分为ChatGPT生成,因此可以说本文GPT含量20%,不过该部分详细内容与本文主旨关系不大,且由GPT-4生成,可略过不看。本文简要回顾了从软件及计算机诞生到当前大模型AI时代的软件工程发展历史,并试图从软件工程历史和ChatGPT实践案例中探讨在大模型时代的软件开发模式。

本文标题来自三个典故:1、标题模式借用伍迪·艾伦的电影名称《子弹飞过百老汇》,内容与电影无关;2、“银弹”出自弗雷德·布鲁克斯的软件工程论文《没有银弹》;3、“先锋大厦”是OpenAI在旧金山的总部所在地。

未来已来,只是尚未均分。

—— 威廉·吉布森(科幻作家)

今年四月参加了QCon Beijing 2024技术大会,令我印象最深刻的是开场微软中国CTO韦青的Keynote,他畅想了在智能时代企业的发展之路。他没有谈及当前的大模型应用,也没有谈及技术或是工程方法。他说的是进化,说的是成长型思维,说的是企业或者说是人应该如何在智能时代生存。人类在迈向智能化社会,而如果企业不转型,那必将被淘汰。企业如何适应智能化时代,韦青用了下面几张对比图来说明,在智能时代,我们不能像在进入汽车时代一样用马车时代的固有思维来设计汽车。这让我为之一振,我比较关心大模型在软件工程上的应用,但之前的思维更多是在聚焦于如何使用智能化来为当前的软件工程工具提效,可当前的软件工程方法都是基于以往的开发经验总结出来的,如果基于当前开发模式来叠加智能化那不正如这张图所形容的一样吗。

carriage-n-automobile.jpg

这同样让我想到另外一个故事:据说最初美国航天飞机的火箭助推器的直径是4英尺8.5英寸。为什么这样设计呢,因为美国铁轨的宽度是4英尺8.5英寸,而运送火箭助推器是通过火车装载,并且中途会经过一个隧道,而隧道的大小刚好比铁轨大一点点。火车轨道宽度的设计是延续自电车轨道而来的,电车轨道又是根据马车宽度来定的,马车宽度又是依据古罗马人铺设的马路而制定的,至于马路的宽度,则是罗马人根据罗马战车的宽度来定的,罗马战车的宽度是依据两匹并行的马的屁股宽度而定。也就是说,古罗马战马屁股的大小决定了航天飞机火箭助推器的直径。

我没有去求证这个故事的真实性,不过从很多产品的设计延续性来看,人们倾向于以经验乃至前人的经验来做设计,就像Office的保存按钮图标是软盘、手机拨号图标是听筒电话一样。但如果需要面向未来的软件开发来设计软件工程和工具,那就需要减少对以往固有经验的依赖,换一种思维模式。我们可以尝试从三个方面来改变思维方式:一是采用“第一性原理”探究事物的本质,二是从历史学习该事物的原理,当前这些特性存在都是前人踩坑后的填充。三是通过实践来验证猜想和推论。

软件工程的本质是什么,我觉得可以引申敏捷宣言里的话语,软件工程就是为了更高质量、更快效率、更低成本的构建“可以运行的软件”。下面我将回顾软件工程的发展历史,并通过对大模型应用在软件工程中的一个小实践,来探讨大模型时代软件工程的可能变化。

一 埃达:第一位程序员

追溯软件工程的历史,先要回顾下计算机和软件的历史。如果问大家两个问题:一、计算机和软件程序哪个先出现?二、计算机(Computer)这个词最初指的是人还是机器?估计很多人会答错。正确答案分别是:软件程序、人。最早的算法由埃达·洛夫莱斯(Ada Lovelace)写成,她是大诗人拜伦的女儿。1834年,巴贝奇构想出一种通用计算机器模型,他将此称为“分析机”,当时没有人能够理解,除了埃达。因为只有埃达能够理解通用计算机器的概念,1843年,巴贝奇邀请她为分析机论文写注解。埃达在写出的大篇幅注解中提出了四个设想,这四个设想后来成为了构建现代计算机的重要理念。第一个设想是对通用计算机器的说明,埃达认为这种机器不仅可以进行预设任务,还可以根据编写和重编程序完成无限数量的可变任务。第二个设想认为通用计算机器的运算不必限制于数字,而是可以存储并计算能用符号表示的任何对象,如文学、逻辑、音乐等。第三个设想则是关于分析机的工作步骤,她以计算伯努利数为例子,描述使用子程序和递归循环等过程来进行计算,相当于我们现在所说的计算机程序或算法。第四个设想则提出了人工智能的问题:机器能思考吗?埃达认为不能。她认为分析机可以根据指令执行操作,但不能产生自己的想法和意图。

由于埃达在注解中提出的第三个设想,以及她写的关于分析机计算伯努利数算法的详细步骤,后人将埃达·洛夫莱斯称为“世界第一位程序员”。1980年左右,美国国防部决定设计一种新的语言来取代当时国防部使用的 450 多种编程语言,设计团队将该编程语言命名为Ada(埃达)。

ada-lovelace.jpg

二 从计算机到编程

19世纪末,哈佛大学天文台台长爱德华·皮克林雇佣了一批聋哑女工,对天文台拍摄的照相底片进行测量和分类工作,她们需要在计算机械上进行大量的数据运算,而这批人也被称为“哈佛计算机”。20世纪初,随着泰勒“科学工作法”的推广和福特T型车流水线作业的发明,工业生产大规模化,工厂对于计算问题的需求也越来越大。那时候一些计算工厂会有一些大房间装满了人类“计算机”(大部分是女性),一排排整齐排列组合,数据从一端输入,经过一个“计算机”计算操作后,再传给下一个“计算机”处理,就像今天计算机的进程处理管道。

真正的现代计算机要等到1945年ENIAC的诞生。约翰·毛克利和赫尔曼·戈德斯坦设计了ENIAC,意为“电子数值积分计算机”(Electronic Numerical Integrator and Computer),而后,他们又根据冯·诺依曼提出的设想设计了世界第一台冯·诺依曼结构计算机EDVAC。EDVAC是ENIAC的继承者,建立了现代计算机的标准:它采用了二进制,并且设计了存储程序,也就是说计算机指令将被存储在存储器中。这种设计思想也将软件与硬件分离,成为独立的存在。

从历史贡献来看,格蕾丝·霍珀(Grace Hopper)是推动“软件编程”革命的第一人。在当时,负责建造ENIAC等计算机硬件的工程师都是男性,而编程工作则交由女性负责,那时候的程序指令会以线缆、开关连接的方式固定在硬件上,每当指令变化的时候,就需要人工编排接线。格蕾丝·霍珀发现了编程的意义所在,她发现软件可以是一个独立于机器硬件的东西,因而设计出了更接近人类表达层面的高级编程语言,从硬件层面抽象出来,也就是COBOL语言,她带领团队所编写的COBOL编译器也成为了世界第一款编译器。有意思的事,霍珀还是“Bug”一词的提出者。有一次她在排查机器故障时,发现是因为继电器上卡住了一只飞蛾造成线路短路导致,她将飞蛾贴在工作日志上,并注明这是首次发现的Bug(虫子),而排除故障的工作也被称为Debug(除虫)。

三 软件工程的诞生

电子计算机诞生以来,硬件能力每年都在飞速提升,尤其在晶体管和集成电路发明之后。相比之下,软件能力的提升却很有限,这导致软件能力无法匹配计算机业务的快速扩张。在上世纪五、六十年代,大型项目的软件出现了周期长、质量低、成本高等一系列问题。为此,北大西洋组织(NATO)在1968年左右组织了一系列会议来讨论软件问题,称之为“软件危机”(Software crisis)。而在会议上,也提出了“软件工程”的相关概念,试图通过一些工程方法来解决软件危机。

“软件工程”一词最早由玛格丽特·汉密尔顿(Margaret Hamilton)提出。她当时参与“Apollo 登月计划”,为了与硬件工程区分提出了软件工程这个词。奥巴马在任时,曾给玛格丽特颁发过“总统自由勋章”,当时玛格丽特的儿子在Twitter上发了张玛格丽特和她编写代码打印稿的合影,让人们了解了这段往事。

margaret-hamilton.webp

四 软件行业的兴起

二战前后,如ENIAC等计算机的构建和应用都由政府或军方主导;而后,IBM推动了计算机在商业领域的流行,商业软件也开始崛起。不过,真正让软件行业壮大的,是个人电脑时代的来临。个人电脑时代由两种力量推动而成。一种是追逐盈利的商业资本力量,在晶体管、微芯片和集成电路发明后,硬件能力每年都在提升,成本也在逐年下降。商业势力看到了这一巨大的盈利机会,硅谷大量的电脑公司如苹果、北极星等纷纷成立,他们的竞争让电脑能够降低成本进入普通老百姓家。另外一种力量是更理想主义的黑客社区力量,源自上世纪六、七十年代的嬉皮士文化,他们执着于信息的自由流通和分享,追求个性独立和自由。这两种力量既矛盾又相辅相成,共同推动了个人电脑的普及。而这两种力量也一直存在,在后来的互联网、移动互联网乃至AI时代都可以看到他们角逐的身影。

个人电脑这一概念最早由万尼瓦尔·布什(Vannevar Bush)于1945年在《诚如所思》一文中提出,他设想这种设备可以成为个人文档库和私人图书馆。布什还是《科学:无尽的前沿》报告的作者,在该报告中他提出了著名的政、企、学三角协作机制,并构建了这一体系,在计算机科学史上促进了很多发明。个人电脑最早在硅谷一个叫“家酿计算机俱乐部”(Homebrew Computer Club)的组织中流行,这是早期的黑客群体之一,很多电脑公司的创始人如乔布斯、沃兹尼亚克等都来自于这个俱乐部。微软创始人比尔·盖茨也曾参与这个组织的聚会,但后来他发表的一封公开信却触怒了整个圈子。

当时微软公司刚刚成立,他们开发的BASIC编译程序被拷贝走了,并在“家酿计算机俱乐部”的黑客群体中流传。这一行为激怒了当时年仅二十岁的比尔·盖茨,1976年,他发表了一封名为《致电脑爱好者的一封信》(An Open Letter to Hobbyists)的公开信,他在信中痛斥自由拷贝软件的行为,说:“硬件必须付钱购买,但软件却被免费共享了。谁会在意为软件辛苦工作的人是否获得了合理报酬呢?”这封信在黑客群体掀起轩然大波,并且在以后的自由软件和开源软件社区中一直被当作反面案例来宣传。但必须承认的是,比尔·盖茨看到了软件的巨大商业价值,也正是他的这一先于时代的洞见,让他引领微软成为世界第一的软件提供商,而他本人也因此获得巨大商业回报成为世界首富。

五 软件工程的发展

软件行业的兴起使得超大型软件密集型系统越来越多,软件也走向全球化。从上世纪六十年代到八十年代,软件工程随着软件行业的兴起而蓬勃发展,这段时间产生了几个重要的软件工程概念。

第一个是软件项目管理,弗雷德·布鲁克斯(Fred Brooks)将他所领导的IBM OS/360项目总结成了《人月神话》一书,书中提出了软件项目管理的一些理念和方法。其中最有名的是这一句:“如果一个软件项目进度已经落后,那么向这个项目中增加人手只会让进度更加落后。”这一观点也被称为“布鲁克斯法则”(Brooks’s law)。《人月神话》揭示了软件项目管理的复杂性,其核心是大型团队的沟通成本。不同于硬件,软件是通过软件抽象来解决业务问题,对于复杂的业务问题只能切分更多的软件任务,引入更多的开发人员,而更多的开发人员也意味着更多的沟通和管理成本。布鲁克斯后来又发表了一篇论文,名为《没有银弹》,他将这种通过软件抽象来解决业务问题的工作称为本质性工作(essential task),而其他将软件抽象转变成可以在机器上运行软件的工作称为附属性工作(accidental task),后者如编译、构建、测试等。在过去的几十年里,通过高级编程语言、分布式、云化等各种技术和工具一直在改进和提升的都是附属性工作,而本质性工作并未得到有效的改进。这是因为软件本身的固有属性决定的,布鲁克斯将其总结为复杂性、隐匿性和易变性。本质性工作大多发生在人们的脑中,这加剧了团队沟通的困难度。因而,布鲁克斯在论文中总结到:“在未来十年内,没有银弹,不会有任何技术或管理上的突破能使软件工程的生产力得到数十倍的提升。”

fred-brooks-mythical-man-month.jpeg

第二个是编程模式的发展,主要是结构化编程和面向对象编程的普及。当时虽然已经开始流行高级编程语言,但编程模式还大量沿用汇编语言思想的goto语句。艾兹格·迪杰斯特拉(Edsger Dijkstra)首先发表论文《goto语句有害论》,而后他与其他软件工程大师们提出了结构化编程模式,使用子程序、条件判断、循环语句来代替goto语句。同时期,面向对象程序设计的理念开始形成。到了七十年代,施乐公司PARC实验室推出Smalltalk编程语言,将面向对象的封装、多态和继承特性引入到编程语言中,而后,C++等语言的成熟将面向对象设计推成编程的主流。结构化编程和面向对象编程的发展提升了软件抽象能力,从某种意义上讲,这是对布鲁克斯所称的本质性工作效率提升的努力。

第三个是开发流程,这时期最有名的发明是瀑布软件开发模型。温斯顿·罗伊斯(Winston Royce)在1970年左右提出了瀑布模型这一软件开发流程,虽然瀑布模型在敏捷软件开发时代备受批评,但它的实际效用是非常明显的。瀑布模型将软件开发过程分为分析、设计、实现、验证、发布等阶段,每个阶段的输出是下个阶段的输入,像分段瀑布一样流转,因此得名。瀑布模型让软件开发过程的各种工作显性化,同时也明确了各项工作的输出件标准,对于大型软件项目来说,易于管理和规范。

六 自由软件与开源软件

比尔·盖茨意识到了软件的商业价值,软件也成为商业公司赚取利润的重要来源,软件不再将源码作为免费产品随硬件售卖附赠,而是编译成二进制发布包对外出售。软件源码不再附赠的情况让一位黑客大感失望,他就是理查德·斯托曼(Richard Stallman, RMS)。他认为信息自由分享的黑客时代传统不应该被抛弃,因此在1984年发起GNU项目,要创建一个完全自由而开放的操作系统来对抗商业封闭的UNIX. 而后他又成立自由软件基金会(Free Software Foundation, FSF),并创造了通用公共许可证(GNU General Public License, GPL)这一软件授权协议。GPL对软件行业的影响是深远的,因为它不仅规定了软件自由分发的权利,还规定了其义务,这也是被称为GPL具有“传染性”的原因。但“传染性”这一词偏负面,实际上GPL对软件自由分发起了至关重要的作用,一些著名的开源软件如Linux、Git都是以GPL协议分发的。

不过斯托曼对于自由软件的执着让他近乎偏执,自由软件社区因此分裂出一批期望更加融入商业社会的人,以埃里克·雷蒙德(Eric Raymond, ESR)为代表,在1998年创立了开放源码促进会(Open Source Initiative, OSI),以商业更友好的开源软件来取代意识形态更浓重的自由软件。雷蒙德在研究了一系列软件项目后,发布了开源软件开发的经典著作《大教堂与集市》,在书中他阐述了两种开发模式,一种是自上而下构建的、集中式的、由专人或专属团队所管控的软件开发方式,这种雷蒙德称为大教堂模式;另一种则是自下而上构建的、分布式的、在互联网上分发传阅的开放的软件开发方式,雷蒙德称之为集市模式。集市模式的代表是Linux项目。

Linux的诞生源于大学时代林纳斯·托瓦兹(Linus Torvalds)暑期的个人项目,他大学放假期间在家无聊,就写了Linux并发布到了互联网新闻组,没想到在黑客圈里广受欢迎,托瓦兹将它无偿开放出来,并广泛接纳社区开发者的代码提交。到了2005年,估计有近一万人给Linux贡献过代码,Linux也成了开源社区最大的项目。Linux项目代表了软件工程的另外一种模式,它没有企业项目那种自上而下的管理,通过互联网分布式协作来实现大型软件项目开发并管控质量。托瓦兹有句名言:“只要眼睛多,Bug容易捉。”(Given enough eyeballs, all bugs are shallow.)

七 万维网及敏捷软件开发

1990年,英国计算机科学家蒂姆·伯纳斯-李(Tim Berners-Lee)和他的工作伙伴罗伯特·卡里奥合作制定了一份计划书《万维网:一个超文本项目的计划书》(World Wide Web: Proposal for a Hyper Text Project),这个名叫万维网(World Wide Web, WWW)的系统将彻底改变计算机行业。互联网(Internet)可以追溯到上世纪六十年代的ARPANET,但一直到了万维网的出现才让它真正成为全球互联网。万维网可以理解为是互联网上的全球信息系统,很多人在概念里其实是将万维网与互联网等同了。在伯纳斯-李的设想中,万维网具备三项技术:全球唯一的统一资源标志符(Uniform Resource Locator, URL),超文本标记语言(HTML)和超文本传输协议(HTTP)。该年年底,伯纳斯-李在他的工作电脑上开发了第一个网页浏览器,实现了HTML解析和HTTP传输,值得一提的是,伯纳斯-李所使用的工作电脑正是乔布斯被赶出苹果后创办的新公司所生产的NeXT电脑。

伯纳斯-李所任职的欧洲核子研究组织希望为万维网申请专利,但伯纳斯-李坚持认为万维网的协议应该免费开放共享,毕竟他的设计初衷就是为了促进分享和协作。后来,欧洲核子研究组织同意了他的请求并采用了GPL协议开放源码。1993年,马克·安德森(Marc Andreessen)开发了第一款商业浏览器Mosaic,网站如雨后春笋般涌现,大型网站如AOL、雅虎等。紧接着,以博客、维基百科为代表的Web2.0开始兴起,用户创造内容让万维网上的数据急剧增加,传统的网站目录方式无法满足用户查找内容的需求,自动化搜索引擎将成为万维网内容的主要查找方式。这也就造了拉里·佩奇(Larry Page)和谢尔盖·布林(Sergey Brin)的Google,取代雅虎成为一代互联网霸主。

万维网的疾速发展使得互联网软件开发呈井喷之态,大量的互联网企业需要快速开发并上线软件应用,并且要根据用户的反馈及时修改以应对需求变化。这种变化使得传统的瀑布开发模式无法满足企业需求,一种新的开发模式应运而生。2001年,十七位软件开发人员在美国犹他州雪鸟滑雪地组织了一场会面,他们中有很多是软件工程大师,包括写作《重构》的马丁·福勒(Martin Fowler)。会上,他们发表了“敏捷软件开发宣言”(Manifesto for agile software development),这其中主要是四句话:“个体和交互胜过流程和工具,可以工作的软件胜过详尽的文档,客户合作胜过合同谈判,响应变化胜过遵循计划。”敏捷软件开发鼓励快速响应变化、迭代开发与变更,它包含了一系列实践,如Scrum、极限编程等。

八 云计算与DevOps

2003年,在亚马逊工作的安迪·杰西(Andy Jassy)发现在亚马逊孵化新业务的过程中,工程师总要把大量时间投入在重复的基础设施构建工作上。他敏锐地察觉到了这一内部机会,并给亚马逊创始人兼CEO贝索斯写了一份商业计划书,在该计划书中构想了亚马逊AWS(Amazon Web Services)云的未来。安迪·杰西带着一个小团队开启了内部创业之旅,2006年发布了云服务S3和EC2。AWS在亚马逊内部的重要性越来越高,2023年利润占亚马逊整个利润的一半多。安迪·杰西一直担任AWS CEO职位,一直到2021年接替贝索斯成为亚马逊CEO. 虽然说云计算这个词在上世纪末就已出现,但AWS是真正将云计算商业化成功的第一个产品。AWS的成功也让众多互联网头部厂商看到机会,谷歌云、微软Azure云等紧追不舍。云计算成为了互联网软件乃至整个IT软件的底座。

云计算的本质是资源弹性,云服务厂商将一个个计算资源连接起来构成一个巨大的资源池,而软件用户只需要租用计算资源,根据业务需要弹性扩缩容。支撑资源弹性的是软件的架构弹性,这种架构模式就是微服务。微服务架构是将庞大的单体结构程序拆分成松耦合的独立组件,以API方式调用,这种架构解耦可以追溯到GoF的《设计模式》和Robert C. Martin的《敏捷软件开发》。另外,GitHub的出现让开源软件更容易流传,开源组件成为构筑现代软件的基本模块,开发软件像是搭积木一样,通过胶水语言将开源组件组装,便可以快速构建业务软件,这种开发模式极大地提升了开发效率。加上云平台提供的软件应用部署能力,从软件开发到部署发布及运维,可以快速在云平台上闭环。这种软件开发模式也被称为云化软件开发。

云化软件开发的工程实践是DevOps,它强调的是开发与运维之间的沟通协作以及更小粒度地发布和更频繁地变更,可以理解为是敏捷软件开发的延伸。Git、依赖包管理、docker、CI/CD等现代化开发工具成为DevOps工程实践的核心:Git的分布式开发方法和版本管理践行软件项目的“Everything as Code”的理念,如基础设施代码化(IaC)、配置代码化、流水线代码化等,团队各角色基于代码协作。诸如Ruby Bundler、Maven、NPM等语言依赖包管理工具使得组件化开发更简单,特别是对于开源组件的引用,一个Node.js工程项目,引用几百甚至上千个开源组件是非常常见的现象,因此网上也有关于NPM依赖黑洞的段子。而Docker、K8s等容器工具降低了微服务开发的门槛,结合CI/CD工具自动化部署,降低运维成本。小结一下,DevOps云化软件开发工程工具实践的几个关键词是代码化、组件化/开源、容器化、自动化,其本质是为了更频繁地变更(发布新特性和修复问题),缩短交付周期。

npm-jokes.jpg

九 大模型时代的软件工程

前文提到,埃达·洛夫莱斯认为电脑不能思考,只会根据指令执行。一个世纪以后,图灵将她的主张写到自己的论文里,称为“洛夫莱斯夫人的异议”。这篇论文便是著名的人工智能奠基论文《计算机器与智能》(Computing Machinery and Intelligence),他在文中还提出了一个被后人称为“图灵测试”(Turing Test)的思想实验,在这个测试中,测试人与人和机器同时用文字交流,但他不知道背后谁是人或机器,如果在一段交流时间内无法通过交流内容来判断区分人和机器,那么就认为该机器通过了图灵测试,也就是说该机器具备与人类相当的智能。一个冷知识是:我们上网时经常被要求输入验证码的英文单词“Captcha”即是“完全自动通过图灵测试来区分计算机或人类”(Completely Automated Public Turing test to tell Computers and Humans Apart)的首字母缩写。图灵自己将此实验称为“模仿游戏”(The Imitation Game),关于他的传记和电影名称则取之于此。

历史上对人工智能的研究主要分成了两大阵营,一派推崇知识和推理逻辑,典型的如专家系统,被称为符号主义,也有称之为“GOFAI”(Good old fashioned artificial intelligence),这一派的发展如该词所言已经是old-fashioned了。还有一派通过模拟人类的大脑构建神经网络,将知识通过机器学习存储在大量的连接中,基于数据学习来发展人工智能,被称为连接主义,这一派的代表是神经网络系统。杰弗里·辛顿(Geoffrey Hinton)在神经网络领域潜心研究三十多年,开创了深度学习这一人工智能分支,而基于深度学习的大模型在近年来发展迅速,尤其在OpenAI的ChatGPT发布后成为全球最热门的技术。ChatGPT让人们看到了AGI(通用人工智能)的可能,它的AIGC能力可以帮助到人们将AI运用在实际的工作和生活中。关于神经网络和深度学习的历史和原理可以参见我的另外一篇文章《从神经网络到 Hugging Face》,这里我想就使用ChatGPT来辅助编码的案例实践来谈一谈我对大模型时代的软件工程的理解。

一个工程实践

问题的背景是:我想做一个类似Perplexity的智能搜索前端页面,但我不太懂前端开发,所了解仅HTML、CSS、JavaScript三项基本知识以及Next.js前端框架而已,因此我希望在ChatGPT(GPT4版本)的帮助下完成前端代码的编写。其中一个场景需求是,用户在输入问题点击搜索按钮后,页面上自动追加并显示答案文字。以下引用和代码部分为我与ChatGPT交流的原始记录,没有做改动。我不是Prompt Engineer,因此Prompt写得比较简陋。(这一段对话比较长,并且回答部分都由ChatGPT生成,不需要了解细节的读者可以快速略过直到“大模型是不是银弹”章节。)

我首先问ChatGPT:

现在你是一名Next.js专家,请帮我实现一段代码,在页面上点击button,在一个div区域内显示一段文字。

ChatGPT回答:

当然可以!下面是一个简单的Next.js页面示例,它实现了你的要求。这个示例中,我们将使用React的状态来控制点击按钮后在div区域显示的文本。

首先,你需要在你的Next.js项目中创建一个新的页面。假设我们将这个页面命名为showText.js,你可以在pages目录下创建这个文件。然后,将以下代码复制到showText.js文件中:

import React, { useState } from "react";

export default function ShowText() {
  // 使用React的useState钩子来管理文本的显示状态
  const [text, setText] = useState("");

  // 定义一个函数来处理按钮点击事件
  const handleClick = () => {
    // 设置要在div中显示的文本
    setText("这是我显示的文本!");
  };

  return (
    <div>
      <button onClick={handleClick}>点击我</button>
      <div>{text}</div>
    </div>
  );
}

这段代码的工作原理是:

使用useState钩子来创建一个名为text的状态变量,这个变量将用来存储在div区域显示的文本。

创建了一个handleClick函数,当按钮被点击时,这个函数会被触发。它通过调用setText函数更新text状态,从而改变div区域内显示的文本。

页面上有一个按钮和一个div元素。按钮的点击事件被设置为调用handleClick函数。当text状态改变时,div中的内容会相应更新显示新的文本。

你可以通过运行Next.js应用来查看效果:在你的项目目录中,使用npm run dev(或者如果你使用Yarn的话,就是yarn dev),然后在浏览器中访问对应的页面(通常是http://localhost:3000/showText,取决于你的文件名和配置)。点击按钮后,你应该能在页面上看到指定的文本显示出来。

ChatGPT不但生成了整段代码,还解释每个步骤,这样我可以理解并按照自己的期望修改里面的逻辑,再按照ChatGPT的调试指导步骤可以直接在本地开发环境中运行。

接着,我将需求更进一步描述:

我需要再显示复杂点,点击button后,div内显示三段文字,每一段延时200ms显示出来

ChatGPT紧接着给出一段:

为了实现点击按钮后在div内逐步显示三段文字,每段文字之间延时200毫秒,我们可以通过使用setTimeout函数来实现。下面是一个简化的实现方式:

首先,定义三段不同的文本作为要显示的内容。

当按钮被点击时,通过设置多个setTimeout,依次在不同的时间间隔后更新状态,从而控制文本的显示。

更新后的showText.js代码如下:

import React, { useState } from "react";

export default function ShowText() {
  const [texts, setTexts] = useState([]); // 使用数组来存储要显示的文本段落

  const handleClick = () => {
    // 清空文本,以防多次点击
    setTexts([]);

    // 定义三段文本
    const textSegments = ["第一段文本显示", "第二段文本显示", "第三段文本显示"];

    // 依次显示每段文本
    textSegments.forEach((segment, index) => {
      setTimeout(
        () => {
          // 更新状态以添加当前文本段落
          setTexts((prev) => [...prev, segment]);
        },
        200 * (index + 1),
      ); // 每段文本延时200ms显示
    });
  };

  return (
    <div>
      <button onClick={handleClick}>点击我</button>
      <div>
        {texts.map((text, index) => (
          <p key={index}>{text}</p> // 使用段落标签<p>来分隔文本段落
        ))}
      </div>
    </div>
  );
}

在这个实现中:

使用了一个数组texts来存储需要显示的文本段落。

每次按钮点击时,首先清空texts数组以重置显示内容。

通过forEach循环和setTimeout,计划在200毫秒、400毫秒、600毫秒后依次更新状态,添加对应的文本段落到texts数组中。

使用map函数在div中为texts数组中的每一段文本渲染一个<p>标签,以此来分隔显示的文本段落。

每次点击按钮时,你会看到三段文本依次出现,每段之间有200毫秒的延迟。

现代前端软件工程推荐强类型语言TypeScript,我让ChatGPT改成TypeScript:

这一段代码请用typescript写一下吧

ChatGPT马上就给我转成TypeScript:

将之前的例子转换为使用TypeScript的形式,主要变化是添加类型注解来增加代码的类型安全性。这不仅有助于防止类型相关的错误,还能提高开发效率,因为TypeScript提供的类型检查和自动完成特性。

下面是使用TypeScript重写的showText.tsx代码:

import React, { useState } from "react";

const ShowText: React.FC = () => {
  const [texts, setTexts] = useState<string[]>([]); // 明确指出texts是字符串数组

  const handleClick = (): void => {
    // 清空文本,以防多次点击
    setTexts([]);

    // 定义三段文本
    const textSegments: string[] = [
      "第一段文本显示",
      "第二段文本显示",
      "第三段文本显示",
    ];

    // 依次显示每段文本
    textSegments.forEach((segment, index) => {
      setTimeout(
        () => {
          // 更新状态以添加当前文本段落
          setTexts((prev) => [...prev, segment]);
        },
        200 * (index + 1),
      ); // 每段文本延时200ms显示
    });
  };

  return (
    <div>
      <button onClick={handleClick}>点击我</button>
      <div>
        {texts.map((text, index) => (
          <p key={index}>{text}</p> // 使用段落标签<p>来分隔文本段落
        ))}
      </div>
    </div>
  );
};

export default ShowText;

这里的主要变化包括:

使用React.FC来声明ShowText组件的类型。React.FCReact.FunctionComponent的缩写,它是一个泛型接口,用于类型化函数式组件。

useState<string[]>([])表明texts状态是一个字符串数组。

handleClick函数声明中使用: void来指明这个函数没有返回值。

使用TypeScript编写Next.js页面组件,可以让你在编译时就捕获到潜在的类型错误,提高代码质量和可维护性。

在我将这段代码复制粘贴后,遇到了编译错误:

编辑器提示报错 “Property ‘forEach’ does not exist on type ‘string | string[]’. Property ‘forEach’ does not exist on type ‘string’.”

让我惊讶的是ChatGPT非常自信的认为代码没有问题,而是给了我其他的debug思路:

这个错误表明TypeScript编译器不能确定textSegments变量在调用forEach方法时是一个数组。在你提供的上下文中,textSegments被明确声明为一个字符串数组(string[]),所以正常情况下不应该出现这个错误。错误信息“Property ‘forEach’ does not exist on type ‘string string[]’. Property ‘forEach’ does not exist on type ‘string’.”暗示在某处textSegments可能被推断为string | string[]类型,即它可能是一个字符串或字符串数组。

考虑到你的代码片段应该没有问题,这个错误可能是由于TypeScript或编辑器状态的某种混乱造成的。尝试以下方法可能会有所帮助:

1. 重启编辑器:有时编辑器或其TypeScript服务可能会进入异常状态,重启编辑器通常能解决这类问题。

2. 检查TypeScript版本:确保你的项目使用的TypeScript版本与编辑器的TypeScript服务版本一致。版本不匹配有时会导致奇怪的类型推断问题。

3. 检查其他代码:确保没有其他地方对textSegments进行修改,使其类型可能被推断为string | string[]。特别是在大型项目中,变量名重复或错误的类型声明可能导致难以发现的错误。

4. 显式声明类型:尽管你已经显式声明了textSegmentsstring[],考虑在调用forEach之前再次确认其类型,虽然这种情况下似乎多余。这可以通过类型断言实现,例如:

(textSegments as string[]).forEach((segment, index) => {
  // ...
});

如果上述步骤仍然不能解决问题,那么可能需要更详细地审查项目的配置和代码上下文。确保tsconfig.json中的配置正确无误,且没有其他可能干扰类型推断的因素。此外,尝试在命令行运行tsc来看是否TypeScript编译器也报相同的错误,这有助于确定问题是由编辑器引起的还是由TypeScript配置引起的。

我发现果然是因为ChatGPT所说的第3点原因,我在拷贝代码时并没有给textSegments 声明类型。不过很快我又遇到了新问题:

另外报错: Argument of type ‘(prev: never[]) => string[]’ is not assignable to parameter of type ‘SetStateAction<never[]>’. Type ‘(prev: never[]) => string[]’ is not assignable to type ‘(prevState: never[]) => never[]’. Type ‘string[]’ is not assignable to type ‘never[]’. Type ‘string’ is not assignable to type ‘never’.

ChatGPT又给了解法:

这个错误提示表明TypeScript无法正确推断setTexts更新函数中回调的返回类型。具体来说,当你尝试使用函数形式的setState更新状态时,TypeScript认为这个回调函数应该返回一个never[]类型,这通常意味着它不能正确推断状态的初始类型。这个问题往往出现在状态的初始值设置或类型注解方面。

错误中提到的never[]类型表示一个数组,但它的元素类型为never,即TypeScript认为这个数组不应该包含任何元素。当你尝试向这样的数组中添加字符串时,会出现类型不匹配的错误,因为string[]不是never[]的子类型。

解决方案

问题的根源在于useState的类型推断。当你使用useState时,如果你没有明确指定状态的类型,并且给定的初始值让TypeScript无法正确推断出来,就可能导致这种情况。在你的例子中,显然TypeScript没有从初始值(空数组[])中推断出正确的类型。

为了解决这个问题,你需要在使用useState时明确指定状态的类型。看看下面的代码,特别注意useState<string[]>([])这一行:

import React, { useState } from "react";

const ShowText: React.FC = () => {
  // 明确指定useState的类型为string[]
  const [texts, setTexts] = useState<string[]>([]);

  const handleClick = (): void => {
    setTexts([]);

    const textSegments: string[] = [
      "第一段文本显示",
      "第二段文本显示",
      "第三段文本显示",
    ];

    textSegments.forEach((segment, index) => {
      setTimeout(
        () => {
          setTexts((prev) => [...prev, segment]);
        },
        200 * (index + 1),
      );
    });
  };

  return (
    <div>
      <button onClick={handleClick}>点击我</button>
      <div>
        {texts.map((text, index) => (
          <p key={index}>{text}</p>
        ))}
      </div>
    </div>
  );
};

export default ShowText;

在这个例子中,通过使用useState<string[]>([]),我们明确告诉TypeScript状态texts是一个字符串数组。这样,当你更新状态时,TypeScript知道你是在向字符串数组中添加元素,而不会尝试将其推断为never[]类型。

确保在使用useState时始终提供足够的类型信息,特别是当状态的初始值可能让TypeScript感到困惑时。这不仅可以避免此类错误,还可以提高代码的可读性和可维护性。

ChatGPT特别贴心的指出,让我特别注意useState<string[]>([])这一行,我检查了代码,发现问题确实就出在这里。经过几轮对话,ChatGPT帮助我实现了需求并Debug代码,最后顺利运行成功。(整个项目的源码开放在GitHub上:hutusi/fellini ,欢迎Star。)

虽然此前我也有使用GitHub Copilot来做补全代码等工作,但没有像这次使用ChatGPT来实现一个完整需求的感觉惊艳。我也曾使用ChatGPT写过文案,甚至写过格律诗,但都不太满意,而此次使用ChatGPT来写代码却完全超出我的预期。它除了能够生成可以运行的代码,还能够准确识别我的意图,解释每段代码的含义,并且提醒我注意可能出错的代码行。不过,上面的例子还只是一个需求的片段,GitHub CEO 在去年5月Web Summit上演示18分钟借助GitHub Copilot X来编写一个贪吃蛇小游戏的例子会让人更加惊叹GPT的能力。这也让人们看到了软件工程的一个可能的发展方向。

十 大模型是不是银弹?

纵观整个软件工程历史,虽然软件的概念早于计算机出现,但真正的软件产业是跟随计算机硬件其后发展出来的。计算机硬件能力发展速度非常快,这一规律被总结成了“摩尔定律”,即芯片的性能每18个月会提升一倍。但软件的能力却不会有这么快速的发展,不但如此,还因为业务需求不断变化而导致软件复杂度急剧增加,软件的复杂性吞噬了硬件的性能。以至于另外一个定律应运而生——安迪-比尔定律:安迪给的,比尔就会拿走。安迪是时任英特尔CEO安迪·格鲁夫,比尔是微软创始人比尔·盖茨。也就是说“摩尔定律”揭示下的芯片提升的性能最终又被软件吃掉了。

究其原因,是软件固有属性——复杂性决定的,软件的复杂性体现在其不可见性、可变性等,这导致软件项目的质量、成本、进度都很难控制,软件工程正是为了解决这些问题而生。通俗来讲,软件工程的目的就是希望用更低的成本、更可控的周期来交付更符合用户期望的软件。瀑布开发模式或者说V模型,其明确分工协作、确定各阶段交付标准是为了使得软件项目进度可控、软件质量可控;而敏捷软件开发也是为了使软件项目通过快速迭代、响应变化来命中用户需求,符合用户期望。DevOps、CI/CD、自动化测试等工具带来了软件构建效率的提升,使得代码可以更快的编译交付上线,而开发人员也可以得到更快的质量反馈。从大型机到个人电脑,从互联网到云计算,软件工程也会随着计算机产业的变化而更新改进,以契合软件行业的发展。不过,弗雷德·布鲁克斯在《没有银弹》中认为,这些改进都属于对软件附属性工作的改进,软件的本质性工作并没有得到真正的改进,这些本质性工作是抽象成计算机可理解的软件所做的工作,大多发生在开发人员的脑子里。而依据布鲁克斯的观点(该文写于1986年),过去几十年乃至未来十年,没有一项技术或方法能大幅减少本质性工作,这也就意味着软件工程生产力不能得到实质性的提升。

不过我认为,在过去的软件工程实践里,有两项技术在软件本质性工作上做出了较大贡献,一项是高级编程语言,一项是可复用的软件对象设计(如设计模式、开源组件),虽然这两者布鲁克斯在文中并不认同。高级编程语言(如面向对象语言)拉近了实际业务与软件抽象的距离,使得开发人员可以使用更符合人类思维的编程语言来与计算机交流,不过我同意布鲁克斯的观点:高级语言对于某些(不擅长抽象的)用户在某种程度上增加了脑力劳动的负担。可复用的软件对象从某种意义上讲节省了可能的软件开发工作。

布鲁克斯还认为人工智能也不能给软件生产力带来数量级的增长,他在文中所说的人工智能是指专家系统,而专家系统也因为其规则和推理无法支撑复杂场景的增长在上世纪八十年代后逐渐式微,引领现代人工智能方向的是深度学习神经网络,现今就是以GPT为代表的大模型。布鲁克斯于2022年去世,不知道如果他看到ChatGPT等大模型应用在软件工程会做如何判断。但从目前业界的共识看,大模型技术将改变软件工程和整个软件行业。

有人将此称为“软件工程3.0”,我不太认可这种说法,因为所谓的“软件工程2.0”(敏捷、DevOps)也只是对传统软件开发模式的迭代改进。我更愿意称大模型前的软件工程为“传统软件工程”,而大模型后的软件工程为“智能软件工程”。在智能软件工程时代,大模型应用可以帮助用户直接将需求转换为代码,这就省去了中间的分析、设计、编码、调试等环节。(当然,对于一些复杂的需求,目前的一些大模型应用可能需要用户做需求的分解,包括提供更详细的上下文,以便于大模型理解用户意图。)分析、设计、编码、调试等环节是基于传统开发模式的任务分工,大模型不需要这样的分工。我们甚至可以把大模型理解为一个承接外包任务的智能全栈工程师,用户只需要给大模型下发任务,而大模型外包公司则负责将任务完成并交付可运行的软件代码(包括测试用例集)。

因此,我觉得在设计软件开发的大模型应用时,基于现有工具延伸、优化用户交互模式都不是首要考虑的因素,而是看两个方面。一是从第一性原理看,该应用是不是瞄准了软件最本质的产品——代码。二是从布鲁克斯的理论看,该应用是不是在解决软件开发的本质性工作。从布鲁克斯发表《没有银弹》以降,没有人或技术敢声称是银弹,敏捷流行的时期最常见的文章是《敏捷不是银弹》,其他如DevOps、微服务等技术出现时都如是说,因为学习或经历过历史的人都知道软件的根本问题其实是人的问题。但大模型的AI时代正在来临,我大胆预测大模型是软件开发的银弹,这颗银弹从先锋大厦(OpenAI总部所在地)射出,飞过福尔松大街,飞过太平洋。

十一 后记

在写作本文时,我正巧看到GitHub CEO Thomas Dohmke前不久在TED上发表的演讲,名为With AI, Anyone Can Be a Coder Now。他预测到2030年,GitHub上的软件开发者将从目前的1亿增加到超过10亿,也就是说全球超过10%的人都能够编写软件。因为在像GitHub Copilot这种大模型应用的帮助下,编写软件将变得像骑自行车一样容易。主持人问:既然AI这么强而且还在加速增强,那人类还需要参与软件开发其中吗?Thomas的回答是:这就是为什么我们给产品起名为“Copilot”(副驾驶员)的原因,因为还需要“Pilot”(驾驶员)。我们需要有创造力的Pilot决定做什么。

这个问题可能很多人有不同的看法,我想到最近看的一本哲学小书《苏菲的世界》。主人公苏菲在一位导师的引导下学习哲学史,苏菲(Sophie)在希腊语中即“智慧”之意,而哲学(Philosophy)即“爱智慧”。苏菲在学习“智慧”的过程中成长,但到最后她和导师意识到自己不过是一位上校写给女儿的书中的角色而已。她们决定逃出书本,融入到现实中的女孩的世界。

附 参考资料

书籍

《创新者》沃尔特·艾萨克森【著】,中信出版社,2017 (英文原版 The Innovators, Walter Isaacson, Simon & Schuster, 2015)

《睡鼠说:个人电脑之迷幻往事》 约翰·马科夫【著】,黄园园【译】,电子工业出版社,2015

《智慧的疆界:从图灵机到人工智能》 周志明【著】,机械工业出版社,2018

《人月神话》 弗雷德·布鲁克斯 【著】,汪颖【译】,清华大学出版社,2002

《设计模式:可复用软件面向对象软件的基础》 GoF【著】, 机械工业出版社, 2000

《敏捷软件开发:原则、模式与实践》Robert C. Martin【著】, 清华大学出版社,2003

《大教堂与集市》埃里克·雷蒙德【著】,卫剑钒【译】,机械工业出版社,2014

《科学:无尽的前沿》万尼瓦尔·布什【著】,崔传刚【译】,中信出版社,2021

《苏菲的世界》乔斯坦·贾德【著】,萧宝森【译】,作家出版社,2007

论文、演讲及文章等

The History of Software Engineering, Grady Booch, IEEE Software, 2018

《诚如所思》万尼瓦尔·布什 (As We May Think, Vannevar Bush) , 1945

《没有银弹:软件工程的本质性与附属性工作》弗雷德·布鲁克斯 (No Silver Bullet—Essence and Accidents of Software Engineering, Fred Brooks), 1986

《致电脑爱好者的一封信》比尔·盖茨 (An Open Letter to Hobbyists, Bill Gates), 1976

Building an app in 18 minutes with GitHub Copilot X, Thomas Dohmke, Web Summit Rio, 2023

With AI, Anyone Can Be a Coder Now, Thomas Dohmke, TED, 2024

《看不见的大猩猩——智能时代的企业生存和发展之路》 韦青(微软中国 首席技术官),QCon Being Keynote, 2024

《从神经网络到 Hugging Face》 胡涂说 hutusi.com, 2024

《改变世界的一次代码提交》 胡涂说 hutusi.com, 2020

《自由的代价》 胡涂说 hutusi.com, 2015

使用 Docker 和 pnpm 优化打包 Nuxt

本文将指导你如何为一个结合了 Prisma 和 Nuxt.js 的全栈项目创建优化后的 Docker 镜像,并使用 pnpm 作为包管理器。

我的项目最终镜像大小从 1.12GB 缩减到了 160.21MB。

我的项目构成

Nuxt.js 是一个基于 Vue.js 的服务器端渲染应用框架,非常适合于构建现代化的 Web 应用。

我的项目直接采用 Nuxt 构建全栈项目。

  • Nuxt3
  • Prisma
  • PNPM

开始构建

首先,我们将使用 node:20-alpine 这个更轻量级的基础镜像来减小最终镜像的大小。Alpine Linux 因其安全、简单且体积小而广受欢迎。

多阶段构建是减少 Docker 镜像大小的有效策略之一。我们将使用三个阶段来构建我们的镜像。

第一阶段:构建依赖项

1
2
3
4
5
6
7
8
9
10
11
ARG NODE_VERSION=node:20-alpineFROM $NODE_VERSION AS dependency-baseWORKDIR /appRUN npm install -g pnpmCOPY package.json pnpm-lock.yaml ./RUN pnpm install --frozen-lockfile`

这一阶段负责安装我们项目的依赖项。我们使用了 pnpm 来代替 npm,pnpm 在缓存和磁盘使用上更为高效。

大部分项目也用 pnpm 而不是 npm 作为包管理工具了。

第二阶段:构建应用程序

1
2
3
4
5
FROM dependency-base AS production-baseCOPY . .RUN pnpm run build

在这一阶段,我们复制了项目代码并执行构建命令。这里的构建指的是 Nuxt.js 的构建过程,它会生成静态文件和服务器端渲染所需的资源。

第三阶段:生成生产镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
FROM $NODE_VERSION AS productionCOPY --from=production-base /app/.output /app/.outputENV NUXT_HOST=0.0.0.0 \    NUXT_APP_VERSION=latest \    DATABASE_URL=file:./db.sqlite \    NODE_ENV=productionWORKDIR /appEXPOSE 3000CMD ["node", "/app/.output/server/index.mjs"]

最后,我们创建了适用于生产环境的镜像。这个镜像仅包含用于运行应用程序的必要文件,减少了不必要的层,使得镜像尽可能地保持精简。

我们还定义了一些环境变量,比如 NUXT_HOSTDATABASE_URL,这些是 Nuxt.js 应用和 Prisma 所需要的。其中,DATABASE_URL 被设置为使用项目根目录下的 SQLite 文件作为数据库。

最终通过暴露端口 3000 并指定启动命令来运行 Nuxt.js 应用程序。

不同构建方式的镜像大小比较

分别为:

  • 3 步构建
  • 2 步构建
  • 直接构建

a3c345aaa51a4b8b802c25bc9d3591c0.png

Dockerfile 总览

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
# Use a smaller base imageARG NODE_VERSION=node:20-alpine# Stage 1: Build dependenciesFROM $NODE_VERSION AS dependency-base# Create app directoryWORKDIR /app# Install pnpmRUN npm install -g pnpm# Copy the package filesCOPY package.json pnpm-lock.yaml ./# Install dependencies using pnpmRUN pnpm install --frozen-lockfile# Stage 2: Build the applicationFROM dependency-base AS production-base# Copy the source codeCOPY . .# Build the applicationRUN pnpm run build# Stage 3: Production imageFROM $NODE_VERSION AS production# Copy built assets from previous stageCOPY --from=production-base /app/.output /app/.output# Define environment variablesENV NUXT_HOST=0.0.0.0 \    NUXT_APP_VERSION=latest \    DATABASE_URL=file:./db.sqlite \    NODE_ENV=production# Set the working directoryWORKDIR /appEXPOSE 3000# Start the appCMD ["node", "/app/.output/server/index.mjs"]

使用 PNPM 的情况下,Jest 解决 ESM 依赖库的报错问题

环境

  • NX
  • PNPM
  • lodash-es
  • Jest

从 karma 转移到 Jest 遇到了如下报错

主要原因是 “node_modules” 文件夹中 ESM(ECMAScript Modules) 库不被 Jest 支持。

鉴于 Jest ESM 支持还在几乎不可用的试验阶段,而目前我主要是在公司项目上迁移到 Jest。所以本文主要采用 transformIgnorePatternsmoduleNameMapper 两种配置来解决这个问题。

11c629a593c4c8484b6cb8ca44d6aa5f.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Test suite failed to runJest encountered an unexpected tokenJest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.By default "node_modules" folder is ignored by transformers.Here's what you can do:    • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.    • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript    • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.    • If you need a custom transformation specify a "transform" option in your config.    • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.You'll find more details and examples of these config options in the docs:https://jestjs.io/docs/configurationFor information about custom transformations, see:https://jestjs.io/docs/code-transformation

以下配置主要以 lodash-es 作为参考。

transformIgnorePatterns

官方文档的解释是:正则表达式模式字符串的数组,在转换之前与所有源文件路径匹配。如果文件路径与任何模式匹配,则不会对其进行转换。
transformIgnorePatterns 用于指定在进行代码转换时应该忽略的文件或文件夹。

而在 NX 默认的 Jest 配置中,配置为 node_modules/(?!.*\\.mjs$)
这个正则表达式的含义是,匹配以 node_modules/ 开头的文件夹路径,但排除那些以 .mjs 为扩展名的文件夹路径。?! 是一个否定预查,表示不匹配这样的文件夹路径。

1
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],

以上配置意思就是将会把以 .mjs 为扩展名的文件从 ESM 转换为 CommonJS,以支持 Jest。

添加转换 lodash-es

顺便支持一下 PNPM

1
2
3
4
5
6
7
const esModules = ['.*\\.mjs$', 'lodash-es'].join('|');export default {    ...    transformIgnorePatterns: [`node_modules/(?!.pnpm|${esModules})`],    ...}

转换后 failed 数量从 15 减少到 11,但是这么做会有一个转换的过程会有额外的支出,需要 51s。不过第一次转换完后貌似就会缓存然后就不用转换了。

ef4e6aeef369b021b707664f9c03549a.png

支出更少的方法 moduleNameMapper

这种方法需要库本身有对应的 CommonJS,就不需要转换了。可以跑到 12s

1
2
3
4
5
6
7
export default {    ...    moduleNameMapper: {        '^lodash-es$': 'lodash',    },  ...}

e87d8ad99b64c8f836a8c1777ec217bf.png

最终配置参考如下

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
/* eslint-disable */const esModules = ['.*\\.mjs$'].join('|');export default {  displayName: 'pc',  preset: '../../jest.preset.js',  setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],  coverageDirectory: '../../coverage/apps/pc',  moduleNameMapper: {    '^lodash-es$': 'lodash',  },  transform: {    '^.+\\.(ts|mjs|js|html)$': [      'jest-preset-angular',      {        tsconfig: '<rootDir>/tsconfig.spec.json',        stringifyContentPathRegex: '\\.(html|svg)$',      },    ],  },  transformIgnorePatterns: [`node_modules/(?!.pnpm|${esModules})`],  snapshotSerializers: [    'jest-preset-angular/build/serializers/no-ng-attributes',    'jest-preset-angular/build/serializers/ng-snapshot',    'jest-preset-angular/build/serializers/html-comment',  ],};

参考

  1. Jest setup "SyntaxError: Unexpected token export"
  2. Configuring Jest · Jest
  3. ECMAScript Modules · Jest
  4. Configuring Jest · Jest

【前端十万个为什么】V0

为什么用 Composition API

c2f54277b2fb37d52596daa96202d926.png

  1. 使逻辑分离更容易:使用可组合函数可以将组件的逻辑分解为多个较小的部分,不再限制于 Options API 中组织代码,更容易重用有状态逻辑。
  2. 灵活性和代码重用性:可以提取和重用共享逻辑,分离关注点,使代码更加模块化和易于维护。
  3. 实现组件的复用和组合:将逻辑拆分为可重用的部分,使用可组合函数组合组件,避免重复编写代码,提高代码的重用性,减少重复和不一致性的风险。
  4. 更好的可读性和可理解性:每个可组合函数封装了特定方面的行为、方便推理和测试、有助于团队合作,使代码结构化和有组织。
  5. 更好的类型推断:使用变量和标准 JavaScript 函数处理组件逻辑。更容易在使用静态类型系统(如 TypeScript)构建大型 Vue 应用程序时进行类型推断

为什么解构 Proxy 会失去效应性

等待补坑

为什么 Vue 中解构 props 会失去响应性

在 Vue 3 中,当你解构 props 时,可能会丧失响应性。这意味着对 props 的更改不会触发组件的更新。

原因是 Vue 的响应性系统依赖 Proxy 来跟踪对象属性的更改。当组件接收到一个对象作为 props 时,Vue 会为该对象的每个属性设置响应性的 getter 和 setter。这使得 Vue 能够检测属性何时更改并相应地更新组件。

当你解构对象 props 时,实际上是创建了一个不再具有响应性的新对象。Vue 为原始对象创建的响应性 getter 和 setter 不会转移到新对象上。

参考

  1. Vue3 如果解构 props 会失去起响应性导致 setup 里一堆 pros.xxx 怎么办? - 这似谁的小鹿的回答 - 知乎
  2. Vue Tip: Destructure Props in Composition API Without Losing …
  3. How To Destructure Props In Vue 3 Without Losing Reactivity | by Nicky Christensen | Medium

为什么 Vue 项目很少用 RxJS

一句话解释

这是因为 Vue 希望成为一个轻量且灵活的框架,允许开发者选择他们喜欢的工具和库。虽然 RxJS 是一个强大的响应式编程库,但 Vue 采用了不同的方法,提供了自己的响应式系统。

详情点

  • 设计理念不同:Vue 注重简单和直观,便于响应式编程;而 RxJS 功能更强、更复杂,适合异步和事件驱动编程。
  • 库的大小与复杂性:RxJS 库大且学习曲线陡,若作为 Vue 的默认依赖,会增加框架大小和开发复杂性,与 Vue 的轻量和灵活理念不符。
  • 灵活性:Vue 设计为灵活且适应不同的项目需求。不将自己绑定到特定的响应式库(如 RxJS),允许开发人员选择最适合他们需求的工具和库。这种灵活性使开发人员能够无缝地将 RxJS 或其他任何库集成到 Vue 项目中。
  • 学习曲线:Vue 拥有平缓的学习曲线,特别是对于初级开发。通过提供自己的响应式系统,Vue 可以提供更简单、更渐进的学习体验。

参考

  1. Introduction to VueJS and RxJS - This Dot Labs
  2. Integrating RxJS with Vue.js | DigitalOcean
  3. Reactive Programming: The Good and the Bad | goodguydaniel.com
  4. A better practice to implement HTTP client in Vue with RxJS for enterprise Apps | by Pawel Woltschkow | Medium
  5. You might not want Rxjs

JavaScript 总结、比较 V2

Promise 与 RxJS Observables 的区别

Promise

  • Promise 是 JavaScript 中内置的,不需要任何额外的库。
  • Promise 表示可能现在或将来可用的单个值。
  • Promise 是急切的,也就是说一旦 Promise 被解析,.then()回调会立即执行。
  • Promise 只能发出单个值。
  • Promise 非常适合处理产生单个结果的简单异步操作。

RxJS Observables

  • Observables 是 RxJS 库的一部分,需要额外安装依赖。
  • Observable 表示可以随时间发出的值流。
  • Observable 是惰性的,也就是说在订阅之前不会执行任何操作。
  • Observable 可以发出多个值,包括零个或多个值。
  • 可以使用各种 RxJS 操作符对 Observable 进行转换和组合,以创建新的定制流。
  • Observable 非常适合处理复杂的异步操作,例如实时数据流或事件驱动编程。

参考

  1. JavaScript Theory: Promise vs Observable - Medium
  2. angular - What is the difference between Promises and Observables? - Stack Overflow
  3. JavaScript Promises vs. RxJS Observables

模版语法的简单实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const name = 'Nexmoe';const message = 'My name is {{name}} and I\'m {{getAge(20)}} years old.';function getAge(age) {  return age;}const replacedMessage = message.replace(/\{\{(.*?)\}\}/g, (match, variableOrFunction) => {  const trimmedValue = variableOrFunction.trim();  if (trimmedValue.includes('(')) {  // 如果占位符包含括号,则表示为带参数的函数替换    const [functionName, ...args] = trimmedValue.split(/\(|\)/).filter(Boolean);    const func = eval(functionName);    return func(...args);  } else {  // 否则为变量替换    return eval(trimmedValue);  }});onsole.log(replacedMessage);

先检查占位符中是否包含括号,如果包含括号,则表示是一个带参数的函数调用。使用split方法和正则表达式来解析函数名和参数,并将其存储在functionNameargs变量中。然后,使用eval函数将函数名转换为实际的函数对象,并使用扩展运算符 (...) 将参数作为参数列表传递给函数。函数执行后,将返回值作为替换后的字符串返回。

如果占位符不包含括号,则表示是一个变量。直接使用eval函数将变量名转换为实际的变量值,并返回其值作为替换后的字符串。

⚠️ 注意:使用eval函数执行代码具有一定的安全风险,因为它可以执行任意的 JavaScript 代码。有相当多的建议建议不使用eval。准备过段时间研究研究不用eval的方法。

MVVM 是什么

MVVM 代表 Model-View-ViewModel,在 MVVM 中,Model 表示应用程序的数据和业务逻辑,View 表示用户界面,ViewModel 充当 Model 和 View 之间的中介。

模型(Model)

  • 模型代表应用程序中的数据和业务逻辑。
  • 它可以是从服务器获取的数据、本地存储的数据或通过其他方式获取的数据。
  • 模型通常实现了一些方法来操作、存储和管理数据。
  • 对应的是组件中的 data、props 属性。

视图(View)

  • 视图是用户界面的可见部分。
  • 它负责展示数据给用户,并接收用户的交互操作。
  • 在 Vue.js 中,视图通常由 Vue 组件表示,可以包含 HTML 模板和样式。

视图模型(ViewModel)

  • 视图模型是连接模型和视图的中间层。
  • 视图模型通常包含了与视图相关的数据、计算属性和方法,以及与模型交互的逻辑。
  • 通过双向绑定(data-binding)将视图和模型连接起来。当模型中的数据发生变化时,视图会自动更新。通过 DOM 事件监听,当用户在视图中输入数据或进行其他交互操作时,视图模型会自动更新模型中的数据。

优势

  • 分离关注点:将数据逻辑与视图逻辑分离,使代码更易于维护和测试。
  • 提高开发效率:通过双向数据绑定和声明式编程风格,减少了手动操作 DOM 的代码量。
  • 可重用性:通过组件化的方式,视图和视图模型可以在不同的应用程序中进行复用。
  • 响应式更新:当模型中的数据发生变化时,视图自动更新,提供了更好的用户体验。

参考

  1. 为什么尤雨溪尤大说 VUE 没有完全遵循 MVVM? - 知乎
  2. Vue 的 MVVM 思想(包含三个常见面试题) - 掘金
  3. MVC,MVP 和 MVVM 的图示 - 阮一峰的网络日志
  4. Getting Started - vue.js
  5. Comparing Vue.js to new JavaScript frameworks - LogRocket Blog

MVC 是什么

MVC 这个概念已经存在很久了,用了这么多年,今天了解一下概念做个总结。

MVC(Model-View-Controller)设计模式将应用程序中的对象分为三个角色:模型(Model)、视图(View)和控制器(Controller)。该模式不仅定义了对象在应用程序中的角色,还定义了对象之间的通信方式。每种类型的对象都通过抽象边界与其他类型的对象分离,并在这些边界上与其他类型的对象进行通信。应用程序中某种 MVC 类型的对象的集合有时被称为层,例如模型层。

848723f97c7a1b862e10abe0445da348.png

模型(Model)

  • 封装应用程序特定的数据,并定义操作和处理数据的逻辑。
  • 可以表示应用程序中的实体,如游戏中的角色或地址簿中的联系人。
  • 可以与其他模型对象建立关联,形成对象图。
  • 应该存储应用程序的持久状态数据。
  • 不应与呈现数据和用户界面相关的视图对象直接连接。

视图(View)

  • 用户可见的对象,负责显示数据和响应用户操作。
  • 知道如何绘制自身,并可以与用户进行交互。
  • 通常通过控制器对象从模型对象中获取数据进行展示和编辑。
  • 在 MVC 应用程序中与模型对象解耦,提供一致性和重用性。

控制器(Controller)

  • 充当视图对象和模型对象之间的中介。
  • 负责处理用户操作,并将其传递给模型层进行数据处理和更新。
  • 可以执行应用程序的设置和协调任务,管理其他对象的生命周期。
  • 在模型对象发生变化时,将新的模型数据传递给视图对象进行显示。

优势

  • 提供良好的应用程序设计,使对象更具可重用性和接口定义明确性。
  • 支持应用程序的可扩展性,易于添加新功能和模块。
  • 分离关注点,使代码更易于维护和测试。
  • 应用程序的模型层、视图层和控制层之间保持了清晰的分离,实现了代码的结构化和职责的明确划分,从而提高了应用程序的可维护性和可扩展性。

参考

  1. https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html

为什么 Bun 这么快

JavaScriptCore 引擎

Bun 使用 JavaScriptCore 引擎,这是 Safari 浏览器使用的引擎,而不是基于 Chromium 的浏览器和 Node.js 使用的 V8 引擎。JavaScriptCore 引擎经过了针对更快启动时间的优化,这有助于 Bun 的速度。

性能分析和优化

大量的性能优化。Bun 的初衷就是要快。[2]

Zig 语言

Bun 利用 Zig 语言进行低级内存控制和消除隐藏控制流。Zig 的设计原则注重性能,通过利用 Zig,Bun 可以实现更好的内存管理和控制,从而提高速度 [2]

参考

  1. Bun 1.0 | Bun Blog
  2. A first look at Bun: is it really 3x faster than Node.js and Deno? - DEV Community

自建 Sentry 使用 script 启用时无效

最近在服务器上面自建了 Sentry。

用 script 方法加入到网站后,始终没有效果,然后在控制台中发现了下面的报错。

The Sentry loader you are trying to use isn’t working anymore, check your configuration.

于是去 Github 上找了下,看着应该是国内网络的问题。

在 sentry/sentry.conf.py 内添加下面内容可以解决。

1
JS_SDK_LOADER_DEFAULT_SDK_URL = "https://browser.sentry-cdn.com/%s/bundle.tracing.replay.debug.min.js"

然后记得重启 Sentry 的 Docker 服务

1
2
sudo docker compose restartsudo docker compose up -d

动画 PNG(APNG) 转 GIF 并无限循环

今天在网上找了些 PNG 格式的动态表情包我是不会告诉你是我是在 LINE 偷的表情包的,于是了解到是 APNG 这种格式。由于微信和 QQ 不支持 APNG,所以就把 APNG 转为 GIF 了,在使用 APNG 转换成 GIF 后,发现在微信上只能播放一次,就产生了如何批量修改 GIF 的循环次数的问题。

所以准备简单介绍一下 APNG。并提供了一个在线工具,可以将 APNG 批量转换为 GIF,但是该工具不能实现无限循环。所以分享了一个批量修改 GIF 循环次数的方法,使用了 Node.js 和批处理脚本两种不同的实现方式。方便 Node 开发者和使用 Windows 的普通用户直接批量处理。

APNG 是什么?

APNG(Animated Portable Network Graphics)是 PNG 的位图动画扩展,可以实现 PNG 格式的动态图片效果。APNG 相比于 GIF 在图片质量和细节表现方面更有优势,而且随着越来越多的浏览器对 APNG 的支持,它有望成为下一代动态图的标准之一。主要有以下区别:

  1. 图片质量:GIF 最多支持 256 种颜色,并且不支持 Alpha 透明通道,这导致 GIF 在色彩丰富的图片和含有半透明效果的图片上质量较差。而 APNG 可以支持更高质量的图片,包括更多的颜色和 Alpha 透明通道,使得动画效果更加细腻。

  2. 构成原理:APNG 和 GIF 都是由多帧构成的动画,但是 APNG 的构成原理允许超自然向下兼容。APNG 的第一帧是标准的 PNG 图片,即使浏览器不认识 APNG 后面的动画数据,也可以无障碍显示第一帧。而如果浏览器支持 APNG,就可以播放后面的帧,实现动画效果。

  3. 浏览器支持:从 Chrome 59 开始,Chrome 浏览器开始支持 APNG,使得大部分浏览器都能显示 APNG 动画。唯独 IE 浏览器不支持 APNG。

更多内容请参考:https://xtaolink.cn/268.html

APNG 批量转 GIF

该工具可以批量将 APNG 转为 GIF,不过不能无限循环。

https://cdkm.com/cn/png-to-gif

批量修改 GIF 为无限循环

bat(普通用户请使用该方法)

下面是使用批处理脚本(.bat)来实现相同的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@echo offsetlocal enabledelayedexpansionset "directoryPath=C:\path\to\directory"for /r "%directoryPath%" %%f in (*.gif) do (    echo Modifying %%~nxf    call :modifyGif "%%f")exit /b:modifyGifset "filePath=%~1"set /p data=<"%filePath%"set "index=!data:~0,16!"set "modifiedData=!data:~0,16!!data:~16,1!!data:~17,1!!data:~19!"echo.!modifiedData!>"%filePath%"exit /b

请将C:\path\to\directory替换为实际的目录路径。将上述代码保存为.bat文件,双击运行即可。脚本将遍历指定目录下的所有.gif文件,并对其进行修改。

请注意,批处理脚本的功能相对有限,无法直接读取二进制文件。上述脚本通过读取文件的第一行来模拟读取文件内容。在修改文件时,它直接将修改后的数据写入文件,而不进行二进制操作。这种方法可能不适用于所有情况,尤其是处理大型文件时可能会有性能问题。如果需要更复杂的二进制文件处理,请考虑使用其他编程语言或工具来实现。

Node(Nexmoe 使用的该方法)

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
const fs = require('fs');const path = require('path');function unlimitedGifRepetitions(path) {  const data = fs.readFileSync(path);  const index = data.indexOf(Buffer.from([0x21, 0xFF, 0x0B]));  if (index < 0) {    throw new Error(`Cannot find Gif Application Extension in ${path}`);  }  data[index + 16] = 255;  data[index + 17] = 255;  return data;}function batchModifyGifFilesInDirectory(directoryPath) {  fs.readdir(directoryPath, (err, files) => {    if (err) {      console.error('Error reading directory:', err);      return;    }    files.forEach(file => {      const filePath = path.join(directoryPath, file);      const fileExtension = path.extname(file);      if (fileExtension === '.gif') {        try {          const modifiedData = unlimitedGifRepetitions(filePath);          fs.writeFileSync(filePath, modifiedData);          console.log(`Modified ${file}`);        } catch (error) {          console.error(`Error modifying ${file}:`, error);        }      }    });  });}const directoryPath = './path/to/directory';batchModifyGifFilesInDirectory(directoryPath);

请注意,上述代码使用了 Node.js 的文件系统模块(fs)来读取和写入文件。此外,需要将./path/to/directory替换为实际的目录路径。在执行该脚本之前,请确保已经安装了 Node.js。

该脚本将批量遍历指定目录下的所有文件,并对后缀名为.gif的文件调用unlimitedGifRepetitions函数进行修改。修改后的数据将写回原始文件。在控制台输出中,你可以看到每个已修改的文件的信息或任何出现的错误信息。

更多内容参考:https://www.b612.me/golang/232.html

更好的工具

这个批处理工具可以将多个 APNG 文件批量转换为 GIF 文件,并且可以对转换后的 GIF 文件批量设置为无限循环。

https://github.com/nexmoe/batch-apng2gif

用 VS Code 管理服务器,我有独特的服务器管理方式

自从 Typora 开始收费之后,我彻底转向使用 VS Code(Visual Studio Code) 进行文章撰写。作为一款 Markdown 编辑器,它已经完全满足了我的需求。

然而,我开始思考是否可以将 Visual Studio Code 用作服务器管理器。在此之前,我一直使用 FinalShell 进行服务器管理,这款工具具备以下特点,也是我所喜欢的:

  • 简单的服务器资源监控
  • 服务器文件浏览
  • 终端功能
  • 多服务器管理

当 VS Code 发布 Remote SSH 功能时,我并没有太过关注。该功能的初衷是用于远程开发,而我并没有远程开发的需求。然而,当我开始进行远程开发时,我决定尝试使用 Remote SSH。结果令我惊喜地发现,Remote SSH 非常适合用于服务器管理工作。

那么,VS Code 有哪些特点,并且满足了我哪些需求呢?

VS Code 的特点

多服务器管理

通过在你需要访问的远程服务器中配置好 SSH Key,便可以轻松地设置 Remote SSH,并指定服务器的 IP 地址,从而直接通过 Remote SSH 访问服务器。

1
2
3
4
5
6
7
Host server1    HostName 服务器 1 的 IP 地址    User 用户名Host server2    HostName 服务器 2 的 IP 地址    User 用户名

通过这样的配置,然后就可以方便地在 VS Code 中访问和管理多个服务器。只需单击服务器列表中的相应服务器,即可快速连接到目标服务器,并在远程环境中执行所需的操作。

文件浏览器

和正常一个项目的使用一样,VS Code 可以打开服务器的一个文件夹。然后我们就可以进行各种各样的文件浏览器的常规操作。还可以使用搜索功能快速查找和定位特定的文件。

我通常就会直接把账号目录直接打开。

同时还可以直接集成 VS Code 本身拥有的强大的文件编辑能力。

从此告别 lsmkdirtouchvi 等操作。

方便快捷的终端体验

可以同时打开多个终端实例,并在它们之间切换。每个终端实例都可以独立运行命令,并保留其输出历史记录。

还可以在编辑器的多个标签页或分割视图中同时打开不同的终端实例,以便同时执行不同的命令。

除此之外,在 VS Code 中,可以通过右键单击文件或文件夹,选择"在终端中打开",快速打开终端并自动切换到对应的路径。再也不用痛苦的到处 cd 了。

Docker GUI 管理

由于我大部分服务都是放在 Docker 上,所以对于 Docker 的使用是非常高频的。

如果你在服务器上使用 Docker 进行容器化管理,VS Code 通常就会推荐你安装 Docker 扩展,然后就可以并以图形化界面的形式管理和操作 Docker 容器。可以方便地查看和管理容器、镜像、网络和卷等 Docker 资源,执行常见的 Docker 命令,以及监控容器的状态和日志。

这基本覆盖了大部分 Docker 常用的操作,也让我大部分时间不再需要使用 Portainer 或是命令行了。

Git GUI 集成

VS Code 提供了强大的 Git GUI 集成功能,可以在代码仓库上进行版本控制和协作。可以直接通过 GUI 进行查看提交历史、比较文件差异、切换分支、合并代码,以及推送和拉取代码等操作。

丰富的个性化主题、插件生态

如果本身就使用 VS Code,便可以直接继承自己的审美。同时 VS Code 比其他终端管理软件拥有更为丰富丰富的个性化主题选项,可以根据自己的喜好和习惯选择适合的主题。也具有丰富的插件社区。

其它

除此之外,VS Code 本身就具有跨平台支持、多语言支持等特性,而且它完全免费。

如何使用 VS Code 进行服务器管理

你需要满足如下条件

  • 能够运行 VS Code 的电脑
  • 正版的 VS Code
  • 生成 SSH Key 并完成配置
  • Remote SSH 插件

其实你需要的并不多,核心是 VS Code 中的 Remote SSH 插件。

配置 SSH Key

你需要在你的电脑上生成 SSH Key,并将公钥配置到服务器上以实现免密登录。你可以在网上查找相关教程了解如何生成和配置 SSH Key。

配置 Remote SSH

通过 这个链接 安装 Remote SSH 后。

你可以按照以下步骤来打开和配置 SSH 主机:

  1. 在 VS Code 左下方找到 Remote SSH 的图标按钮。
  2. 点击该按钮,然后选择 “Connect to Host” 选项。
  3. 再次点击 “Configure SSH Hosts”。

就可以在 VS Code 中进行配置,指定服务器的 IP 地址和用户名。示例配置如下:

1
2
3
4
5
6
7
Host server1    HostName 服务器 1 的 IP 地址    User 用户名Host server2    HostName 服务器 2 的 IP 地址    User 用户名

完成配置后便可以在左下角直接连接服务器了。

插件以及其他推荐

Monitor Pro 插件

资源监控,由我开发。

Monitor Pro 是一款资源监控工具,实时跟踪系统指标。监测 CPU、内存、网络、文件系统使用率,电池百分比和充电状态。可自定义顺序和刷新间隔,提供高占用警报。适用于开发人员、系统管理员和普通用户。

https://marketplace.visualstudio.com/items?itemName=nexmoe.monitor-pro

Docker 插件

如果你在使用 Docker 容器来部署应用程序,这个插件可以帮助你在 VS Code 中管理和调试 Docker 容器。

Wakatime 插件

统计你在服务器上的摸鱼时间。

zsh 与 ohmyzsh

使用 ohmyzsh 以及它的生态,为你的命令行集成自动补全和纠错功能。

累积布局偏移优化 CLS 完全指南

什么是布局偏移

一个十几秒的短视频解释清楚。

更详细的解释是:布局偏移指的是在网页上发生突然变化时,页面中的内容位置发生意外移动的现象。这种情况常常让人感到困扰,因为它会导致阅读中断或误操作。布局偏移通常是由于资源异步加载或动态添加到页面上的 DOM 元素导致的。可能的原因包括具有未知尺寸的图像或视频、字体与其备用字体渲染大小不同,或者第三方广告或小部件动态调整大小。

难受的是,网站在开发过程中的功能通常与用户体验有很大不同。个性化或第三方内容在开发中的行为通常与生产环境中不同,测试图像通常已经存在于开发者的浏览器缓存中,本地运行的 API 调用通常非常快,延迟几乎不可察觉。

什么是 CLS

累积布局偏移 CLS(Cumulative Layout Shift)是一个指标。

是对页面整个生命周期中发生的每个意外布局变化的最大布局变化分数的度量。

CLS 通过测量实际用户遇到布局偏移的频率来帮助解决布局偏移问题。它可以帮助开发者了解布局偏移在真实用户中发生的情况,从而采取相应的措施进行修复。

为什么要优化 CLS

布局偏移是一个非常影响用户体验的问题,通过上面那一个简短的视频也能理解。

布局偏移通常会导致意外点击、页面方向的迷失,最终导致用户受挫。用户往往不会逗留太久。有时也会使用户不按照预计的产品流程走。

通常优化好布局偏移能够很好的提高用户粘性、用户停留时间等指标。

Yahoo! JAPAN News 通过降低 CLS 0.2 分,得到如下成果。

如何降低 CLS

图片等媒体元素占位

在图像、视频等媒体资源元素中始终包含宽度和高度大小属性。或用 CSS 中的 min-heightaspect-ratio 或类似的方式保留所需的空间。

aspect-ratio

可以用来直接指定当前元素的比例。

https://developer.mozilla.org/zh-CN/docs/Web/CSS/aspect-ratio

对浏览器的支持:

padding-bottom

如果考虑浏览器支持问题仍然可以考虑使用目前一个被广泛接受的基解决方案 “Padding-Top Hack”。这个解决方案需要一个父元素和一个绝对型的子元素。然后计算出长宽比的百分比来设置为 padding-top。例如:

1
2
3
<div class="container">  <img class="media" src="..." alt="..."></div>
1
2
3
4
5
6
7
8
9
10
.container {  position: relative;  width: 100%;  padding-top: 56.25%; /* 16:9 Aspect Ratio */}.media {  position: absolute;  top: 0;}

使用不易产生偏移的 CSS

其中 transfrom 表现很好,以下举几个例子。
用例可以在这里找到:https://play.tailwindcss.com/26PxFA6UVI

zoom VS transform: scale

zoom 会撑大页面并向右偏移时,transform: scale 只是在原地放大。

margin VS transform: translate

margin 造成父元素变大,transform: translate 只是让当前元素移动。

border VS box-shadow

border 会撑起父元素,而 box-shadow 并不会。

小心你的懒加载

懒加载会引起布局的偏移,如果你在有懒加载长列表的里进行跳转,请小心!
无动画进行跳转,能够一定程度上避免该问题。

小心使用 transition: all

在页面首次加载或者跳转页面时,transition: all 可能会导致元素的 padding 等从参数为 0 开始渲染,照成页面的抖动。

这都是痛:
Commit:表格以及友情链接图标抖动
Commit:修复导航栏抖动问题

标签顺序导致的偏移问题

由于在移动端上优先展示主要内容,因此侧边栏的 markup 位于主要内容的后面;而在更大的屏幕上,则通过设置 CSS order 的方式进行排序,将主要内容移到中间(即第二列),伪代码如下:

1
2
3
4
5
6
7
8
9
export default function MainLayout(props) {  return (    <Container>      <Main className={css`@media screen and (min-width: breakpoint) { order: 0 }`} />      <Left className={css`@media screen and (min-width: breakpoint) { order: -1 }`} />      <Right className={css`@media screen and (min-width: breakpoint) { order: 1 }`} />    </Container>  )}

浏览器在首次绘制时并没有完整解析 DOM、只知道 <Main /> 的存在、但不知道 <Left /> 或者 <Right /> 的存在,才因此将 <Main /> 渲染进第一列而不是第二列;直到第二次绘制时,浏览器才将 <Main /> 渲染进第二列、将 <Left /> 渲染进第一列。

Chrome 并不是一次完整解析 HTML 的,在以下两种情况下,Chrome 会暂停解析、开始渲染和绘制:

  1. Chrome 解析器在读取了 65535 字节的 HTML 后暂停
  2. Chrome 在遇到 <script> 标签后,会继续读取约 50 个「Token」之后暂停

详细了解请看:优化博客的累计布局偏移(CLS)问题

网页跳转与前进后退缓存

默认情况下,所有浏览器都使用 bfcache,但由于各种原因,有些站点不适合使用 bfcache。有关如何测试和识别阻止 bfcache 使用的任何问题的更多详细信息,请阅读 bfcache 文章

在你离开后,bfcache 将页面保存在浏览器内存中很短的一段时间,所以如果你返回它们,那么它们将完全恢复为你离开时的样子。这意味着完全加载的页面立即可用,而不会出现任何变化。

现在的 SPA 应用也能很轻易的保证路由跳转页面布局的一致性。记住始终保持你的目录和导航栏在页面的固定位置。

字体

在下载和渲染网络字体之前,通常有两种处理方式:

  1. 使用网络字体替代备用字体(FOUT——未样式化文本的闪烁)。
  2. 使用备用字体显示“不可见”文本,直到网络字体可用并且文本可见(FOIT——不可见文本的闪烁)。

着两种方式都可能导致布局变化。即使文本是不可见的,它仍然使用备用字体进行布局。这意味着使用该字体的文本块以及周围的内容在网络字体加载时会发生布局变化,与 FOUT 的可见字体完全相同。

以下方法可以帮助你最小化这种问题:

  1. 使用 font-display: optional 可以避免重新布局,因为只有在初始布局时网络字体可用时才会使用它。
  2. 使用匹配度高的备用字体。例如,使用 font-family: "Google Sans", sans-serif; 将确保在加载"Google Sans"字体时使用浏览器的无衬线备用字体。如果只使用 font-family: "Google Sans" 而不指定备用字体,将使用默认字体,而在 Chrome 上默认字体是"Times",它是比默认无衬线字体的匹配度更差。
  3. 使用新的 size-adjustascent-overridedescent-overrideline-gap-override API 来尽量减小备用字体和网络字体之间的大小差异,详细信息请参阅“Improved font fallbacks”文章。
  4. 使用 Font Loading API 可以减少获取所需字体的时间。
  5. 使用 <link rel=preload> 尽早加载关键的网络字体。预加载的字体有更高的机会达到首次绘制,这样就不会发生布局变化。
  6. 阅读有关字体最佳实践的“Best practices for fonts”文章。

使用真正的骨架屏

骨架屏好坏示例

测量 CLS 分数

生产阶段

实验阶段

Lighthouse in DevTools

能够针对移动设备和桌面设备生成网页的实际性能报告,并能够提供关于如何改进相应网页的建议。

在本地开发期间从 DevTools 运行 Lighthouse 非常方便。

PageSpeed Insights

应该就是在线版的 Lighthouse。

Performance in DevTools

性能选项卡在 Chrome 的 DevTools 配置文件的所有页面行为在一段时间内记录。时间轴上会出现一个标记为“Experience”的图层,突出显示布局的变化和发生变化的元素。

Web Vitals extension

最好将 Web vital 扩展视为查找性能问题的抽查工具,而不是全面的调试工具——这是 Chrome 的 DevTools 中的性能选项卡的工作。

结语

作为一个对自己项目有较高要求的人,平常几乎都会接触到布局偏移优化或者 Lighthouse,只不过之前自己瞎折腾的时候还没有 CLS 这个概念,现在算是对 CLS 有了较为清晰的概念了。
CLS 作为一个非常基础的优化指标,在用户体验上非常重要,任何项目都应该针对 CLS 做优化。

如有勘误,请及时指出,感谢!

参考

  1. https://web.dev/cls/
  2. https://web.dev/optimize-cls
  3. https://developers.google.com/publisher-tag/guides/minimize-layout-shift
  4. https://web.dev/yahoo-japan-news/
  5. https://addyosmani.com/blog/infinite-scroll-without-layout-shifts/
  6. https://blog.skk.moe/post/fix-blog-cls/
  7. https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio
❌