跳至主要内容

博文

Node.js + TypeScript 服务端开发指南

面向已掌握 PHP、Go、早期(ES5 之前)JavaScript 的开发者 本文不从"变量是什么""函数是什么"讲起,而是假设你已经具备成熟的后端工程经验。每个新概念都会尽量与 PHP 或 Go 中你熟悉的对应物做类比,帮你用最短路径完成知识迁移。目标是让你能够独立设计、编写、测试、部署一个生产级的 TypeScript + Node.js 服务端应用。 目录 心智模型:Node.js 到底是什么 环境搭建与版本管理 现代 JavaScript 速通(ES5 → ES2024) TypeScript 核心 模块系统:CommonJS vs ESM 包管理与工程化 服务端开发实战 数据持久化 异步编程深入 测试 部署与运维 安全基线 进阶学习路径 附录:PHP / Go / Node 速查表 0. 心智模型 先建立一个准确的类比,后面所有内容都建立在这个模型上。 维度 PHP(传统 PHP-FPM) Go Node.js 执行方式 每个请求由 worker 进程处理,请求结束后大部分状态销毁 编译为原生二进制,进程常驻,goroutine 并发 进程常驻, 单线程事件循环 + 系统级异步 I/O 并发模型 多进程/多线程(由 FPM 管理) 多核并行,goroutine + channel,由调度器抢占 单线程跑你的 JS 代码,I/O 交给 libuv 线程池/内核异步接口,通过事件循环回调结果 一次请求阻塞会怎样 只影响这一个 worker 只影响这个 goroutine 会阻塞整个进程的所有请求 (这是 Node 新手最常踩的坑) 类型系统 弱类型,PHP8 起可选类型声明 强类型,编译期检查 JS 本身无类型;TypeScript 是 编译期 类型层,编译后类型信息完全擦除 部署产物 源码 + PHP 解释器 单个静态二进制 源码/编译后 JS + Node 运行时(除非用 node --compile-cache 或打包成单文件可执行文件) 关键心智转变 :在 Go 里你用 goroutine 换取并行;在 Node 里你 没有 并行(除非显式用 Worker Threads),你...
最新博文

Laravel 框架详解(附 Yii 对照)

Laravel 框架详解(附 Yii 对照) 本文以 Laravel 为主线,系统介绍其基本用法;Yii(以主流的 Yii 2 分支为准)仅在后文以对照方式说明与 Laravel 的差异,不再单独展开完整介绍。内容主要参考 Laravel 官方文档(laravel.com/docs/12.x)与 Yii 官方文档(yiiframework.com/doc/guide/2.0、release-cycle 页面)整理而成。 目录 Laravel 概览 安装与环境要求 目录结构 路由(Routing) 控制器(Controller) 数据库与 ORM:Eloquent 视图与模板引擎:Blade 表单验证 命令行工具:Artisan 认证与权限控制 生态与工具链 Yii 对照:与 Laravel 的主要差异 核心区别对比表 如何选择 参考资料 1. Laravel 概览 Laravel 由 Taylor Otwell 于 2011 年创建,定位是"优雅、富有表达力的语法",目标是把日常 Web 开发中重复、繁琐的工作(路由、鉴权、缓存、队列、会话等)封装为简洁一致的 API。它是目前 PHP 生态中最流行、社区最庞大的全栈框架,背后由商业公司 Laravel LLC 持续投入,围绕核心框架形成了一整套官方付费/开源工具链:Forge(服务器管理)、Vapor(Serverless 部署)、Nova(后台管理面板)、Envoyer(零停机部署)、以及 Livewire、Inertia.js、Cashier、Horizon、Octane 等专项扩展包。 当前主线版本为 Laravel 12 (2025 年发布),最低要求 PHP 8.2 。框架遵循 MVC 架构,基于 Composer 管理依赖,内置服务容器(IoC Container)、数据库迁移、队列、事件系统等,使用 MIT 许可证开源。 2. 安装与环境要求 # 使用 Composer 创建项目 composer create-project laravel/laravel example-app # 或使用官方安装器(需先全局安装 laravel/installer) laravel new example-app cd exa...

MySQL EXPLAIN 怎么看

MySQL EXPLAIN 怎么看 EXPLAIN 输出主要看这几个关键字段,我按重要性和常见坑点来讲。 核心字段速览 字段 含义 id 查询的执行顺序标识,id 越大越先执行(子查询、派生表会有不同 id) select_type 查询类型:SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION 等 table 当前这一行对应的表 type 最重要 ,访问类型,反映查询效率 possible_keys 可能用到的索引 key 实际用到 的索引 key_len 索引使用的字节数,可以判断联合索引用了几列 rows 预估扫描行数(估算值,非精确) filtered 按条件过滤后剩余行的百分比 Extra 第二重要 ,很多关键信息都在这里 type 字段:从好到差的排序 system > const > eq_ref > ref > range > index > ALL system/const :主键或唯一索引等值查询,最快,基本只在单表单行时出现 eq_ref :多表连接时,被驱动表用主键/唯一索引做等值匹配(比如 a.id = b.a_id 且 b.a_id 是唯一索引) ref :普通索引的等值查询,可能返回多行 range :索引范围扫描,比如 > 、 < 、 BETWEEN 、 IN index :索引全扫描(遍历整棵索引树),比 ALL 好但依然要扫全部索引 ALL :全表扫描,一般需要优化 经验标准 :一般要求至少达到 range 级别,最好是 ref 或以上,看到大表出现 ALL 基本就要警觉了。 Extra 字段:常见情况解读 1. Using filesort(需要额外排序) 出现在 ORDER BY 无法利用索引顺序时,MySQL 需要在内存或磁盘中额外排序。 常见诱因 : - ORDER BY 的列没有索引 - 联合索引使用顺序和 ORDER BY 不一致 - WHERE 条件用了索引,但排序列不在索引里,且中间断了顺序性 优化思路 :建立能同时覆盖 WHERE 和 ORDER BY 的联合索引,让索引顺序和排序顺序一致。 2...

