今日已更新 233 条资讯 | 累计 20205 条内容
关于我们

标签:#Java

找到 624 篇相关文章

AI 资讯

Stop trusting environment variables in your TypeScript apps

Environment variables look simple until one of them is missing, empty, malformed, or interpreted in a way your application did not expect. In TypeScript projects, this can be easy to overlook. The code may be typed, the build may pass, and the app may still ship with broken configuration. This is especially common in frontend builds, server-side rendering, backend services, CLI tools, Docker images, and CI/CD pipelines, where configuration is injected from outside the codebase. That is the problem valitype is designed to address: strict, type-safe validation of environment variables with zero dependencies. The problem with environment variables Environment variables are always external input. They can come from: .env files CI/CD variables Docker or container platforms hosting providers build scripts deployment environments TypeScript can describe what your code expects, but it cannot guarantee that the environment actually contains valid values. This looks harmless: const apiUrl = import . meta . env . VITE_API_URL const debug = Boolean ( import . meta . env . VITE_DEBUG ) const port = Number ( process . env . PORT ) But simple casting can hide invalid configuration: Boolean ( ' false ' ) // true Boolean ( ' 0 ' ) // true Number ( ' 0xff ' ) // 255 Number ( ' 1e5 ' ) // 100000 Number ( '' ) // 0 For application configuration, “parseable” is not the same as “valid”. Invalid configuration should be caught before deployment, either during build, CI, server startup, or a dedicated validation step. The problem in React and frontend builds React applications usually do not read environment variables directly at runtime in the browser. Instead, tools like Vite and frameworks like Next.js inject selected values during build time. That makes validation important. Once the frontend bundle is built and deployed, changing a bad environment variable usually means rebuilding and redeploying the application. For frontend apps, validation should answer a few basic questions before

2026-06-28 原文 →
开发者

【互動藝術 DIY】用 p5.js 做一塊會呼吸的粒子背景(無程式背景可)

