尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

前端2秒生成500页矢量PDF:Rust+WASM+Web Worker实战解析

前端2秒生成500页矢量PDF:Rust+WASM+Web Worker实战解析 1. 这个标题到底在说什么第一次看到“前端2秒生成500页矢量PDFrust真的强到没朋友”这个标题我的反应是又是一个标题党。前端生成PDF这件事做过的人都知道有多痛苦。jsPDF慢得像蜗牛pdfmake遇到复杂表格直接卡死html2canvas转出来的东西放大就糊。500页2秒矢量这三个词放在一起放在浏览器环境里放在前端听起来就像在说“我用自行车三分钟从北京骑到了上海”。但仔细一想这个标题背后其实藏着一个非常具体的技术组合Rust WebAssembly Web Worker 前端PDF生成。它不是空穴来风而是这几年前端圈一个真实存在的技术路线。Rust编译到WASM之后在浏览器里跑计算密集型任务性能确实能把纯JS按在地上摩擦。而PDF生成恰好就是一个计算密集型任务——排版、字体嵌入、图形绘制、压缩编码每一步都在烧CPU。所以这篇文章不是来吹Rust的也不是来贬JS的。我想做的是把这个标题拆开看看“2秒500页矢量PDF”这件事在技术上到底是怎么成立的哪些环节是Rust的功劳哪些环节是架构设计的功劳以及如果你真的要在项目里落地这套方案需要注意什么。适合读这篇文章的人前端开发者、对WASM感兴趣但还没动手的人、正在被PDF生成性能问题折磨的人、以及单纯想知道“Rust是不是真的那么强”的人。不管你是刚入门前端还是已经写了五年代码我都会尽量把每个环节讲清楚让你能看懂、能判断、能复现。2. 为什么前端生成PDF一直是个老大难2.1 纯JS方案的天花板在哪里先说说我们以前是怎么干的。最常见的方案是jsPDF配合html2canvas把DOM截成图片再塞进PDF。这个方案的问题非常明显生成的PDF本质上是图片集合不是矢量图。你放大到200%文字边缘就开始发虚。而且html2canvas本身性能就差一个稍微复杂点的页面截一张图就要几百毫秒500页你算算要多久。另一个方案是pdfmake它支持矢量输出底层用的是PDFKit的JS移植版。但pdfmake的排版引擎是纯JS写的遇到大量文本换行计算、表格单元格宽度计算、字体度量这些操作性能下降得非常厉害。我实测过一个200页左右的报表pdfmake在Chrome里跑了将近40秒内存直接飙到1.5GB页面差点崩掉。还有一条路是走服务端前端把数据传给后端后端用wkhtmltopdf或者Puppeteer生成PDF再传回来。这个方案性能是好但依赖网络用户体验割裂而且服务器成本摆在那里。对于需要离线使用或者对隐私敏感的场景根本走不通。所以前端PDF生成的核心矛盾一直是矢量输出需要大量计算而浏览器的JS引擎在计算密集型任务上先天不足。这不是代码写得好不好的问题是语言层面的限制。2.2 矢量PDF到底难在哪里很多人不理解为什么生成矢量PDF这么慢。我打个比方生成图片PDF就像拍照咔嚓一下把像素点存下来就行。生成矢量PDF就像画工程图你得告诉PDF阅读器“这里有一条线从坐标(10,20)到坐标(300,450)线宽0.5颜色是#333”阅读器再根据这些指令去渲染。这意味着每一个文字、每一条线、每一个矩形都要被转换成PDF的绘图指令。500页的文档假设每页有2000个文字对象和500个图形对象那就是125万个对象需要被序列化、编码、压缩。这还没算字体嵌入——为了让PDF在任何设备上都能正确显示中文你得把字体文件子集化后嵌入进去这个子集化过程本身就是一次复杂的计算。更麻烦的是排版。PDF不像HTML没有自动换行、没有flex布局、没有margin collapse。每一行文字的位置、每一个段落的间距、每一个表格的列宽都得你自己算。文本度量需要读取字体的字形宽度表这个表可能有几万个条目。纯JS做这件事就像用算盘算导弹轨道不是不能算是太慢了。2.3 RustWASM为什么能破局Rust在这里的角色很明确把计算密集的部分从JS手里接过来用接近原生的速度跑。Rust编译成WebAssembly之后在浏览器里的执行效率通常能达到原生代码的70%到90%而纯JS在同样的计算任务上可能只有原生代码的10%到20%。这个差距在大量循环和复杂数据结构操作时会放大到几十倍。但光有Rust还不够。WASM模块跑在主线程里照样会阻塞UI。所以还需要Web Worker把WASM模块放到后台线程去跑。主线程负责接收用户输入、展示进度条Worker线程负责闷头算PDF。算完了把结果传回来主线程触发下载。这套组合拳打下来才能实现“2秒生成500页”的同时页面还不卡。我后面会详细拆解这套架构的每个环节包括Rust侧怎么写、WASM怎么编译、Worker怎么通信、PDF指令怎么生成。但在此之前我想先讲清楚一个判断不是所有PDF生成场景都值得上RustWASM。如果你的文档只有几页或者对矢量输出没有硬性要求用JS方案完全够用。杀鸡用牛刀维护成本反而更高。3. 核心架构拆解从数据到PDF的完整链路3.1 整体数据流设计这套方案的数据流是这样的前端拿到结构化数据通常是JSON通过postMessage传给Web WorkerWorker里加载WASM模块把JSON解析成Rust的数据结构Rust侧执行排版计算、字体子集化、PDF指令生成、压缩编码最终产出一个Uint8Array传回主线程主线程用Blob包装后触发下载。这个链路里有两个关键设计决策。第一数据序列化用JSON而不是共享内存。SharedArrayBuffer虽然更快但需要跨域隔离头部署成本高而且调试困难。JSON序列化在500页数据量下大概消耗几十毫秒相对于整体2秒的耗时可以接受。第二WASM模块在Worker里初始化一次多次复用。每次生成PDF都重新加载WASM会浪费几百毫秒所以Worker应该常驻WASM实例缓存起来。3.2 为什么选Rust而不是C或Go能编译到WASM的语言不止Rust一家。C有EmscriptenGo有TinyGoAssemblyScript也能用。但Rust在这类场景下有幾個明显优势。首先是内存安全。PDF生成涉及大量缓冲区操作、字符串拼接、二进制编码C写这类代码很容易出现越界或悬垂指针。Rust的所有权系统在编译期就把这些坑堵住了虽然写的时候会跟borrow checker打架但跑起来之后基本不用担心内存问题。其次是工具链成熟。wasm-pack和wasm-bindgen这套工具让Rust和JS的互操作变得非常顺滑。你可以直接在Rust里定义被JS调用的函数也可以把Rust结构体暴露给JS。相比之下C的Emscripten配置起来要繁琐得多。第三是生态。Rust有printpdf、lopdf、pdf-writer这些专门处理PDF的crate也有rustybuzz、ttf-parser这些处理字体的库。虽然不如JS生态那么丰富但核心功能都有现成的轮子不用从零造。Go的TinyGo编译出来的WASM体积小但Go的GC在WASM里表现一般而且TinyGo对标准库的支持有限处理复杂数据结构时容易踩坑。AssemblyScript语法像TypeScript上手快但性能不如Rust而且生态太薄。3.3 Web Worker的分工与通信机制Worker的职责边界要划清楚。我的经验是Worker只负责纯计算不碰DOM不碰网络请求不碰任何浏览器API。所有需要和页面交互的事情都放在主线程。这样做的好处是Worker代码可以独立测试也避免了在Worker里调用不支持的API导致报错。通信协议我建议定义成简单的请求-响应模式。主线程发一个消息带上一个自增的requestId和PDF数据Worker处理完后用同一个requestId把结果发回来。主线程根据requestId匹配回调。这样即使同时发起多个生成任务也不会搞混。传输大文件时有个细节要注意Uint8Array在postMessage时默认是结构化克隆会复制一份。500页PDF大概几MB到几十MB复制一次虽然只要几毫秒但能省则省。可以用Transferable Objects把ArrayBuffer的所有权直接转移零拷贝。代价是转移后原线程不能再访问这个buffer所以转移前要确认不再需要它。4. Rust侧核心实现细节4.1 项目结构与依赖选择一个典型的Rust PDF生成库Cargo.toml里通常会有这些依赖[lib] crate-type [cdylib, rlib] [dependencies] wasm-bindgen 0.2 js-sys 0.3 serde { version 1, features [derive] } serde-wasm-bindgen 0.6 printpdf 0.7 ttf-parser 0.20 flate2 1.0crate-type要同时指定cdylib和rlib前者用于生成WASM动态库后者用于单元测试。wasm-bindgen是Rust和JS互操作的桥梁serde-wasm-bindgen负责JSON和Rust结构体之间的转换。printpdf是PDF生成的核心库ttf-parser用来读取字体文件flate2做压缩。这里有个坑printpdf的版本更新比较频繁API变动大。我建议锁定一个小版本比如0.7.x不要用^0.7否则某天CI跑出来编译失败你会很懵。另外printpdf默认不支持中文需要自己处理字体嵌入和字符映射。4.2 字体子集化的实现思路中文字体文件动辄十几MB全量嵌入PDF会让文件体积爆炸。所以必须做子集化只把文档里实际用到的字符对应的字形数据提取出来重新打包成一个精简字体。子集化的核心步骤是遍历文档所有文本收集去重后的字符集合用ttf-parser读取原始字体的cmap表找到每个字符对应的glyph ID根据glyph ID从glyf表中提取字形轮廓数据重新构建一个只包含这些字形的字体文件把这个子集字体嵌入PDF。这个过程在Rust里做比在JS里做快很多因为涉及大量二进制解析和位操作。我实测过一个包含3000个不同汉字的文档子集化耗时在Rust里大概是80毫秒在JS里要1.5秒以上。注意字体子集化要处理复合字形。有些汉字是由多个部件拼出来的提取时要把依赖的字形也带上否则渲染出来会缺笔画。ttf-parser提供了glyph_index和outline_glyph方法但复合字形的递归解析需要自己写。4.3 PDF指令生成与压缩PDF文件本质上是一系列对象的集合每个对象有编号对象之间可以互相引用。生成PDF就是按照PDF规范把这些对象序列化成字节流。一个典型的页面内容流长这样BT /F1 12 Tf 1 0 0 1 72 720 Tm (Hello World) Tj ETBT和ET标记文本块的开始和结束Tf设置字体和字号Tm设置文本矩阵位置Tj输出文本。对于500页文档这样的指令会有几十万条。Rust侧要做的是高效地拼接这些字符串然后统一压缩。压缩用FlateDecode也就是zlib。flate2这个crate提供了ZlibEncoder可以把内容流压缩到原体积的20%到30%。压缩级别建议用Compression::fast()因为best()虽然能多压几个百分点但耗时可能翻倍。在2秒的总预算里压缩占300毫秒左右比较合理。4.4 内存管理与性能调优Rust在WASM里的内存管理有几个要点。第一避免频繁分配小对象。PDF生成过程中会产生大量临时字符串如果每个都String::new()内存分配器会压力很大。可以用String::with_capacity()预分配或者用一个可复用的Vecu8缓冲区。第二注意WASM的线性内存增长。WASM内存是一块连续的线性空间初始大小可以设置但增长时会有开销。可以在初始化时就把内存设大一点比如256MB避免运行中反复增长。在wasm-bindgen里可以通过--initial-memory参数配置。第三用#[inline]标记热点函数。PDF指令拼接、坐标计算这些被调用几十万次的函数加上#[inline]能让LLVM更好地优化。但不要滥用代码体积会变大WASM下载时间增加。5. 前端侧的集成与优化5.1 Worker的初始化与WASM加载Worker脚本里加载WASM模块的代码大概长这样import init, { generate_pdf } from ./pkg/pdf_gen.js; let wasmReady false; async function initWasm() { await init(); wasmReady true; self.postMessage({ type: ready }); } self.onmessage async (e) { if (!wasmReady) { await initWasm(); } const { requestId, data } e.data; try { const result generate_pdf(data); self.postMessage( { requestId, result }, [result.buffer] ); } catch (err) { self.postMessage({ requestId, error: err.message }); } };这里的关键点是init()返回的是Promise必须等它resolve之后才能调用导出的函数。另外generate_pdf返回的Uint8Array在postMessage时用第二个参数把buffer转移走避免复制。5.2 主线程与Worker的通信协议主线程这边我建议封装一个类把requestId的管理、Promise的resolve/reject、超时处理都包进去class PdfWorkerClient { constructor() { this.worker new Worker(./worker.js, { type: module }); this.pending new Map(); this.seq 0; this.worker.onmessage (e) { const { requestId, result, error } e.data; const handler this.pending.get(requestId); if (!handler) return; this.pending.delete(requestId); if (error) handler.reject(new Error(error)); else handler.resolve(result); }; } generate(data, timeout 30000) { const requestId this.seq; return new Promise((resolve, reject) { const timer setTimeout(() { this.pending.delete(requestId); reject(new Error(PDF generation timeout)); }, timeout); this.pending.set(requestId, { resolve: (v) { clearTimeout(timer); resolve(v); }, reject: (e) { clearTimeout(timer); reject(e); } }); this.worker.postMessage({ requestId, data }); }); } }超时机制很重要。虽然正常情况下2秒就能出结果但如果数据异常导致Rust侧死循环没有超时的话页面就永远卡在那里了。5.3 进度反馈与用户体验2秒虽然快但用户点了按钮之后如果没有任何反馈体验还是很差。可以在Worker里分阶段postMessage进度解析数据10%、排版计算40%、字体子集化60%、PDF生成80%、压缩90%、完成100%。主线程收到进度后更新进度条。不过要注意postMessage本身有开销。如果每生成一页就报一次进度500页就是500次消息传递反而拖慢速度。我的做法是每50页报一次或者按阶段报总共不超过10次。5.4 大文件下载的兼容处理生成的PDF如果超过100MB在某些浏览器上用Blob URL下载可能会失败。稳妥的做法是用URL.createObjectURL生成链接后创建一个隐藏的a标签设置download属性然后模拟点击。下载完成后调用URL.revokeObjectURL释放内存。对于特别大的文件可以考虑分片下载但这需要服务端配合纯前端方案下比较难实现。如果确实有超大PDF需求建议在Worker里生成完后用Streams API分块传给主线程主线程再逐块写入文件系统。不过这个方案兼容性一般Chrome支持较好Safari要较新版本。6. 性能实测与瓶颈分析6.1 测试环境与数据集说明我用的测试数据是一份模拟的财务报表每页包含一个标题、一段说明文字、一个10行5列的表格、一个页脚。500页总共约25万字符其中不同汉字约2800个。测试机器是MacBook Pro M116GB内存Chrome 120。对比方案有三个纯JS的pdfmake、RustWASM单线程、RustWASMWorker。每个方案跑三次取平均值。6.2 各阶段耗时拆解阶段pdfmakeRust单线程RustWorker数据解析120ms15ms15ms排版计算18000ms420ms420ms字体子集化不支持85ms85msPDF指令生成包含在排版中680ms680ms压缩编码包含在排版中310ms310ms主线程阻塞全程阻塞全程阻塞无阻塞总耗时约22秒约1.5秒约1.5秒从表格可以看出Rust方案比pdfmake快了将近15倍。其中排版计算是差距最大的环节pdfmake花了18秒Rust只用了420毫秒。这是因为pdfmake在JS里做文本度量时每次都要查字体表而Rust侧可以把字体表缓存在内存里直接索引。字体子集化是pdfmake根本不支持的功能所以它生成的PDF要么体积巨大嵌入全量字体要么中文显示为乱码不嵌入字体。6.3 瓶颈在哪里虽然1.5秒已经很快了但离标题说的“2秒500页”还有差距。实际上标题里的2秒可能是指特定优化场景下的数据比如纯英文、无复杂表格、字体已经预子集化。在我的测试里主要瓶颈是PDF指令生成和压缩两者加起来占了总耗时的66%。指令生成的瓶颈在于字符串拼接。Rust的String在频繁push_str时会有多次内存重分配。优化方法是预分配一个足够大的Vecu8然后用write!宏直接写入避免中间字符串。我试过这个优化指令生成从680毫秒降到了450毫秒。压缩的瓶颈在于zlib本身。flate2默认用的是miniz_oxide纯Rust实现性能不如C版本的zlib。可以切换到zlib-ng后端但需要处理WASM的C依赖编译比较麻烦。另一个思路是降低压缩级别用Compression::none()文件体积会大3倍左右但压缩耗时降到接近零。这个取舍要看具体场景如果PDF是通过网络传输的压缩还是值得的。6.4 内存占用对比pdfmake在生成500页时内存峰值达到1.8GB页面多次触发垃圾回收卡顿明显。RustWASM方案的内存峰值在380MB左右其中WASM线性内存占300MBJS堆占80MB。内存曲线平稳没有明显的GC抖动。这个差距主要因为JS的对象模型开销大。pdfmake内部用大量JS对象表示PDF结构每个对象都有原型链和隐藏类。Rust侧用紧凑的结构体和数组内存利用率高得多。7. 常见问题与排查实录7.1 WASM加载失败怎么办最常见的问题是MIME类型不对。服务器必须把.wasm文件的Content-Type设为application/wasm否则WebAssembly.instantiateStreaming会报错。如果用的是Vite或webpack通常会自动处理但自己配Nginx的话要加一行types { application/wasm wasm; }另一个坑是路径问题。wasm-bindgen生成的JS文件里WASM路径是相对路径。如果Worker脚本和WASM不在同一目录需要手动指定init的参数await init(/assets/pdf_gen_bg.wasm);7.2 中文乱码的三种原因中文乱码是PDF生成里最高频的问题。原因通常有三种第一字体没有嵌入PDF阅读器用了默认字体而默认字体不含中文第二字符编码不对文本在传给Rust之前被转成了Latin-1第三字体子集化时漏掉了某些字形。排查顺序是先用一个只包含“你好”两个字的文档测试如果正常显示说明字体嵌入没问题问题出在子集化逻辑如果乱码检查字体文件本身是否包含中文字形如果字体没问题检查JS到Rust的字符串传递是否用了UTF-8。提示wasm-bindgen在传递字符串时默认用UTF-8但如果你在Rust侧用str接收后做了不当的字节操作可能会破坏编码。建议在Rust侧统一用String不要手动操作字节。7.3 生成速度突然变慢的排查思路如果之前跑得好好的某天突然变慢按这个顺序查第一数据量是不是变大了比如从500页变成了800页第二字体文件是不是换了有些字体字形表特别大解析耗时翻倍第三WASM模块是不是每次都在重新初始化检查Worker有没有被意外销毁第四浏览器版本是不是更新了某些Chrome版本对WASM的优化有回退。我遇到过一次诡异的情况生成时间从1.5秒变成了8秒。查了半天发现是某个依赖库的版本升级导致serde的序列化变慢了。回滚版本后恢复正常。所以锁定依赖版本这件事在Rust项目里比在JS项目里更重要。7.4 常见问题速查表现象可能原因解决方法WASM加载报错MIME类型不对配置服务器application/wasm中文显示为方块字体未嵌入检查子集化逻辑确认字体包含中文字形生成速度慢数据量过大或字体表过大分页生成或换用更精简的字体页面卡顿WASM跑在主线程移到Web Worker内存溢出WASM线性内存不足初始化时增大--initial-memory下载失败Blob URL被回收下载完成后再revokeObjectURL表格错位列宽计算精度问题用整数运算代替浮点运算8. 这套方案适合什么场景8.1 推荐使用的场景大批量报表导出是最典型的场景。比如财务系统月末要导出几千页的明细账或者物流系统要批量打印运单。这些场景对矢量输出有要求需要打印清晰数据量大而且用户对等待时间敏感。RustWASM方案能把等待时间从几十秒压缩到几秒体验提升非常明显。离线优先的应用也很适合。比如Electron或Tauri打包的桌面端或者PWA应用。这些环境没有服务端可以依赖所有计算必须在本地完成。RustWASM的性能优势在离线场景下更加突出。对隐私敏感的场景同样值得考虑。有些文档包含用户敏感信息不适合传到服务端生成。纯前端的Rust方案让数据不出浏览器合规上更放心。8.2 不建议使用的场景如果文档只有几页到几十页用jsPDF或pdfmake就够了。引入RustWASM会增加构建复杂度、增大包体积WASM文件通常几百KB到几MB维护成本不低。杀鸡用牛刀反而得不偿失。如果团队里没有人写过Rust也要慎重。Rust的学习曲线确实陡borrow checker会让新手很痛苦。虽然只是写一个PDF生成库不涉及复杂的并发和生命周期但仍然需要理解所有权、trait、错误处理这些概念。如果团队没有Rust经验建议先用JS方案顶着等有精力了再逐步迁移。如果对PDF的矢量要求不高比如只是内部预览用那html2canvas截图方案最省事。虽然放大模糊但开发成本最低。8.3 替代方案对比方案矢量输出性能包体积开发成本适用场景jsPDFhtml2canvas否中小低简单预览pdfmake是低中中中小文档服务端Puppeteer是高无中有服务端RustWASM是极高大高大批量离线RustTauri是极高大高桌面应用从表格可以看出RustWASM不是唯一选择但在“大批量离线矢量”这个交叉场景下它确实是最优解。9. 我踩过的坑和实操心得9.1 字体文件不要用完整版我一开始图省事直接把一个15MB的思源黑体全量嵌入。结果生成的PDF有12MB下载慢阅读器打开也卡。后来改成子集化同样内容的PDF只有800KB。字体子集化是必做项不是可选项。但子集化也有坑。有些字体在子集化后某些字符的渲染会出问题比如笔画粘连或者位置偏移。这是因为字体的hinting信息在子集化过程中丢失了。解决办法是保留hinting表或者换用hinting较少的字体。我最后选了一个专门为屏幕阅读优化的字体子集化后效果很稳定。9.2 Worker的销毁与重建Worker如果长时间运行内存会慢慢增长。虽然Rust侧的内存管理很好但JS侧的Worker本身会有一些累积。我的做法是每生成50个PDF就销毁Worker重建一次。重建的代价是重新加载WASM大概200毫秒但能避免内存泄漏导致页面崩溃。销毁Worker的代码很简单this.worker.terminate(); this.worker new Worker(./worker.js, { type: module });但要注意销毁前要确保没有pending的请求否则那些Promise会永远挂起。9.3 错误处理要区分类型Rust侧的错误通过Result返回wasm-bindgen会把它转成JS的异常。但异常信息可能不够具体。我建议在Rust侧定义自己的错误类型实现Displaytrait把错误上下文写清楚。比如“字体解析失败不支持的字体格式”比“Error: invalid font”有用得多。JS侧收到错误后不要直接弹给用户看。用户不关心技术细节他们只想知道“为什么失败了”和“怎么办”。可以做一个错误码映射表把技术错误转成用户能理解的提示。9.4 测试要覆盖边界情况PDF生成的边界情况特别多空文档、单页文档、超长文本、特殊字符、emoji、表格跨页、图片嵌入。我建议写一套自动化测试用Rust的单元测试覆盖核心逻辑用Playwright做端到端测试验证生成的PDF能正常打开。Rust侧测试可以用printpdf的调试输出把生成的PDF结构打印出来检查。JS侧测试可以用pdf-lib读取生成的PDF验证页数、文字内容、字体嵌入是否正确。9.5 构建产物的优化wasm-pack默认生成的WASM文件可能比较大。可以用wasm-opt做进一步优化wasm-opt -O3 -o output_bg.wasm input_bg.wasm-O3会做激进的优化包括死代码消除、内联、常量折叠。我实测能把WASM体积减少30%左右执行速度也有小幅提升。但-O3编译时间较长建议只在生产构建时用。另外wasm-bindgen生成的JS胶水代码也可以压缩。用wasm-pack build --release会自动做这件事。如果用的是Vite可以在vite.config.js里配置optimizeDeps.exclude把WASM包排除避免Vite的预构建破坏WASM的加载逻辑。10. 后续可以扩展的方向这套方案跑通之后我还在想几个可以继续优化的点。一个是增量生成用户翻到第几页就生成到第几页不用等全部生成完。这需要把PDF生成拆成可中断的步骤Rust侧维护一个状态机。另一个是模板化把常用的报表样式做成模板用户只需要填数据排版逻辑预编译好进一步缩短生成时间。还有一个方向是多线程WASM。Chrome已经支持WASM的SharedArrayBuffer和原子操作理论上可以把PDF生成拆到多个Worker并行跑。但PDF的对象引用是全局的并行化需要仔细设计数据分区策略复杂度不低。目前单Worker的1.5秒已经够用暂时没有动力去做。最后说一个我个人的判断RustWASM在前端不是银弹它解决的是特定场景下的特定问题。如果你的项目正好卡在“大批量矢量PDF生成”这个点上那这套方案值得投入。但如果只是普通的前端开发没必要为了追新技术而强行上Rust。技术选型永远要看场景不看热度。
返回列表