优惠券规则引擎算法核心

规则引擎的"算法"其实不是一个单一算法,而是三个简单机制拼起来的: 一、执行模型:责任链 + 短路求值 规则不是把所有维度一次性打分再综合判断,而是按类型(领取/门槛/叠加/计价)分成一条条链,链上的规则按优先级排好顺序,从头开始逐条求值。只要有一条不通过,立刻停止,后面的规则根本不会被执行。这样做两个好处:一是省计算,大部分场景第一条不通过的规则往往排在前面就能拦住;二是"因为哪条规则被拒绝"天然有唯一确定的答案,不会出现好几条规则都不通过、不知道该给用户看哪个原因的情况。 二、表达模型:统一接口 + 两种求值方式 所有规则背后都实现同一个"输入上下文、输出通过与否"的接口,但内部求值方式不同。结构化规则(互斥组、数量上限)本质是直接在代码里写好的逻辑判断,输入什么类型就按什么逻辑走。表达式规则则是运行时的一个小型解释器:配置的字符串在编译阶段被解析成一棵语法树,求值时对着这棵树递归计算,碰到"订单金额""用户等级"这类字段引用,就去当前上下文里取对应的值代进去。因为两者对外都是同一个接口,链上可以随意混排,规则引擎本身不需要关心某条规则到底是硬编码的还是配置出来的。 三、一致性模型:整体编译 + 原子替换 规则变更不是"改一条生效一条",而是把这一批新规则 整体 重新编译成一份全新的、不可变的规则集合,编译全部成功才用一个原子指针把"当前生效的规则集合"整体切换过去。切换的一瞬间,正在处理中的老请求仍然用切换前的那份规则跑完,不会出现跑到一半规则变了的中间状态;读取规则的一方也完全不需要加锁,只是读一个指针。这本质上是一种"整体重建、原子替换"的无锁方案,好处是新规则里哪怕只有一条写错导致编译失败,也是整批回退,不会出现新旧规则混杂生效的情况。 四、冲突检测:集合求交 规则之间是否矛盾,判断方式很朴素——把每个券模板所在的互斥组做成一个"模板 → 它所属的组"的索引,两个模板是否互斥,就看它们各自所在的组集合有没有交集;而"必须同时使用"和"不能同时使用"这两组模板 ID,如果本身就有重叠,那就是配置自相矛盾,直接在提交时拦截,不...

go编译参数:race,trace和逃逸分析

可以把 Go 这三个东西理解成分别回答三个完全不同的问题: 工具 核心问题 关注对象 常用命令 Race 「有没有多个 goroutine 在同时乱改数据?」 并发内存访问 go test -race ./... Trace 「程序运行时到底发生了什么?」 Goroutine、调度、GC、阻塞 go test -trace=trace.out 逃逸分析 「这个变量为什么跑到堆上去了?」 内存分配 go build -gcflags="-m" 1. Race:查并发 Bug var n int go func () { n++ }() go func () { n++ }() Race Detector 会告诉你: Goroutine A 正在写 n Goroutine B 同时也在读/写 n ↓ DATA RACE 它关注的是 内存访问之间有没有正确同步 。 2. Trace:查运行时行为 例如你的服务突然: CPU 很高 ↓ 到底是哪个 goroutine 在跑? ↓ 是不是 GC? 是不是锁阻塞? 是不是 syscall? 是不是网络等待? Trace 会把运行过程记录下来,然后: go tool trace trace.out 你可以看到 Goroutine 的运行、阻塞、唤醒,以及 GC、调度器等信息。 所以它更像: 给 Go 程序装一个“黑匣子”,看看程序运行过程中到底发生了什么。 3. 逃逸分析:查内存分配 func foo () * int { x := 10 return &x } 编译器发现: x escapes to heap 因为 foo() 返回以后 x 还必须存在。 另一个例子: func foo () { x := 10 fmt.Println(x) } 编译器可能判断 x 不需要逃逸到堆。 它主要回答: 这个对象能不能安全地留在栈上?如果不能,就放堆上。 最简单的记忆方式 Race ↓ 并发有没有撞车? 🚗💥 Trace ↓ 程序运行时发生了什么? 🔍 Escape Analysis ↓ 变量到底该放栈还...