【互動藝術 DIY】用 p5.js 做一塊會呼吸的粒子背景 先問自己:阿哲會想動手嗎? 看完這個效果,阿哲可能會想: 「那如果我把粒子排成自己的名字,滑鼠靠近時會散開嗎?」 這就是正確的方向—— 讓讀者想自己動手改參數 ,而不是背程式碼。 這個方法厲害在哪? p5.js 官網有很多炫技的粒子效果,但大部分只是給你看「很厲害」。 這次不一樣——我要教你 用最少程式碼,做出最有呼吸感的互動 。 秘密是:用「距離」控制行為,用「lerp」讓移動變溫柔。 教學順序 先建立粒子:讓一群點組成畫面 教動畫:用 sin() 做出呼吸節奏 教距離感:用 dist() 偵測滑鼠 教溫柔散開:用 lerp() 柔和移動,不是瞬移 最後加美感:透明度、殘影、暖色 第一步:讓粒子回家 class Particle { constructor ( x , y ) { this . homeX = x ; // 記住家的位置 this . homeY = y ; this . x = x ; this . y = y ; } // 讓粒子回家的力量 returnHome () { this . x = lerp ( this . x , this . homeX , 0.05 ); // 每次移動5%的距離 this . y = lerp ( this . y , this . homeY , 0.05 ); } show () { noStroke (); fill ( 255 , 180 , 120 , 200 ); // 暖橙色 ellipse ( this . x , this . y , 4 ); } } 第二步:偵測滑鼠距離 function draw () { for ( let p of particles ) { let d = dist ( mouseX , mouseY , p . x , p . y ); if ( d < 100 ) { // 滑鼠靠近時,輕輕推開 let force = 0.05 * ( 100 - d ) / 100 ; p . x += ( p . x - mouseX ) / d * force ; p . y += ( p . y - mouseY ) / d * force ; } p . returnHome (); p . show (); } } 第三步:加呼吸節奏 let breathPhase = 0 ; function draw () { breathPhase += 0.02 ; let breath = sin ( breathPhase ); // -1 ~ +1 來回循環 background ( 10 , 8 , 5 ); // 暖暗色背景 for ( let p of particles ) { let d = dist ( mouseX , mouseY , p . x , p . y ); if ( d < 100 ) { let force = 0.05 * ( 100 - d ) / 100 ; p . x += ( p . x - mouseX ) / d * force ; p . y += ( p . y - mouseY ) / d * force ; } p . returnHome (); p . show ( breath ); // 把呼吸相位傳進去 } } function show ( breath ) { noStroke (); // 呼吸時變亮,吐氣時變暗 let alpha = map ( breath , - 1 , 1 , 150 , 255 ); fill ( 255 , 180 , 120 , alpha ); ellipse ( this . x , this . y , 4 ); } 第四步:殘影效果 function draw () { // 不要每幀清掉背景,而是蓋一層半透明黑色 fill ( 10 , 8 , 5 , 30 ); rect ( 0 , 0 , width , height ); // ... 其餘粒子邏輯 } 這樣粒子移動時會留下淡淡的光跡——很有沉浸式裝置的 feel。 阿哲可以怎麼玩? 參數 預設值 改成... 效果 感知半徑 100px 50px 只有非常靠近才有反應 回家速度 0.05 0.02 超級慢,像在水裡 回家速度 0.05 0.2 快一點回覆 粒子數量 200 50 稀疏的星塵感 粒子颜色 暖橙 淡粉 更柔和的感覺 延伸練習 把粒子排成自己的名字 :讓粒子組成「阿哲」或英文字母輪廓,滑鼠靠近時文字散開,離開後慢慢聚回來。 滑鼠不是破壞者,是一陣風 :不只是排斥,而是讓粒子沿著滑鼠移動方向飄

2026-06-28 原文 →
开发者

I Built a Unit Converter in Pure Vanilla JS — 7 Categories, 70+ Units, 165 Tests, Zero Dependencies

Unit converters are everywhere online, but they all seem to either require an account, run ads that cover half the screen, or send your input to a server for no reason. I built one that runs entirely in your browser, with no dependencies, no tracking, and no round-trips. 👉 https://unit-converter-dev.pages.dev What It Does Seven conversion categories, 70+ units, real-time bidirectional conversion: Category Example units Length mm, cm, m, km, in, ft, yd, mi, nmi, light-year Weight mg, g, kg, t, oz, lb, st, short ton Temperature °C, °F, K, °R Volume ml, l, m³, fl oz, cup, pint, quart, gallon, tbsp, tsp Area mm², cm², m², km², ha, acre, ft², in², mi², yd² Speed m/s, km/h, mph, ft/s, knot, Mach Data bit, byte, KB/KiB, MB/MiB, GB/GiB, TB — both SI and binary Features: Bidirectional — type in either field, the other updates instantly Swap button — flip from/to with one click All-units panel — see your input converted to every unit in the category simultaneously Formula display — shows the conversion factor (e.g. "1 Mile = 1.609344 Kilometer") Zero dependencies — single HTML file, no build step, no npm Implementation Notes Linear vs. non-linear conversions Most unit conversions are linear: multiply by a factor to get to the base unit, divide by another factor to get to the target. The approach: function convert ( catKey , fromUnit , toUnit , value ) { const base = toBase ( catKey , fromUnit , value ); // → base unit return fromBase ( catKey , toUnit , base ); // base unit → target } function toBase ( catKey , unit , value ) { const u = CATEGORIES [ catKey ]. units [ unit ]; if ( u . toBase ) return u . toBase ( value ); // non-linear (temperature) return value * u . factor ; } Temperature is the classic non-linear case. You can't just multiply to convert between Celsius, Fahrenheit, and Kelvin — you need offset arithmetic: temperature : { units : { C : { toBase : v => v + 273.15 , // °C → K fromBase : v => v - 273.15 , // K → °C }, F : { toBase : v => ( v - 32 ) * 5 / 9 + 2

2026-06-28 原文 →
AI 资讯

I Built a QR Code Generator in Pure Vanilla JS — No Libraries, No Server, 202 Tests

QR codes look like magic — a grid of black and white squares that encodes anything from a URL to a business card. But how do they actually work? I decided to find out the hard way: implement the full QR Code Model 2 algorithm in vanilla JavaScript, zero external dependencies. The result: QR Code Generator — a free, client-side tool that generates QR codes from any text or URL. 👉 https://qr-code-generator-e83.pages.dev Why No Libraries? I maintain a collection of browser-only developer tools at devnestio . Every tool has the same rule: zero external dependencies. No npm installs, no CDN scripts, no servers. For most tools (JSON diff, Base64 encoder, UUID generator) that's easy. QR codes are different. The spec is a 126-page ISO document. Most developers just npm install qrcode and call it a day. But writing it from scratch taught me more about error-correcting codes, Galois field arithmetic, and matrix encoding than I ever expected. Worth every hour. What the Tool Does Real-time generation as you type (debounced at 80ms) Size selector — 128 × 128, 256 × 256, or 512 × 512 pixels Error correction level — L (7%), M (15%), Q (25%), H (30%) Color picker — any foreground and background color PNG download via canvas SVG download with crisp vector output at any scale How QR Codes Actually Work QR Code Model 2 (the standard you see everywhere) has six major steps. Here's the short version: 1. Data Encoding Text gets encoded into one of three modes based on content: Numeric ( 0-9 ): packs 3 digits into 10 bits — most compact Alphanumeric ( 0-9 A-Z $%*+-./:space ): 2 chars into 11 bits Byte (everything else): UTF-8, one byte per 8 bits The encoder picks the mode automatically and finds the minimum QR version (1–40) that fits the data. function detectMode ( text ) { if ( /^ \d +$/ . test ( text )) return NUMERIC_MODE ; if ( text . split ( '' ). every ( c => ALPHANUMS . includes ( c ))) return ALPHANUM_MODE ; return BYTE_MODE ; } 2. Reed-Solomon Error Correction This is the hard

2026-06-28 原文 →
AI 资讯

Orchestrate Saga Compensation Timeouts in Real Time (Kiponos Java SDK)

A checkout saga spans inventory, payment, shipping, and loyalty. Downstream latency shifts every hour. Black Friday is not the day to discover your payment step timeout is baked into application.yml across twelve Spring Boot services. Kiponos.io gives every saga participant the same live orchestration parameters — step timeouts, retry budgets, compensation triggers — via one shared config tree. Each JVM reads locally on every saga step; ops adjusts once in the dashboard; WebSocket deltas propagate without redeploying the fleet. Why sagas break with static config Typical saga coordinator code: if ( step . elapsedMs () > 8000 ) { compensate ( "payment" , sagaId ); } That 8000 usually comes from: Per-service YAML — payment service says 8s, inventory says 12s; nobody agrees during an incident Env vars in Helm — change means rolling twelve deployments Shared DB config table — poll per step adds latency and coupling Saga steps are high-frequency reads inside workflow engines. You need local memory reads and async updates — the same contract as live API rate limits . Architecture: one tree, many participants ┌─────────────────┐ WebSocket deltas ┌──────────────────────┐ │ Kiponos.io UI │ ────────────────────────► │ Inventory service │ │ platform ops │ │ Payment service │ └─────────────────┘ │ Shipping service │ │ (each: in-mem SDK) │ └──────────┬───────────┘ │ .getInt() local ▼ ┌──────────────────────┐ │ saga step executor │ └──────────────────────┘ Every participant connects to profile ['orders']['v2']['prod']['sagas'] . When NOC extends payment.step_timeout_ms , all JVMs see the new value on the next step — no config server poll, no inter-service "what is timeout now?" REST calls. Shared saga config tree sagas/ checkout/ payment/ step_timeout_ms : 8000 max_retries : 2 retry_backoff_ms : 500 compensate_on_timeout : true inventory/ step_timeout_ms : 5000 max_retries : 3 hold_ttl_seconds : 120 shipping/ step_timeout_ms : 12000 fallback_carrier : ups_ground global/ saga_ttl_m

2026-06-28 原文 →
AI 资讯

60 Themes, 51 Components, still 0 Dependencies. Yumekit v0.5 Released!

Back in May we here at Waggy Labs launched the beta release of our Web Component UI kit " Yumekit ". Yumekit is a pure web component UI toolkit. Upon its release, it was comprised of roughly 36 fully styled and fully functional UI components that work with just about every web architecture straight out of the box. No configuration or setup necessary, all one needs to do is include the Yumekit script (using either a CDN or installed through NPM) and start building. All components come styled out of the box with no need to include any style sheets. Last week, we launched version 0.5. With this latest release, that job is being made easier with the inclusion of new components that add several layout options as well as new Data, Navigation, and Utility components, bringing the total number of components to 51. For us, this toolkit has provided us a framework-agnostic solution for our internal tools as well as any client projects. With over 60 themes spread over 9 well-known (and some brand new) open source Design Systems all built directly into the library, we have plenty of options available to us to keep our designs fresh without needing to spend hours dealing with CSS. It's light-weight, dependency free, and well documented. New in 0.5 Animate The y-animate component allows you to animate entrances and exits for nested components using a few simple configuration attributes. Code The y-code component allows you to display formatted and colorized code, as well as providing a few easy and convenient ways for your users to copy the provided code. Help The y-help component provides a tutorial experience for users of your application with minimal configuration. Simply provide the elements to be highlighted, the messages to be shown, and it handles the rest! Paginator y-paginator provides a configurable set of pagination buttons to help your users navigate through large data sets. Sidebar We had originally included a y-appbar component (which we still do) that had a "Sideba

2026-06-27 原文 →
AI 资讯

How to Detect Which Font Is Actually Rendering in a Browser (Not Just the CSS Stack)

getComputedStyle(element).fontFamily returns the CSS declaration: "Hiragino Kaku Gothic ProN", "Yu Gothic", "Noto Sans JP", sans-serif . That's not the font that rendered. It's a priority list. The browser picks the first one that's available and contains a glyph for the character being rendered. For Latin text, this distinction usually doesn't matter — Windows, macOS, and Linux have converged on a small set of common system fonts. For Japanese, it matters enormously. The visual weight, stroke contrast, and letterform style of Hiragino, Yu Gothic, and Noto Sans JP are genuinely different. A site designed on macOS (where Hiragino is the system Japanese font) looks different on Windows (where Yu Gothic is the fallback). Here's how to figure out what's actually rendering, and what I learned building Japanese Font Finder to automate it. Why getComputedStyle Doesn't Answer the Question getComputedStyle(el).fontFamily gives you the cascade result — what the browser received after applying all CSS rules. But it doesn't tell you which entry in the stack was selected. The underlying question is: does this font exist on this system, and does it have a glyph for this specific character? For Japanese, both conditions matter. A font might exist on the system but only cover a subset of kanji (common with CJK fonts that split across multiple files). The browser will use that font for characters it covers, and fall back for others. Canvas-Based Font Detection The classical technique uses a <canvas> element to measure text rendered with each font in the stack: function getFallbackWidth ( canvas , char ) { const ctx = canvas . getContext ( ' 2d ' ); ctx . font = `16px monospace` ; // known-available baseline return ctx . measureText ( char ). width ; } function testFont ( fontName , char ) { const canvas = document . createElement ( ' canvas ' ); const ctx = canvas . getContext ( ' 2d ' ); ctx . font = `16px " ${ fontName } ", monospace` ; return ctx . measureText ( char ). width ; }

2026-06-27 原文 →
AI 资讯

UTC, GMT, and the time zone bugs that keep biting developers

Time zones are one of those topics that look simple until you ship something and a user in another country sees the wrong time. Here are the traps I keep seeing, and how to reason about them in 2026. UTC is not a time zone, and GMT is not UTC UTC (Coordinated Universal Time) is a time standard, not a region. GMT is a time zone that happens to share the same offset as UTC most of the year. For storage and math, always think in UTC. Treat GMT as just another named zone. Rule 1: store timestamps in UTC Store every instant as UTC (or an epoch value). Convert to a local zone only at the edges, when you display to a user. If you store local times, you will eventually lose the offset and never recover the true instant. Rule 2: an offset is not a zone +09:00 tells you the offset right now. It does not tell you the zone, because zones change offset across the year due to daylight saving time. Store the IANA zone name (like America/New_York ), not just the offset. The offset is derived from the zone plus the date. Rule 3: DST is where it hurts The same wall-clock time can happen twice (fall back) or never (spring forward). Scheduling "9am every day" is a zone-aware operation, not an offset-aware one. Libraries like the built-in Intl.DateTimeFormat and Temporal (now widely available) handle this correctly if you give them a zone name. new Intl . DateTimeFormat ( ' en-US ' , { timeZone : ' Asia/Tokyo ' , dateStyle : ' short ' , timeStyle : ' short ' , }). format ( new Date ()); Rule 4: scheduling across teams is an overlap problem For a distributed team, the useful question is not "what time is it there" but "when do our working hours overlap". That is a set-intersection over each person's 9-to-5 expressed in UTC. A tool for the human side When I just need to eyeball overlaps and pick a meeting time without writing code, I use the free tool I built: ZonePlan , a time zone meeting planner and live world clock. If you want the practical playbook for picking meeting times, I wrote

2026-06-27 原文 →
AI 资讯

I hooked up Trading212 to Home Assistant and now Alexa tells me if I'm up or down every morning

I've been using Home Assistant for a few years and Trading212 for longer than that. It was inevitable these two things would end up connected. The Trading212 API is surprisingly good — portfolio value, individual positions, pies, dividends, all there. So I wrote a custom integration to pull it all into HA as sensors, then a Lovelace card to make it actually look decent on a dashboard rather than a wall of entity rows. The card does zero-config auto-discovery which was the bit I spent the most time on. You drop it on a dashboard and it finds your sensors automatically — no copying entity IDs, no manual config unless you want it. Five card types: portfolio overview with a sparkline, scrollable positions list, pies with goal progress, and a combined one if you want everything in one card. The sparkline was fiddly. HA's recorder only writes state changes, not regular samples, so if your portfolio value is flat between polls the chart has gaps. Had to smooth over those client-side. The part I use most though is the automations. Every weekday at 8am Alexa tells me where I stand: action : - action : notify.alexa_media_kitchen data : message : > Portfolio is worth {{ states('sensor.trading212_total_value') | float | round(0) | int }} pounds. Today you are {% if states('sensor.trading212_pnl_today') | float >= 0 %}up{% else %}down{% endif %} {{ states('sensor.trading212_pnl_today') | float | abs | round(2) }} pounds. data : type : tts And Friday at 6pm I get the weekly version with P&L for the week and which position moved the most. I like that it just tells me — if the market's had a bad week I'd probably avoid opening the app, but Alexa doesn't give me the option to ignore it. Both the integration and the card are on GitHub. The card is in HACS as a custom repo while it waits for default catalogue approval: https://github.com/Smart-Home-Assistant-UK/lovelace-trading212-card I wrote up the full setup with all the automation YAML here if you want to copy the whole thing: ful

2026-06-27 原文 →
AI 资讯

7 Spring Boot Annotations Every Beginner Should Know

When I first started learning Spring Boot, I was overwhelmed by annotations. Every file seemed to have symbols starting with @ . @SpringBootApplication @RestController @Service @Autowired At first, I treated them like magic spells. I copied them from tutorials and hoped everything would work. Eventually, I realized that understanding a few key annotations made Spring Boot much less intimidating. If you're just starting your Spring Boot journey, these are the annotations I believe you should understand first. 1. @SpringBootApplication This is usually the first annotation you'll see in a Spring Boot project. @SpringBootApplication public class DemoApplication { public static void main ( String [] args ) { SpringApplication . run ( DemoApplication . class , args ); } } Think of it as the starting point of your application . When Spring Boot sees this annotation, it knows: Where the application begins Which components need to be scanned Which configurations should be loaded Without it, your Spring Boot application won't know how to start properly. 2. @RestController If you're building REST APIs, you'll use this annotation frequently. @RestController public class HelloController { @GetMapping ( "/hello" ) public String hello () { return "Hello, World!" ; } } A class marked with @RestController tells Spring: "The methods inside this class will handle HTTP requests and return data." Instead of returning web pages, it usually returns: JSON Strings Objects API responses Whenever I create a new API endpoint, this is one of the first annotations I add. 3. @GetMapping This annotation is used when you want to handle GET requests . @GetMapping ( "/students" ) public String getStudents () { return "List of students" ; } A GET request is typically used to retrieve information. Examples: Get user details Fetch products View student records Whenever a client requests data from the server, @GetMapping often comes into play. 4. @PostMapping While @GetMapping retrieves data, @PostMappin

2026-06-27 原文 →
开发者

I built a free whale tracker for Polymarket — here's what I learned

The problem: I kept missing big moves on Polymarket because I had no way to see what the biggest traders were betting on in real time. So I built WhaleTrack — a free, no-signup tool that shows you exactly what top Polymarket whales are buying and selling. What it does Live whale activity feed — see the last 40 trades from top wallets, updated on refresh Whale leaderboard — P&L, win rate, trade count for the biggest accounts No login, no ads, no fluff — just the data How it works The whole thing is vanilla HTML/CSS/JS deployed on Vercel with two serverless functions: /api/whales.js — hits the Polymarket leaderboard API, fetches position stats for each whale, calculates win rates from closed positions /api/activity.js — pulls recent trades for each whale wallet in parallel, filters out internal combo transactions (no title / zero price), and returns the 40 most recent trades The serverless layer solves CORS — Polymarket's data API doesn't allow browser requests, so everything goes server-side. Tech stack Frontend: Vanilla HTML/CSS/JS (zero dependencies) Backend: Vercel serverless functions Data: Polymarket public data API Deploy: Vercel (free tier) Biggest lesson Filtering bad data is half the work. The raw API returns combo trades and internal transactions that show up as "Unknown Market @ 0¢" — useless noise. Had to figure out which fields to check (title, price > 0) to strip them. Also: win rate calculation is tricky when most whales have unrealized profits. Showing "—" instead of 0% is more honest. Try it WhaleTrack → Also launched on Product Hunt today if you want to show some love: Product Hunt Built this in a weekend. Happy to answer questions about the Polymarket API or Vercel serverless setup.

2026-06-27 原文 →
AI 资讯

Three Months with Java 26: My Thoughts After Using the Latest Release

Java 26 was officially released in March 2026, and after spending the past three months exploring its new features, experimenting with preview APIs, and using it in personal projects, I think it's a good time to share my impressions. Unlike launch-day articles that simply list every new feature, this is a practical look at what actually stood out to me after having some time to work with Java 26. Some improvements are immediately useful, while others feel like building blocks for the future of the language. Java continues its predictable six-month release cycle, and Java 26 is another example of gradual, thoughtful evolution rather than dramatic change. In this article, I'll cover the features I found most interesting, what I like, what I probably won't use right away, and whether I think Java 26 is worth upgrading to. Why Upgrade to Java 26? Every Java release makes the platform: Faster More secure Easier to write Better for cloud applications Even if you don't immediately use every new feature, upgrading allows you to benefit from JVM optimizations and improved tooling. 1. Better Performance Java 26 continues improving the JVM with optimizations for: Faster startup Better garbage collection Reduced memory usage Improved JIT compilation Most applications will benefit automatically without changing a single line of code. 2. Improved Pattern Matching Pattern matching keeps becoming more powerful. Instead of writing: if ( obj instanceof String ) { String text = ( String ) obj ; System . out . println ( text . length ()); } You can simply write: if ( obj instanceof String text ) { System . out . println ( text . length ()); } Cleaner code with less casting. 3. Record Improvements Records remain one of Java's best additions for immutable data. public record User ( Long id , String name , String email ) {} Instead of writing dozens of lines containing: constructor getters equals() hashCode() toString() Java generates them automatically. 4. Better String Templates (Previe

2026-06-27 原文 →
产品设计

I Rebuilt Instagram Stories' Segmented Progress Bars

Instagram/WhatsApp Stories have a signature UI: those segmented bars across the top, one filling at a time. It looks fancy but it's a simple pattern. Here's a live, tappable rebuild in vanilla JS + CSS. 📸 Try it (tap left/right, hold to pause): https://dev48v.infy.uk/design/day17-instagram-stories.html The segmented bar One bar per story. The rule: only the active segment animates its width 0→100%; segments before it are full, segments after are empty. When the active one completes, advance to the next and reset the rule. Driving the fill A single requestAnimationFrame loop tracks elapsed time vs the per-story duration (~4s) and sets the active bar's width. On completion → next story. The interactions that sell it Tap the right half = next, left half = previous (split the screen into two zones). Press-and-hold = pause ( pointerdown pauses the timer, pointerup resumes) — so users can actually read. Reset past/future segment states whenever you jump. Why rAF over CSS animation A timer loop makes pause/resume and tap-to-skip trivial — you control the clock. Pure CSS animations are harder to interrupt mid-fill. 🔨 Full build (segments → animate active → advance → tap zones → hold-to-pause) on the page: https://dev48v.infy.uk/design/day17-instagram-stories.html Part of DesignFromZero. 🌐 https://dev48v.infy.uk

2026-06-26 原文 →
AI 资讯

Rust Ate the JavaScript Toolchain. Then Cloudflare Bought It

I run Vite on almost everything. Astro sites, Nuxt projects, a small group of libraries I maintain on the side. The build tool is the part of the stack I think about least, because it just works. So when the thing under all of that changes twice in three months, I read the release notes properly. Here is what actually changed, what breaks, and the part that made developers argue for a week straight. For Five Years, Vite Ran on Two Bundlers When Vite launched, it made a pragmatic bet. esbuild for the dev server, because it is fast. Rollup for production, because its output is well optimized. Two tools, two jobs. It worked. But it had a cost. Two bundlers meant two configs, two sets of quirks, and output that could drift between dev and prod. You tuned one, and the other behaved slightly differently. Vite 8 ends the split. It shipped on March 12 with a single bundler called Rolldown, written in Rust, with the Rollup plugin API on top. Under Rolldown sits Oxc, a Rust parser and transformer that does the TypeScript and JSX work Babel used to do. One language. One pipeline. Dev and prod finally agree. This Is a Pattern, Not a One-Off esbuild (Go) made webpack look slow. Bun did the same to Node for some workloads. Biome replaced Prettier and ESLint and runs many times faster. Now Rolldown does it to Rollup and esbuild at the same time. Every time a core JavaScript tool gets rewritten in a compiled language, the same thing happens. The speed jump is large enough to make the old version look broken. The interesting part is not the speed. It is the compatibility. These Rust tools do not ask you to relearn your stack. Rolldown speaks the Rollup plugin API. Biome follows ESLint and Prettier conventions. The migration is designed to be boring, and boring is the point. The Numbers, With a Grain of Salt The headline figure is real. Linear cut its production build from 46 seconds to 6 . Vite reports builds 10 to 30 times faster than the old Rollup path. Other large projects repor

2026-06-26 原文 →