普通视图

发现新文章,点击刷新页面。
昨天 — 2026年2月21日首页

你不知道的JS(下):深入编程

作者 牛奶
2026年2月21日 21:53

你不知道的JS(下):深入编程

本文是《你不知道的JavaScript(下卷)》的阅读笔记,第一部分:深入编程。 供自己以后查漏补缺,也欢迎同道朋友交流学习。

原文地址

墨渊书肆/你不知道的JS(下):深入编程

代码与语句

程序是一组特定的计算机指令。指令的格式和组合规则被称为计算机语言(语法)。

语句

执行特定任务的一组单词、数字和运算符被称为语句。

a = b * 2;
  • ab:变量
  • 2:字面值
  • =*:运算符
  • JS 语句通常以分号 ; 结尾。

表达式

语句由一个或多个表达式组成。表达式是对变量、值的引用,或者是其与运算符的组合。

执行程序

程序需要通过解释器或编译器翻译成计算机可理解的命令后执行。 JS 引擎实际上会即时编译(JIT)程序,然后立即执行编译后的代码。虽然 JS 常被称为解释型语言,但现代引擎的 JIT 过程使得其运行速度非常快。

实践环境

最简单的方法是使用浏览器(Chrome、Firefox 等)的开发者工具。

  • 输出console.log()(控制台输出)或 alert()(弹窗输出)。
  • 输入prompt()(获取用户输入)。

运算符

JavaScript 常用运算符包括:

  • 赋值=(将值保存在变量中)。
  • 算术+-*/%(取模)。
  • 复合赋值+=-=*=/=(如 a += 2 等同于 a = a + 2)。
  • 递增/递减++(递增)、--(递减)。
  • 对象属性访问.(如 obj.a)或 [](如 obj["a"])。
  • 相等==(宽松相等)、===(严格相等)。
  • 逻辑&&(与)、||(或)、!(非),用于表示复合条件。

值与类型

在编程术语中,对值的不同表示方法称为类型。JavaScript 提供了以下内置基本类型:

  • 数字 (number):用于数学计算。
  • 字符串 (string):一个或多个字符组成的文本。
  • 布尔值 (boolean)truefalse,用于决策判断。
  • 除此之外,还提供 数组对象函数 等复合类型。

类型转换

JavaScript 提供显式和隐式两种类型转换机制。

var a = "42"; 
var b = Number(a); // 显式类型转换
console.log( a ); // "42" 
console.log( b ); // 42
console.log( a == b ); // true,隐式类型转换(宽松相等)

代码注释

编写代码不仅是给计算机看,也是给开发者阅读。良好的注释能显著提高代码的可读性,解释器会忽略这些内容。

变量

变量是用于跟踪值变化的符号容器。JavaScript 采用动态(弱)类型机制,变量可以持有任意类型的值。

ES6 块作用域声明

除了传统的 var,ES6 引入了更强大的变量声明方式:

  • let 声明:创建块级作用域变量。相比 var,它解决了提升导致的逻辑混乱,并引入了“暂时性死区”(TDZ)。

  • const 声明:用于创建只读常量。注意,const 锁定的是变量的赋值,而不是值本身。

    const a = [1, 2, 3]; 
    a.push( 4 ); // 成功!内容可以修改
    console.log( a ); // [1, 2, 3, 4] 
    a = 42; // TypeError! 赋值被锁定
    

模板字面量

ES6 引入了反引号 ( ` ) 界定的模板字面量,支持变量插值和多行字符串。

var name = "Kyle"; 
var greeting = `Hello ${name}!`; // 插值解析
var text = `
Now is the time 
for all good men
`; // 支持多行

解构

解构是一种“结构化赋值”方法,可以从数组或对象中快速提取值。

var [ a, b, c ] = [1, 2, 3]; 
var { x, y } = { x: 10, y: 20 };

块与条件判断

:使用 { .. } 将一系列语句组织在一起。

条件判断:最常用的是 if 语句,根据条件的真假决定是否执行后续代码块。

var bank_balance = 302.13; 
var amount = 99.99; 
if (amount < bank_balance) { 
    console.log( "I want to buy this phone!" ); 
}

循环

循环用于重复执行任务,每次执行被称为一次“迭代”。

  • while / do..while:根据条件循环。
  • for:更紧凑的循环形式,包含初始化、测试条件和更新。
var i = 0;
while (true) { 
    if ((i <= 9) === false) { 
        break; // 停止循环
    } 
    console.log(i); 
    i = i + 1; 
} 

for (var i = 0; i <= 9; i++) { 
    console.log( i ); 
}

函数

函数是可复用的代码片段,可以接受参数并返回值。

function printAmount(amt) { 
    console.log( amt.toFixed( 2 ) ); 
} 
function formatAmount() { 
    return "$" + amount.toFixed( 2 ); 
} 
var amount = 99.99; 
printAmount( amount * 2 ); // "199.98" 
amount = formatAmount(); 
console.log( amount ); // "$99.99"

作用域

在 JS 中,每个函数都有自己的作用域(词法作用域)。作用域是变量的集合及访问规则。

  • 只有函数内部的代码才能访问该作用域中的变量。
  • 作用域可以彼此嵌套:内层作用域可以访问外层作用域的变量,反之则不行。

小结

学习编程并不必然是复杂、费力的过程。我们需要熟悉几个基本的概念:

  • 运算符:在值上执行动作。
  • 值与类型:执行各种类型的动作需要值和类型,比如对数字进行数学运算,用字符串输出。
  • 变量:在程序的执行过程中需要变量来保存数据(也就是状态)。
  • 条件判断:需要 if 这样的条件判断来作出决策。
  • 循环:需要循环来重复任务,直到不满足某个条件。
  • 函数:需要函数将代码组织为逻辑上可复用的块。

在编程学习中,实践是绝对无法替代的。理论无法让你成为一个程序员,唯有动手尝试。

你不知道的JS(下):总结与未来

作者 牛奶
2026年2月21日 21:45

你不知道的JS(下):总结与未来

本文是《你不知道的JavaScript(下卷)》的阅读笔记,第四部分:总结与未来。 供自己以后查漏补缺,也欢迎同道朋友交流学习。

原文地址

墨渊书肆/你不知道的JS(下):总结与未来

深入“你不知道的JS”系列回顾

1. 作用域和闭包

深入理解编译器对代码的处理方式(如“提升”),掌握词法作用域。这是研究闭包的基础,让我们明白变量是如何在不同层级的作用域中被查找和管理的。

2. this 和对象原型

this 是根据函数执行方式动态绑定的,而非定义位置。原型机制是一个属性查找链(委托),模拟类继承通常是对该机制的误用。

3. 类型和语法

类型转换(强制转换)是被严重低估的工具。正确使用它能显著提升代码质量,而不是回避它。

4. 异步和性能

异步编程不仅关乎应用响应速度,更是现代 JS 开发中代码易读性和可维护性的关键。

5. ES6 及更新版本

ES6 是 JavaScript 的一个巨大飞跃。令人兴奋的新特性包括:

  • 语法糖:解构赋值、默认参数值、简洁方法、计算属性、箭头函数。
  • 作用域:块作用域(let/const)。
  • 处理能力:Promise、生成器(Generators)、迭代器(Iterators)。
  • 元编程:代理(Proxy)、反射(Reflect)。
  • 新结构与 API:Map、Set、Symbol、模块(Modules)。
  • 集合扩展:TypedArray。

6. 集合与数据结构

ES6 极大地丰富了处理数据的手段:

  • Map/WeakMap:真正的键值对映射,键可以是任意类型(包括对象)。WeakMap 允许键被垃圾回收,适合存储元数据。
  • Set/WeakSet:唯一值的集合。WeakSet 同样支持弱引用,成员必须是对象。
  • TypedArray:如 Uint8ArrayFloat64Array,提供了对二进制数据的结构化访问,是处理音频、视频及 Canvas 数据的利器。

7. 元编程 (Meta Programming)

元编程关注程序自身的结构和运行时行为:

  • Proxy (代理):通过自定义处理函数(traps)拦截并重新定义对象的底层操作(如 get、set、has 等)。

    var pobj = new Proxy( obj, {
        get(target, key) {
            console.log( "accessing: ", key );
            return target[key];
        }
    } );
    
  • Reflect (反射):提供了一套与 Proxy 拦截器一一对应的静态方法,用于执行对象的默认行为。

  • 尾调用优化 (TCE):ES6 规范要求在严格模式下支持尾调用优化,能够有效避免递归时的栈溢出问题。

8. 新增 API 亮点

  • ArrayArray.of(..) 解决了 Array(..) 构造器的单数字陷阱;Array.from(..) 将类数组轻松转换为真数组。
  • ObjectObject.assign(..) 用于对象混入/克隆。
  • String:新增 includes(..)startsWith(..)repeat(..) 等实用方法。

9. ES6 之后与未来展望

JavaScript 的进化从未停歇:

  • 异步增强async/await(ES2017)让异步代码看起来像同步一样自然。
  • Object.observe:虽然最终被 Proxy 取代,但它代表了数据绑定机制的早期探索。
  • SIMD:单指令多数据流,旨在利用 CPU 并行指令加速数值计算。
  • WebAssembly (WASM):为 JS 引擎引入二进制指令格式,让 C/C++ 等高性能语言能以接近原生的速度在浏览器运行。
  • 正则表达式:新增 u (Unicode) 和 y (Sticky) 标识符。
  • 数字扩展:新的二进制 (0b) 和八进制 (0o) 字面量形式。

10. 代码组织与封装

  • Iterators (迭代器):提供了一套标准化的数据遍历协议。
  • Generators (生成器):通过 yield 实现可暂停/恢复的函数执行。
  • Modules (模块):原生支持基于文件的模块系统,通过 exportimport 实现静态依赖分析。
  • Classes (类):虽然只是原型委托的语法糖,但极大地简化了“面向对象”风格代码的编写。

ES 的现在与未来

版本演进

JavaScript 标准的官方名称是 ECMAScript (ES)

  • ES3:早期的流行标准(IE6-8 时代)。
  • ES5:2009 年发布,现代浏览器的稳固基石。
  • ES6 (ES2015):具有里程碑意义,引入了模块化和类等大型特性。
  • 后续版本:采用基于年份的命名方式(如 ES2016, ES2017...),每年发布一次,使语言特性能够更快速地迭代。

持续进化与工具化

JavaScript 的发展速度已显著加快。为了解决开发者想用新特性与旧环境支持落后之间的矛盾,工具化变得至关重要。

Transpiling 的重要性

Transpiling(转换+编译)技术(如使用 Babel)允许开发者编写最前沿的 ES 代码,并将其自动转换为兼容旧环境(如 ES5)的代码。这让我们既能享受语言进化的红利,又能兼顾用户覆盖面。配合 Polyfilling(填补 API 缺失),构成了现代 JS 开发的基础设施。

小结

JavaScript 的旅程从未停止:

  • 核心积淀:通过对作用域、this、类型和异步的深入探讨,我们夯实了 JS 的底层知识架构。
  • ES6 飞跃:作为里程碑式的版本,ES6 彻底改变了我们编写 JavaScript 的方式,使其具备了开发大型复杂应用的能力。
  • 面向未来:随着年度版本的发布和 WebAssembly 等新技术的出现,JS 正在变得更强、更快、更无处不在。
  • 工具赋能:Transpiler 和 Polyfill 是我们保持技术领先、跨越版本鸿沟的得力助手。

学习这门语言的秘诀在于:不满足于“它能运行”,而要追求“它是如何运行的”。唯有如此,方能在这门不断进化的语言中游刃有余。

你不知道的JS(下):深入JS(下)

作者 牛奶
2026年2月21日 21:43

你不知道的JS(下):深入JS(下)

本文是《你不知道的JavaScript(下卷)》的阅读笔记,第三部分:深入JS(下)。 供自己以后查漏补缺,也欢迎同道朋友交流学习。

严格模式 (Strict Mode)

ES5 引入了严格模式,通过 "use strict"; 开启。它可以使代码更安全、更易于引擎优化。

  • 不允许省略 var 的隐式自动全局变量声明。
  • 限制了某些不安全或不合理的语法行为。

函数进阶

作为值的函数

函数在 JavaScript 中是第一类对象,可以作为值赋给变量,也可以作为参数传递或从其他函数返回。

var foo = function() { /* .. */ };
var x = function bar(){ /* .. */ };

立即调用函数表达式 (IIFE)

IIFE 用于创建一个临时作用域并立即执行代码。它也可以有返回值:

var x = (function IIFE(){ 
    return 42; 
})(); 
x; // 42

闭包 (Closure)

闭包允许函数在其定义的词法作用域之外执行时,仍能“记忆”并访问该作用域。

模块模式

这是闭包最常见的应用。模块允许定义外部不可见的私有实现,同时提供公开 API。

function User(){ 
    var username, password; 
    function doLogin(user,pw) { 
        username = user; 
        password = pw; 
    } 
    var publicAPI = { 
        login: doLogin 
    }; 
    return publicAPI;
} 
var fred = User(); 
fred.login( "fred", "12Battery34!" );

this 标识符

this 指向哪个对象取决于函数是如何被调用的。遵循以下四条规则:

  1. 默认绑定:非严格模式下指向全局对象,严格模式下为 undefined
  2. 隐式绑定:由上下文对象调用(如 obj1.foo()),指向该对象。
  3. 显式绑定:通过 callapplybind 指定指向。
  4. new 绑定:指向新创建的空对象。
function foo() { console.log( this.bar ); } 
var bar = "global"; 
var obj1 = { bar: "obj1", foo: foo }; 
var obj2 = { bar: "obj2" }; 

foo();            // "global" (默认绑定)
obj1.foo();       // "obj1"   (隐式绑定)
foo.call( obj2 ); // "obj2"   (显式绑定)
new foo();        // undefined (new 绑定)

原型 (Prototype)

当访问对象不存在的属性时,JavaScript 会自动在内部原型链上查找。这是一种属性查找的备用机制(也称为委托)。

var foo = { a: 42 }; 
var bar = Object.create( foo ); 
bar.a; // 42 (委托给 foo 查找)

ES6 核心特性

符号 (Symbol)

Symbol 是 ES6 引入的新原生类型,没有字面量形式,主要用于创建唯一的、不会冲突的键值。

  • 单例模式:非常适合实现模块单例。
  • 符号注册:通过 Symbol.for(..) 在全局注册表中查找或创建符号。
  • 隐藏属性:符号属性不会出现在一般的属性枚举中(如 Object.keys),需使用 Object.getOwnPropertySymbols(..) 获取。

迭代器 (Iterator)

迭代器是一个结构化模式,用于从数据源一次提取一个值。

  • 接口:必须包含 next() 方法,返回 { value, done }

  • 自定义迭代器:可以手动实现 [Symbol.iterator] 接口。

    var Fib = { 
        [Symbol.iterator]() { 
            var n1 = 1, n2 = 1; 
            return { 
                next() { 
                    var current = n2; 
                    n2 = n1; n1 = n1 + current; 
                    return { value: current, done: false }; 
                } 
            }; 
        } 
    };
    

生成器 (Generator)

生成器是一种特殊的函数,可以在执行中暂停(yield)并恢复。

  • 语法function *foo() { .. }
  • 迭代器控制:生成器返回一个迭代器,通过调用 next() 来控制生成器的执行流。
  • 双向通信yield 不仅可以返回值,还可以接收 next(val) 传入的值。

模块 (Modules)

ES6 模块是基于文件的单例,具有静态 API。

  • 导出与导入:使用 exportimport
  • 静态加载:编译时确定依赖关系,支持模块间循环依赖。
  • 对比旧方法:不再需要依赖闭包和封装函数来实现模块化。

填补与转换 (Polyfilling & Transpiling)

Polyfilling

根据新特性定义,在旧环境中手动实现等价行为的代码。适用于新 API。

if (!Number.isNaN) { 
    Number.isNaN = function isNaN(x) { 
        return x !== x; // NaN 是唯一不等于自身的值
    }; 
}

Transpiling

通过工具(如 Babel)将新语法转换为等价的旧版代码。适用于新语法特性(如箭头函数、解构等),因为这些无法通过 Polyfill 实现。

小结

JavaScript 的进阶特性赋予了这门语言强大的表达能力:

  • 闭包与模块:通过词法作用域记忆功能实现私有化封装,是构建大型应用的基础。
  • this 与原型:理解动态绑定规则与原型委托机制,能够更高效地进行对象间的功能复用。
  • ES6 新范式:迭代器、生成器和原生模块系统标志着 JS 向更成熟、更工程化的方向迈进。
  • 兼容性保障:通过 Polyfill 和 Transpiling,我们可以在拥抱未来的同时,确保代码在旧环境中的稳健运行。

掌握这些核心机制,不仅能帮助我们写出更好的代码,更能让我们深入理解 JavaScript 的运行本质。

你不知道的JS(下):深入JS(上)

作者 牛奶
2026年2月21日 21:42

你不知道的JS(下):深入JS(上)

本文是《你不知道的JavaScript(下卷)》的阅读笔记,第二部分:深入JS(上)。 供自己以后查漏补缺,也欢迎同道朋友交流学习。

值和类型

JavaScript 的值有类型,但变量无类型。内置类型包括:

  • 字符串 (string)
  • 数字 (number)
  • 布尔型 (boolean)
  • nullundefined
  • 对象 (object)
  • 符号 (symbol,ES6 新增)

使用 typeof 运算符可以查看值的类型。注意:typeof null 返回 "object",这是一个历史遗留问题。

对象

对象是 JavaScript 中最有用的值类型,可以设置属性。

var obj = { 
    a: "hello world", 
    b: 42, 
    c: true
}; 
obj.a; // "hello world" 
obj["b"]; // 42

数组与函数

数组和函数是对象的特殊子类型:

  • 数组:持有值的对象,通过数字索引位置管理。

    var arr = ["hello world", 42, true]; 
    arr[0]; // "hello world" 
    arr.length; // 3 
    typeof arr; // "object"
    
  • 函数:也是对象的一个子类型,可以拥有属性。

    function foo() { return 42; } 
    foo.bar = "hello world"; 
    typeof foo; // "function"
    

内置类型方法

内置类型及其子类型拥有作为属性和方法暴露出来的行为:

var a = "hello world"; 
a.length; // 11 
a.toUpperCase(); // "HELLO WORLD" 

值的比较

JavaScript 中任何比较的结果都是布尔值(truefalse)。

真与假 (Truthy & Falsy)

JavaScript 中的“假”值列表:

  • ""(空字符串)
  • 0-0NaN(无效数字)
  • nullundefined
  • false 除以上值外,所有其他值均为“真”值。

相等性

相等运算符有四种:=====!=!==

  • ==:允许类型转换情况下的相等性检查。
  • ===:不允许类型转换(严格相等)。
var a = "42"; 
var b = 42;
a == b;  // true (隐式转换)
a === b; // false (严格相等)

关系比较

<><=>= 用于比较有序值(如数字或字母序字符串 "bar" < "foo")。

变量与作用域

变量标识符必须由 a-zA-Z$_ 开始,可以包含数字。

ES6 语法扩展

  • spread/rest 运算符 (...):取决于使用位置,用于展开数组或收集参数。

    // 展开
    function foo(x,y,z) { console.log( x, y, z ); } 
    foo( ...[1,2,3] ); // 1 2 3
    // 收集
    var a = [2,3,4]; 
    var b = [ 1, ...a, 5 ]; // [1,2,3,4,5]
    
  • 默认参数值:为缺失参数提供默认值。

    function foo(x = 11, y = 31) { console.log( x + y ); } 
    foo(5); // 36 (y 使用默认值)
    foo(5, undefined); // 36 (undefined 触发默认值)
    foo(5, null); // 5 (null 被强制转换为 0)
    

提升 (Hoisting)

使用 var 声明的变量和函数声明会被“提升”到其所在作用域的最顶端。

var a = 2;
foo(); 
function foo() { 
    a = 3; 
    console.log( a ); // 3 
    var a; // 声明被提升到了 foo() 的顶端
} 
console.log( a ); // 2

作用域嵌套

声明后的变量在当前作用域及其所有内层作用域中随处可见。

function foo() { 
    var a = 1; 
    function bar() { 
        var b = 2; 
        function baz() { 
            var c = 3; 
            console.log( a, b, c ); // 1 2 3 (向上查找作用域链)
        } 
        baz(); 
    } 
    bar(); 
}

条件判断与循环

除了 if..else,JavaScript 还提供了多种控制流机制。

条件判断

  • switch:适用于多分支判断。
  • 三元运算符 ? ::简洁的条件表达式。

循环

  • for..of 循环:ES6 新增,直接在迭代器产生的上循环。

    var a = ["a","b","c"]; 
    for (var val of a) { 
        console.log( val ); // "a" "b" "c"
    }
    

箭头函数 (=>)

箭头函数不仅是更短的语法,它还解决了 this 绑定的常见痛点(采用词法 this)。

var controller = { 
    makeRequest: function(){ 
        btn.addEventListener( "click", () => { 
            this.makeRequest(); // this 继承自父层,即 controller
        }, false ); 
    } 
};

箭头函数是匿名函数表达式,没有自己的 argumentssupernew.target

小结

深入理解 JavaScript 的第一步是掌握其核心机制:

  • 值与类型:JS 的变量无类型但值有类型。
  • 强制类型转换:理解 ===== 的区别,以及真假值的判断规则。
  • 作用域与提升:掌握 var 的声明提升行为以及嵌套作用域的查找规则。
  • 现代语法:熟悉 ES6 带来的 spread 运算符、默认参数、for..of 循环以及箭头函数等新特性。

通过掌握这些基础,我们可以更从容地应对更高级的 JS 特性。

阿里千问推出Qwen Coding Plan

2026年2月21日 19:39
2月21日,阿里千问宣布推出Qwen Coding Plan,支持更多模型,上新Qwen3.5-Plus、Qwen3-Coder-Next,适配QwenCode、ClaudeCode、Cline等Al工具使用。

ls Cheatsheet

Basic Listing

Use these commands for everyday directory listing.

Command Description
ls List files in current directory
ls /path/to/dir List files in specific directory
ls -1 One entry per line
ls -a Include hidden files
ls -A Include hidden files except . and ..

Long Format and Metadata

Show permissions, ownership, size, and timestamps.

Command Description
ls -l Long listing format
ls -lh Human-readable file sizes
ls -la Long format with hidden files
ls -n Numeric UID and GID
ls -li Show inode numbers

Sorting

Sort files by time, size, extension, or version.

Command Description
ls -lt Sort by modification time (newest first)
ls -ltr Sort by modification time (oldest first)
ls -lS Sort by file size (largest first)
ls -lX Sort by extension
ls -lv Natural sort for version-like names

Time Display

Control which timestamp is shown.

Command Description
ls -lt --time=atime Sort/show by access time
ls -lt --time=ctime Sort/show by status change time
ls -l --time-style=long-iso ISO-like date format
ls -l --full-time Full timestamp precision

Directory Views

List directories recursively or show directory entries only.

Command Description
ls -la --group-directories-first Long listing with directories before files
ls -d */ List only directories in current path
ls -ld /path/to/dir Show metadata for directory itself
ls -R Recursive listing
ls -laR Recursive long listing with hidden files

Output Formatting

Adjust visual style and indicators.

Command Description
ls -F Append indicator (/, *, @) by file type
ls -p Append / to directories
ls -m Comma-separated output
ls -x List entries across rows instead of down columns
ls --color=auto Enable colorized output when supported

Filtering with Globs

List files that match shell patterns.

Command Description
ls *.log List files ending in .log
ls file?.txt Match single-character wildcard
ls [ab]*.conf Match names starting with a or b
ls -d .[^.]* List hidden files (common shell pattern)

Common Patterns

Frequent command combinations.

Command Description
ls -lah Most common detailed listing
ls -lhS Largest files first with readable sizes
ls -lat Newest files first including hidden entries
`ls -1 wc -l`
`ls -l grep ‘^d’`

Troubleshooting

Quick checks for typical listing issues.

Issue Check
Hidden files not visible Add -a or -A
File sizes are hard to read Use -h with -l
Wrong sort order Confirm flags (-t, -S, -X, -r)
No color output Try ls --color=auto and check alias settings
Path looks empty Verify permissions with ls -ld /path

Related Guides

Use these references for deeper file management workflows.

Guide Description
How to List Files in Linux Using the ls Command Full ls guide with practical examples
Du Command in Linux Check disk usage and file sizes
Linux Commands Cheatsheet General command quick reference

【节点】[ReflectionProbe节点]原理解析与实际应用

作者 SmalBox
2026年2月21日 19:21

【Unity Shader Graph 使用与特效实现】专栏-直达

摘要

Unity URP中的ReflectionProbe节点是实现环境反射效果的核心工具,通过采样场景反射探针的立方体贴图数据,为动态物体提供真实反射。该节点需要输入对象空间的法线和视图方向向量,支持LOD控制反射模糊度。技术实现上依赖Unity反射探针系统,在片元着色器中计算反射向量并进行立方体贴图采样。主要支持URP管线,与HDRP不兼容。典型应用包括金属材质、水面效果和动态反射,使用时需注意反射探针布置、坐标系匹配和性能优化。节点生成的HLSL代码调用SHADERGRAPH_REFLECTION_PROBE宏处理复杂反射计算,开发者可通过理解底层机制实现自定义扩展。

描述

Reflection Probe 节点是 Unity URP Shader Graph 中用于实现高质量反射效果的核心工具。该节点允许着色器访问场景中最近的反射探针(Reflection Probe)数据,为材质提供基于环境的真实反射信息。在现代实时渲染中,反射探针技术是模拟环境反射的关键手段,它通过预计算或实时捕获场景的立方体贴图,为动态对象提供准确的环境光照和反射细节。

反射探针的工作原理是在场景中的特定位置捕获周围环境的360度视图,并将其存储为立方体贴图。当使用 Reflection Probe 节点时,着色器会根据物体的表面法线和视图方向,从最近的反射探针中采样相应的反射颜色。这种机制使得移动的物体能够在不同环境中自动获得正确的反射效果,而无需为每个物体单独设置反射贴图。

该节点需要两个关键的输入参数才能正常工作:法线向量和视图方向向量。法线向量定义了表面的朝向,用于计算反射方向;视图方向向量则表示摄像机到表面点的方向,两者结合可以确定从哪个角度采样反射探针。此外,节点还提供了 LOD 输入参数,允许在不同的细节级别进行采样,这个功能特别有用于创建模糊反射效果或性能优化。

需要注意的是,Reflection Probe 节点的具体实现行为并非在全局范围内统一定义。Shader Graph 本身并不定义此节点的具体函数实现,而是由各个渲染管线为其定义要执行的 HLSL 代码。这意味着相同的节点在不同的渲染管线中可能会产生不同的结果,开发者在跨管线使用着色器时需要特别注意兼容性问题。

技术实现原理

从技术层面看,Reflection Probe 节点底层依赖于 Unity 的反射探针系统。当在场景中放置反射探针时,Unity 会在该位置捕获环境信息并生成立方体贴图。Shader Graph 中的 Reflection Probe 节点在着色器执行时,会执行以下关键步骤:

  • 首先确定物体表面点对应的最近反射探针
  • 根据输入的法线和视图方向计算反射向量
  • 使用反射向量在立方体贴图中进行采样
  • 应用可能的LOD模糊处理
  • 输出最终的反射颜色值

这个过程在片元着色器中执行,为每个像素提供精确的反射计算。对于性能考虑,URP 通常会对反射探针采样进行优化,比如使用较低分辨率的立方体贴图或采用近似计算方法。

支持的渲染管线

Reflection Probe 节点目前主要支持以下渲染管线:

  • 通用渲染管线(Universal Render Pipeline, URP)

需要注意的是,高清渲染管线(High Definition Render Pipeline, HDRP)并不支持此节点。HDRP 有自己专门的反射系统实现,使用不同的节点和方法来处理反射效果。这种差异源于两个渲染管线的设计目标和架构不同 - URP 更注重性能和跨平台兼容性,而 HDRP 则专注于高端图形效果。

如果开发者计划构建需要在多个渲染管线中使用的着色器,强烈建议在实际项目应用前,分别在目标管线中进行测试和验证。某些节点可能在一个渲染管线中已完整定义并正常工作,而在另一个管线中可能未实现或行为不一致。如果 Reflection Probe 节点在某个渲染管线中未定义,通常会返回 Vector3(0, 0, 0),即黑色值,这可能导致反射效果完全丢失。

端口

Reflection Probe 节点包含多个输入和输出端口,每个端口都有特定的功能和数据类型要求。正确理解和使用这些端口是实现预期反射效果的关键。

输入端口

View Dir 端口是关键的输入参数之一,它要求提供 Vector 3 类型的视图方向数据。这个方向应该基于对象空间(Object Space)表示,即从当前表面点指向摄像机的方向向量。视图方向在反射计算中至关重要,因为它与表面法线共同决定了反射向量的计算。在实际应用中,这个端口通常连接到 Shader Graph 中的 View Direction 节点,该节点会自动提供正确的视图方向向量。

  • 数据类型:Vector 3
  • 空间要求:对象空间(Object Space)
  • 典型连接:View Direction 节点
  • 功能说明:定义了从表面点到摄像机的方向,用于反射计算

Normal 端口是另一个必需的输入参数,同样需要 Vector 3 类型的法线向量,基于对象空间。表面法线定义了面的朝向,是光学计算中的基础要素。在反射计算中,法线用于根据入射光方向(视图方向的逆方向)计算反射方向。这个端口通常连接到 Normal Vector 节点,或者连接到自定义法线贴图处理后的结果。

  • 数据类型:Vector 3
  • 空间要求:对象空间(Object Space)
  • 典型连接:Normal Vector 节点或法线贴图采样结果
  • 功能说明:定义表面朝向,参与反射方向计算

LOD 端口是一个可选的浮点数输入,用于控制采样反射探针的细节级别。LOD 技术允许在不同距离或根据不同性能需求使用不同精度的纹理。在 Reflection Probe 节点的上下文中,LOD 参数主要用于创建模糊反射效果 - 较高的 LOD 值会产生更模糊的反射,模拟粗糙表面的反射特性或创建特殊的视觉效果。

  • 数据类型:Float
  • 取值范围:通常为 0 到最大 LOD 级别
  • 特殊应用:通过动画或参数控制实现动态模糊效果
  • 性能影响:较高的 LOD 值可能降低采样精度但提升性能

输出端口

Out 端口是节点的唯一输出,提供 Vector 3 类型的反射颜色值。这个输出代表了根据输入参数从反射探针采样得到的 RGB 颜色值,可以直接用于着色器的最终输出或与其他颜色值进行混合。输出的颜色强度和质量取决于多个因素,包括反射探针的设置、场景光照环境以及输入的参数准确性。

  • 数据类型:Vector 3(RGB 颜色)
  • 取值范围:通常为 HDR 颜色值,可能超过 [0,1] 范围
  • 使用方式:可直接输出或与漫反射、其他效果混合
  • 色彩空间:根据项目设置可能是线性或伽马空间

端口连接实践

在实际的 Shader Graph 制作中,正确连接这些端口是实现高质量反射效果的关键。典型的连接方式包括:

  • 将 View Direction 节点连接到 View Dir 端口
  • 将 Normal Vector 节点连接到 Normal 端口
  • 使用 Float 参数或数学节点控制 LOD 端口
  • 将 Out 端口连接到主着色器的相应输入,如 Emission 或反射颜色混合节点

理解每个端口的空间要求特别重要 - 不匹配的空间坐标系会导致错误的反射计算。例如,如果提供了世界空间的法线方向但节点期望对象空间法线,反射方向将完全错误,导致反射效果不符合预期。

生成的代码示例

Reflection Probe 节点在 Shader Graph 背后生成的代码展示了其实际的工作原理和实现方式。通过理解这些生成的代码,开发者可以更深入地掌握节点的功能,并在需要时进行自定义扩展或优化。

基础函数实现

以下示例代码表示 Reflection Probe 节点的一种典型 HLSL 实现:

void Unity_ReflectionProbe_float(float3 ViewDir, float3 Normal, float LOD, out float3 Out)
{
    Out = SHADERGRAPH_REFLECTION_PROBE(ViewDir, Normal, LOD);
}

这段代码定义了一个名为 Unity_ReflectionProbe_float 的函数,这是 Shader Graph 为 Reflection Probe 节点生成的标准函数。函数接受三个输入参数:ViewDir(视图方向)、Normal(法线方向)和 LOD(细节级别),并通过输出参数 Out 返回反射颜色结果。

函数内部调用了 SHADERGRAPH_REFLECTION_PROBE宏,这是 URP 渲染管线为 Shader Graph 定义的专门用于反射探针采样的内部函数。这个宏封装了所有复杂的反射计算逻辑,包括:

  • 反射探针的选择和混合
  • 反射向量的计算和变换
  • 立方体贴图的采样和过滤
  • LOD 级别的应用

代码解析与技术细节

从生成的代码中可以看出几个重要的技术细节:

  • 函数使用 float 精度变体(通过 _float 后缀标识),这表明节点支持多种精度模式,包括 half 和 fixed,以适应不同的性能需求和平台限制
  • 所有向量参数都基于相同的坐标系,确保数学计算的一致性
  • LOD 参数直接传递给底层采样函数,实现细节级别的控制
  • 输出是简单的 RGB 颜色值,易于集成到各种着色模型中

在实际的着色器编译过程中,SHADERGRAPH_REFLECTION_PROBE 宏会被展开为具体的 HLSL 代码,这些代码会根据当前的渲染管线和平台进行优化。例如,在移动平台上,可能会使用更简化的数学计算或较低精度的数据类型以提升性能。

自定义扩展可能性

了解生成的代码结构为开发者提供了自定义反射效果的基础。虽然 Shader Graph 提供了便捷的视觉化编程方式,但在某些高级用例中,可能需要在自定义函数节点中直接编写类似的代码。例如,开发者可以:

  • 修改反射向量的计算方式以实现特殊效果
  • 添加额外的后处理步骤,如色彩校正或对比度调整
  • 实现多个反射探针的混合算法
  • 添加基于距离或角度的反射强度衰减

通过理解 Reflection Probe 节点的代码生成模式,开发者可以更好地调试着色器问题,优化性能,并在需要时突破 Shader Graph 可视化编程的限制,实现更复杂的反射效果。

应用场景与实例

Reflection Probe 节点在实时渲染中有广泛的应用场景,从基本的金属材质到复杂的视觉特效都可以见到它的身影。理解这些应用场景有助于在实际项目中更好地利用这一强大工具。

金属与反射表面

最常见的应用是为金属材质和反射表面添加环境反射。金属材质的特点是具有高度的镜面反射性,能够清晰地反射周围环境。使用 Reflection Probe 节点可以轻松实现这种效果:

  • 将 Reflection Probe 节点的输出直接连接到主着色器的 Emission 输入,创建明亮的金属反射
  • 与基础的 PBR 材质结合,将反射输出与漫反射颜色混合,实现更自然的材质外观
  • 通过 LOD 参数控制反射的清晰度,模拟不同粗糙度的金属表面

例如,创建一个镀铬金属材质时,可以使用较低的 LOD 值获得清晰的反射,而创建 brushed metal(刷痕金属)时,则可以使用较高的 LOD 值产生模糊的反射效果。

水面与透明材质

水面、玻璃和其他透明/半透明材质也需要精确的反射效果来增强真实感。在这些材质中,反射通常与折射、透明度等效果结合使用:

  • 使用 Fresnel 效应控制反射强度,使在掠射角度反射更强
  • 将反射颜色与折射效果混合,模拟水面的光学特性
  • 通过透明度混合,使反射与背后的物体内容自然融合

Reflection Probe 节点在这些应用中提供了基础的环境反射信息,与其他着色器效果结合可以创建出令人信服的透明材质。

动态反射效果

通过动画或脚本控制 Reflection Probe 节点的参数,可以创建各种动态反射效果:

  • 随时间变化的 LOD 值可以创建反射模糊度的动画,模拟焦点变化或视觉特效
  • 基于物体速度或其他游戏参数调整反射强度
  • 在特定事件触发时改变反射特性,如击中金属表面时增强反射

这些动态效果大大增强了游戏的交互性和视觉冲击力,使反射不再是静态的表面属性,而是能够响应游戏状态变化的动态元素。

性能优化技术

在性能敏感的应用中,Reflection Probe 节点也需要适当的优化策略:

  • 使用较高的 LOD 值减少采样成本,特别是在远处物体上
  • 根据物体与摄像机的距离动态调整反射质量
  • 在移动平台上使用较低分辨率的反射探针
  • 对不重要的小物体禁用反射或使用简化的反射计算

理解这些应用场景和技巧可以帮助开发者在保证视觉效果的同时,维持良好的渲染性能。

最佳实践与常见问题

在使用 Reflection Probe 节点时,遵循一些最佳实践可以避免常见问题,并确保反射效果的质量和性能。

反射探针设置建议

Reflection Probe 节点的效果很大程度上依赖于场景中反射探针的正确设置:

  • 在关键区域放置足够多的反射探针,确保动态物体总能找到合适的探针
  • 根据场景需求选择合适的探针类型:Baked(烘焙)用于静态环境,Realtime(实时)用于动态环境
  • 设置适当的探针影响范围,避免探针之间不自然的切换
  • 使用探针代理体积(Reflection Probe Proxy Volume)处理大型物体的反射

正确的场景设置是获得高质量反射效果的前提,Shader Graph 中的节点配置只能在此基础上进行微调和优化。

常见问题与解决方案

在使用 Reflection Probe 节点时,开发者可能会遇到一些典型问题:

  • 反射缺失或黑色输出:检查场景中是否有激活的反射探针;确认反射探针已正确烘焙;验证法线和视图方向输入是否正确
  • 反射方向错误:确认所有输入向量使用相同的坐标系;检查法线贴图是否正确应用;验证视图方向计算
  • 性能问题:减少实时反射探针的使用;增加 LOD 值降低采样质量;使用较低分辨率的立方体贴图
  • 平台间不一致:在不同目标平台上测试着色器;检查着色器变体是否正确生成;确认所有依赖功能在目标平台上可用

与其他节点的配合

Reflection Probe 节点通常与其他 Shader Graph 节点结合使用,以实现更复杂的效果:

  • 与 Fresnel Effect 节点结合,实现基于视角的反射强度变化
  • 使用 Math 节点对反射颜色进行后处理,如调整亮度、对比度或饱和度
  • 通过 Lerp 节点将反射与其它纹理或颜色混合,创建自定义的材质表现
  • 与 Time 节点结合,创建动态的反射动画效果

【Unity Shader Graph 使用与特效实现】专栏-直达 (欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

上海机场单日客流量达42.49万人次 创历史新高

2026年2月21日 19:14
2月21日,据上海机场集团消息,根据快报数据显示,昨日(2月20日),上海机场单日客流量达到42.49万人次,其中浦东机场27.57万人次、虹桥机场14.92万人次,同比去年春运同期增长6.4%,创下历史新高,迎来新年“开门红”。(每经网)

传奇掌舵人今日退休,Xbox 正式「死去」

作者 苏伟鸿
2026年2月21日 18:52

动荡了几年的微软游戏业务,今天又迎来巨大人事变动。

在 Xbox 诞生之初就已经加入团队,掌舵 Xbox 长达 12 年的传奇高管菲尔 · 斯宾塞(Phil Spencer),今天宣布退休,即将离开微软。

他亲眼见证 Xbox 的诞生,在低谷时刻出手拉了它一把,最终又在时代转向之际,选择「亲手」为传统主机时代画上句号。

从狂热玩家,到 Xbox 掌舵人

对于全世界的玩家来说,菲尔 · 斯宾塞这个名字恐怕不会陌生,甚至还有几分亲切,在粉丝眼中,他几乎就是 Xbox 品牌的「吉祥物」。

1968 年,菲尔 · 斯宾塞在美国华盛顿州出生。对于有点内向的他来说,电子游戏很早就成为了生活的一部分。

在华盛顿大学读书的时候,斯宾塞遇到了自己的第一款「人生游戏」——《机器人 2084》(Robotron 2084)。他在学校附近的 7-11 便利店街机上度过了无数个夜晚,也因此接触到了古早的电子游戏社区。

很快他也意识到,游戏不仅属于朋友之间。他和父亲经常一起玩  Commodore 64 上的「One-on-One」篮球游戏,这也是他第一次和家人一起玩游戏。

▲ 图源:YouTube@Polaventris

1988 年,距离 Xbox 诞生还有 13 年,刚满 20 岁的斯宾塞加入了微软。虽然他一开始担任的是技术岗位,但除了写代码还做过项目管理、商务协调等等,工作经验相当多元。

即使工作很忙,斯宾塞并没有放弃对游戏的热爱。很快同事也发现了身边有这么一号「游戏狂人」,不仅在办公室打《网络创世纪》——斯宾塞甚至是这款游戏的「测试服」玩家,他还经常出没于街头的街机厅。

▲ 《网络创世纪》

2001 年,为了抵御 PlayStation 2 在游戏和多媒体上的冲击,微软推出 Xbox 主机。作为一名知名游戏发烧友,斯宾塞很快也被调往 Xbox 部门,担任微软游戏工作室 EMEA(欧洲、中东和非洲)总经理,负责和游戏工作室,例如 RARE、狮头的合作。

Xbox 360 时代,他开始整合内容资源,在微软内部推动自有 IP 与工作室的长期布局,《战争机器》《光环》等经典第一方系列,都是他推动的项目。

▲ 《战争机器》限定版 Xbox 360

其实从这些早年的工作方向可以看出,比起 Xbox 硬件,斯宾塞的工作重心放在了 Xbox 的游戏内容和软件服务,这样的取向在他 2014 年正式接手 Xbox 部门后,彻底影响了这个品牌的走向。

Xbox One 发布后,由于强调多媒体消费而非游戏,遭到了玩家的强烈不满。2014 年,斯宾塞临危受命出任 Xbox 掌门人,上任第一件事就是将焦点拉回「游戏」:取消强制联网、推动 Xbox 兼容计划,并进一步加强培养第一方工作室。

与此同时,斯宾塞也萌生了对游戏租赁服务的构思。

彼时,影视流媒体 Netflix、音乐流媒体 Spotify 开始崭露头角,微软也正在不断加强云服务的战略,斯宾塞和 Xbox 部门决定将游戏租赁服务转向订阅模式。

2017 年 6 月,Xbox Game Pass 正式问世,允许用户通过云端下载和游玩游戏,例如 Windows、iOS、Android,而不仅限于 Xbox 主机,整个 Xbox 品牌开始了战略转型。

除此之外,斯宾塞还主导了 2020 年对 Bethesda 以及 2023 年对动视暴雪的收购。后者成为了游戏史上规模最大的收购之一,也让微软的内容储备空前强大。2022 年,Xbox、Bethesda、动视、暴雪、King 等相关业务正式组合并升格为「微软游戏」Microsoft Gaming,斯宾塞出任 CEO。

但斯宾塞并没能带领微软游戏业务再创辉煌。这两年,微软不断传来关闭工作室、裁员的消息。在今年 1 月份的财报中,整个 Xbox 部门营收同比下降 9%,硬件业务同比下滑 32%。

在索尼 PlayStaion 5 和任天堂 Switch 销量都破亿的情况下,同一世代的 Xbox Series S|X 销量预估不足 3000 万,是微软销量表现最差的游戏机。

▲ 斯宾塞和 Xbox Series S|X

而 Xbox Game Pass 已经成为了 Xbox 业务的收入支柱。在 2025 财年,XGP 创造了 50 亿美元的收入。只是成本和开支不断增大,微软也不得不调整 XGP 的费用和产品组合。1 月财报显示,Xbox 游戏订阅收入下降了 5%。

与此同时,微软却对 Xbox 的盈利能力提出了 30% 利润率的高要求,作为对比,近几年 Xbox 的利润率在 10% 到 20% 之间浮动。

不管怎么看,Xbox 又来到了一个低谷时刻,只是上一次扶大厦之将倾的人,这次选择了离开。

斯宾塞将会持续担任顾问直到今年夏季。至于 38 年的微软和 Xbox 之旅后,这位游戏迷人生的下一道「关卡」在何方,还尚未可知。

AI 出身的接班人

对于外界来说,斯宾塞选择在这个时间点退休,多少有点始料未及:去年夏天微软才说完「斯宾塞短期内不会离职」,也有爆料称斯宾塞至少会留任到下一代 Xbox 发布。

更耐人寻味的是,原本被业内视作斯宾塞「接班人」的 Xbox 总裁 Sarah Bond,也在同一时间宣布辞职,即将离开微软。

在宣布辞职前 3 个小时,Bond 还在领英上发布了和工作相关的动态,征求大家对 Xbox 无障碍功能的意见,说明离职很可能是一次临时决定。

接棒斯宾塞成为微软游戏 CEO 的,则是一个大部分玩家此前都未曾听过的名字——Asha Sharma。

接手游戏部门之前,Asha Sharma 主要负责 AI 业务,担任微软 CoreAI 产品总裁,也在 Meta 出任过领导岗位。纵观她的履历,微软游戏业务 CEO 是她第一个和「游戏」有关的职务。

比起知道游戏哪里好玩,这位高管更擅长将 AI 深度集成至开发流程,优化整个开发进程提升效率。

在首条备忘录中,Sharma 表示自己的第一项任务就是:了解 Xbox 业务的运作原理,并保护它。

除了承诺不会让「没有灵魂的 AI 泔水(AI Slop)」进入 Xbox 游戏中,Sharma 还强调,自己将致力于「Xbox 的回归」。

她还提拔了此前执掌 Xbox 游戏工作室的 Matt Booty, 他将成为微软游戏的执行副总裁和首席内容官,将负责进一步整合游戏内容。

▲ 左:Asha Sharma,右: Matt Booty

和本身就是一名狂热玩家的斯宾塞对比,Sharma 连是不是玩家都要打一个问号,这样的落差自然引来了玩家们的质疑和担忧,主机文化的那种认同感也会松动。

当然,这也不代表 Xbox 品牌的末日到来,主管不是玩家,不代表她做不好游戏业务。任天堂传奇社长山内溥本人也不玩电子游戏,但在他的带领下,任天堂从卖纸牌的玩具公司,摇身一变电子游戏世界的霸主。

这位 AI 主管入主 Xbox,说不定也能为这个危机四伏的业务,注入全新的血液。

Xbox 会回归,但回归的已不是「Xbox」

在斯宾塞的带领下,「Xbox」整个品牌已经彻底转型,从传统围绕主机硬件构建生态,变成通过云服务让生态无处不在。

作为 Xbox 护城河的《光环》系列,将于今年正式登陆索尼 PS 平台,已经意味着微软彻底放弃主机大战那种竞争模式,让更多人玩上游戏更重要。

比起销量平平的 Xbox Series X|S 主机,微软屡次肯定商业价值的游戏订阅服务 Xbox Game Pass,才明显是这几年 Xbox 品牌的「旗舰产品」。

这种前提下诞生的次世代 Xbox,自然也会有所不同。

不管是多个媒体爆料,还是微软官方多重暗示,我们几乎可以确信,下一代 Xbox 主机和掌机,会更接近一台 Windows PC,不仅兼容 Xbox 生态,也能和 PC 一样使用 Steam、Epic 等第三方游戏商店。

微软 CEO Satya Nadella 在一场访谈中透露:

人们认为主机和 PC 是两种不同的东西,这有点好笑。我们打造游戏主机是因为想要制造一台性能更好的 PC,以便可以进行游戏,所以我想重新审视一些传统观念……游戏主机会提供强大的的性能,我认为也会推动系统的发展。

微软和华硕合作的 ROG Xbox Ally 掌机,完全可以看作是一台未来 Xbox 的雏形——运行完整的 Windows 11 系统,使用 Xbox 全屏界面覆盖,只启动必要的系统进程,确保硬件能释放出更多性能。

这也意味着,所谓的「Xbox 硬件」不会只是微软独家产品,更多 OEM 厂商可以取得授权,打造自己的 Xbox 主机或掌机,就像 PC 产品一样。

对比索尼、任天堂这些对手,跳出硬件拘束,平台更广阔的微软,更能把 Xbox 整合成「服务 + 品牌 + 内容」的产品形态,这在游戏行业也是非常少见的。

AI、云计算设施上的布局,也是微软这家科技企业独有的优势。一月份,Google DeepMind 发布了三代视觉语言模型 Genie 3,其快速生成可交互 3D 世界的能力,让游戏引擎巨头 Unity,以及Take-Two、任天堂、CD Projekt Red 等制作商股价应声下跌,直观反映了 AI 对于传统游戏制作的冲击之大。

▲ Genie 3 生成的游戏场景

选择一位熟悉 AI 的高管出任 Xbox 掌门人,释放的信号已经不能再明显:在这个「AI 改变万物」时代,微软也有意用 AI 来改变传统游戏的创作方式。

▲ Asha Sharma 和菲尔 · 斯宾塞

这不意味着 AI 就要取代传统的人类开发游戏,AI 只是一种手段和技术,决定游戏灵魂的还是人类的创意和想法,而刚好,微软手里同样也有着不少大名鼎鼎的游戏开发者资源。

回到传统的「游戏大战」视角,比起任天堂索尼,Xbox 的处境当然相对较差,面临的压力也巨大,但背靠科技公司的微软,反而手上能整合的资源最多。

对于那些原教旨主义的玩家和 Xbox 粉丝来说,很可惜,曾经纯粹的主机和游戏品牌「Xbox」,甚至在菲尔 · 斯宾塞离开之前,就已经「死去」。

但对于微软,以及整个 AI 时代来说,一个崭新的 Xbox,正在诞生。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


丰田汽车在美召回4374辆雷克萨斯LX600

2026年2月21日 18:46
2月21日,据美国国家公路交通安全管理局(NHTSA)披露,丰田汽车正在召回部分2025-2026年款雷克萨斯LX600车型,共计4374辆。召回原因为:若变速箱电磁阀发生故障,变速箱电子控制单元(ECU)与发动机ECU之间的通信可能中断,从而导致变速箱损坏。经销商将免费升级变速箱ECU软件。(界面新闻)

1000 块/年的输入法,我用它习惯了「口喷」,再也回不去打字了 | AI 器物志

作者 苏伟鸿
2026年2月21日 18:34

编者按:
当 AI 开始寻找自己的形状,有些选择出人意料。
AI 在智能手机上生出了一颗独立按键,似乎让智能手机找回了久违的进化动力。眼镜凭借着视觉和听觉的天然入口,隐隐有了下一代个人终端的影子。一些小而专注的设备,在某些瞬间似乎比 All in one 的设备更为可靠。与此同时,那些寄望一次性替代手机的激进尝试,却遭遇了现实的冷遇。
技术的落地,从来不只是功能的堆叠,更关乎人的习惯、场景的契合,以及对「好用」的重新定义。
爱范儿推出「AI 器物志」栏目,想和你一起观察:AI 如何改变硬件设计,如何重塑人机交互,以及更重要的——AI 将以怎样的形态进入我们的日常生活?

我很难用熟悉的软件分类去安放 Typeless。

它跟传统输入法格格不入——界面里几乎看不到键盘,最显眼的是一个语音按钮。它也跟那些自称「AI 加持」的输入法不太像,那些产品总喜欢把功能铺满首页,Typeless 的功能反而少得可怜,像是故意把选择题删成了一道填空题。

这份不合群带来一个关键词:越界。

输入法原本服务人与人沟通,目标清晰——打字更快,选词更准。Typeless 把边界往外推了一步,它更在意把自然语言说出的需求梳理得井井有条。它把语言提炼成想法,或者说,它从一段话里捞出真正的意图,再把意图写成一段能直接用的文字。

输入的对象变了。不只是写给人,更多是写给模型。

一款会思考的输入法

我第一次意识到它「会思考」,是在最普通的口述里。

说话时会绕,会补充,会重复,也会用很多填充词。Typeless 的输出更像想清楚之后才落笔的版本——句子更短,信息更集中,语气更收敛。它不执着把说过的每一个音节都留下来,更在意到底想表达什么。

▲ 口述内容被 Typeless 转写后

临时改主意时,差异更明显。传统听写会把自我修正一股脑堆在屏幕上,留下许多中间态。Typeless 更像把中间态折起来,只把最后那个「定稿」留下。屏幕上出现的不是过程,是结果。

需要把一段想法拆成条目时,用普通输入法得先说完再自己排版。Typeless 往往会主动把结构摆出来,逻辑顺序更清楚,段落边界更干净。它像是随手把笔记整理了一遍。

「边说边改」是另一种用法。说完一段话,接着补一句改写要求——更克制、更正式、更短,或者把语气改成邮件——它会在原文上直接调整。不需要停下来选字、删句、重写开头,只要继续说出修改意图。

翻译也是高频场景。需要中英来回切换时,它把翻译变成输入动作的一部分。更省心的是语气处理,它不会把句子翻得像说明书,整体更接近日常沟通。

在办公室或通勤场景里不方便大声说话?它提供了小声输入一类的模式。语音输入过去常被「场合」限制,这类适配决定了它能不能真的用起来,而不是只在安静房间里表现良好。

常用表达也能做成快捷方式——一段固定格式的确认信息,一段常用的工作回复。Typeless 更像把这些东西做成可调用的块,减少重复劳动。输入法从「敲字」变成「调度」。

这些体验汇总到一个点上:Typeless 一直在 Thinking。它把杂乱的口语消化掉,再把更有条理的文字吐出来。它不追求完整复刻说话的全过程,它在整理真正的想法。

这是它最不一样的地方。

AI 器物的新物种

在讨论 AI 产品时,我们更习惯看到的是软硬结合的新尝试——智能眼镜、AI 耳机、豆包手机,它们在新场景里重新定义硬件的形态和交互方式。Typeless 走的是另一条路。

它是纯软件工具,但本质上仍然是硬件的延伸。

从打字机到键盘,再到输入法,这条线索一直存在。打字机把手写变成了机械敲击,键盘把机械敲击变成了电信号,输入法把电信号变成了字符选择。每一次演进,都是在人与文字之间增加一层更高效的转译机制。

Typeless 延续了这个逻辑,但加入了一个新元素——AI 不再只是辅助选字或纠错,它成为输入链路的核心。

传统输入法关心的是「把字打出来」,效率体现在敲击次数、选词准确率、响应速度。到了模型时代,真正消耗时间的往往不是第一次把需求说清楚,而是后续的反复修改。一次改动里夹着大量细节——语气、结构、删改尺度、信息顺序,每一项都需要来回拉扯。人工沟通的成本会在这一步迅速膨胀。

Typeless 解决的就是这段拉扯。

它让「说一句—改一下—再说一句—再改一下」变得顺滑,五到十分钟内把十轮调整连续做完。每一轮都能直接看到结果,马上继续下一轮。输入不再以「把字符敲完」为终点,而是以「文本进入可继续加工的状态」为终点。

这里出现了一个新的「精准输入」。

打字机和键盘诞生时,精准指向的是某个字、某句话。AI 时代的输入变长了,上下文变厚了,沟通频次也变高了。现在的精准更像针对一段超长上下文的控制:按想要的方式分段,或者连写;把某一句压短,或者把某一段扩写;要求它不要分点,或者把逻辑拆成几条。

控制对象变了,输入法的职责也随之变化。

这也是「给 AI 用的输入法」的含义。

▲ Prompt 由 Typeless 转写而成

Typeless 的重点不在社交表达的情绪张力,它更适合把需求交给模型,再把模型产出收拢成能用的文本。它强化的是人与 AI 的沟通效率。商业模式也很符合这种取向——界面极简,没有广告位,付费方式更像「为结果付费」。订阅用户不限量,非订阅用户每周有固定额度。产品用得越多,价值越容易被衡量。

把它放回国内输入法的语境,对比会更清晰。

老派输入法以搜狗为代表,今天也能加上「AI」二字,也能提供一堆 AI 功能。但它依旧像原来的产品——键盘还在,广告和功能标签也还在。输入法被迫承担太多与输入无关的任务,效率容易被稀释。

▲ 搜狗 AI 输入法

另一类是 AI 工具的延伸,比如豆包或微信输入法,它们更像把既有的 AI 能力塞进键盘里,做成一个入口。入口当然有用,但入口并不等于工具。入口解决的是「去哪里用 AI」,Typeless 更关心「怎样把 AI 用得更精确」。

▲ 左边为豆包输入法听写,右边为 Typeless 听写

真正的 AI 输入法,服务的对象变了。它主要服务与模型的高频沟通,服务长上下文里的精确控制,服务反复修改直到结果落地。它不需要把自己做成一个热闹的广场,它只要把那条最难的链路打通。

它也有副作用。用它跟同事沟通时,偶尔会显得过于干净,像把语气里的缓冲都删掉了。对方会觉得不够有人味。会在这种场景里切回普通输入法,手动敲几句更口语的句子,补一个表情,或者加一段无意义的笑声。这不是 Typeless 的问题,而是它的真实位置——它最自然的场景是与 AI 沟通,不是与人闲聊。

▲ 给同事发显得有点「人机」感

输入法向来是残酷的赛道。到处都能用,也意味着到处都会被挑剔。每一次卡顿、每一次误判、每一次隐私疑虑,都会直接影响它能否留下来。Typeless 要证明的不是「模型有多强」,而是「日常输入是否真的变快、变准、变省心」。

当人与 AI 的沟通变得日常,输入法可能会成为最隐蔽、也最核心的接口。它要做的不是替用户写完一切,而是把说出的信息整理成更可控、更可迭代的文本,让「多轮修改」从一种负担变成一种自然动作。

这类产品最终能不能站住脚,取决于两件事:一是它能否在所有细碎场景里保持稳定,二是它能否让「为结果付费」变得理所当然。

输入层向来没有中间地带——要么融入习惯,要么被迅速替换。Typeless 作为 AI 产品演进史上的一个新节点,把自己定位在了那条更窄、也更陡的路上。

One more thing: 我们是怎么用嘴「喷」出一篇文章的

上面的这些文字,以及下面的部分文字,我们全程只动了嘴皮子,指挥 Typeless、ChatGPT、Claude 等工具完成,没有手打一个字。

按照以往,要写一篇这样的文章,最少也得花上 2 个小时,现在只用了 30 分钟。

先介绍一下这个产品的具体细节。Typeless App 支持手机端的 iOS 和 Android,以及电脑端的 Windows 和 Mac。

免费方案提供每周 4000 字转写;而付费没有字数的限制,每个月 30 美元,每个季度 60 美元,一年 144 美元。

这个价格并不便宜,但它很符合 AI 时代「付费交货」的结果导向模式,即使是免费用户,也不会遇到广告和太多限制,最主要的差距仅限转写字数。

其实 Typeless 不太像一个「输入法」,它完全没有传统的键盘,只有少数几个按键,更不用提什么 AI 斗图、表情包的功能,只做好「语音转文字」的本职工作。

我很喜欢 Typeless 的在设备上全局的集成形式——手机上是输入法,电脑上是热键,让它可以像 AI 助手一般跨应用使用,这是 ChatGPT 无法给出的细节体悟。

整个过程还挺有意思,一开始我们只是想测试用 Typeless 和 ChatGPT 进行写稿的过程,但随着一轮一轮的对话深入,稿子不断打磨,最终出来了一篇观点明确的文章,不仅行文流畅,AI 味也很少。

一开始,我们先抛出了一些初步的想法,关于 Typeless 这个产品的一些观点,以及资料收集和写作注意事项,这些「意识流」的口述被 Typeless 整理成条理清晰的文字,直接用作 ChatGPT 的提示词。

ChatGPT 给出的第一版稿件没啥信息量,结构也不正确,语言平铺直叙还很有 AI 味,距离一篇好看的文章还有不小距离。换做平时,想要给细致的修改建议,不免得要花大量的笔墨给出新的提示词。

▲ Prompt 由 Typeless 转写

但现在我们有 Typeless,只要把听写打开,我们可以从头到尾一句一句提修改意见,并根据文段补充相应的观点和叙述。

▲ Prompt 由 Typeless 转写

我们需要尽可能给出细节,比如对比 Typeless 和搜狗、豆包、微信输入法区别的部分,就需要强调这几种产品的差异,AI 在写作时才能凸显 Typeless 的优势。

▲ Prompt 由 Typeless 转写

经过几轮的修改,ChatGPT 生成的内容已经相对完善,这时候我们可以换用 Claude 进行润色。

我们首先给 Claude 喂了几篇爱范儿写过的 AI 新硬件文章,让它充分学习我们行文的风格,据此来修改 ChatGPT 的草稿。

Claude 的初稿也还有提升空间,这时候我们可以继续用 Typeless 帮我们转述一些相对更细节的修改建议,直到满意为止。

▲ Prompt 由 Typeless 转写

其实我们对着 Typeless 侃侃而谈的文本量,累计可能已经比最终的成稿还要大,但出稿的效率大大提升,并且过程要比单纯写作更加轻松。

AI 时代,Typeless 应该「无处不在」

一开始试用 Typeless 的时候,作为一个不太习惯用语言来梳理想法和表达自己,也不需要长篇大论去表达想法的人,我会觉得它不适合我,更适合天天需要给出大量反馈的领导、Mentor、甲方人群。

但进一步探索使用之后,我觉得我还是狭隘了。在这个 AI 时代下,Typeless 不应该只是一个独立的 App,更应该成为一种「标配」无处不在。

从小处说,「语音转文字」,远远不能停留在「准」,在 AI 时代下更应该追求「精」。以后发语音转文字就全是精炼的信息,而不是满屏的「呃」「那个」以及口误。

▲ 42 秒的语音有用信息只有 10 个字

比起给爸妈手机装一个 Typeless,我更希望类似的功能直接集成到微信中——或者说,所有应用内置的「语音转文字」功能,都值得以 Typeless 的方式重做一遍。

更大的价值,在于 Typeless 给 AI 交互提供了一种新的可能。

哪怕是每天都在写稿,我的表达能力经常追不上自己的想法。甚至不是写稿,只是用键盘和 ChatGPT 对话,很多时候火花在敲击字母的时候,就已经熄灭。

改成开口说话,事情会轻松很多。我不必先想好结构,也不用马上挑最精确的词,语言会先把材料「拽」出来,观点和洞察会更自然而然流淌。

这就像在现场指导一个实习生做修改,指令可以很细,细到每一句话怎么落地——是的,我们每个人都有了 AI 作为「乙方」。

指望「一句话」让 AI 生成一切,基本不现实,信息密度太低,AI 很容易离题,素材又撑不起来,于是成品常常空、泛、虚,表面上写完了,读起来却像没落过笔。

对于 AI 来说,「上下文」很大程度决定了生成的质量,我们必须要给模型「喂」大量的想法、观点和语料,才能得到更符合预期的结果。为什么这两年内存价格大涨?要运行和训练 AI,超大的上下文必不可少,于是 AI 行业产生了对内存的巨大需求。

用 Typeless 的体验,更像是在给 AI 喂一份更丰富的语料,生成的内容有据可依,观点也够牢靠,AI 更多只是负责把这些碎片变成更好读的文章。

所以,不仅微信可以集成类似 Typeless 的功能,所有的 AI 公司,完全可以把这种「AI 翻译层」集成在聊天机器人之中,引导用户把提示词往多了说。

而只要用户给 AI 注入的内容够多,AI 模型能力的差距,也会被进一步缩小。

▲ 用 Typeless 转写的超长 Prompt

或许有人会对 Typeless-ChatGPT 这套解决方案有点悲观,这岂不是意味着,人类创作真的会彻底在 AI 时代消亡?

是,但又不全是,Typeless 只能消除「写作」这件事的成本和门槛,但却进一步凸显出「思想」的重要,让人类的感悟、观点、洞察变成了写作真正的核心。

#欢迎关注爱范儿官方微信公众号:爱范儿(微信号:ifanr),更多精彩内容第一时间为您奉上。

爱范儿 | 原文链接 · 查看评论 · 新浪微博


清华AIR团队揭示人类与智驾算法视觉注意力的本质差异

2026年2月21日 18:21
2月21日,据清华大学智能产业研究院消息,清华大学智能产业研究院AIR团队于2026年2月发表在《npj Artificial Intelligence》的研究《驾驶任务中的人类与算法视觉注意力》,以自动驾驶这一安全关键领域为载体,首次通过“人类眼动追踪实验+算法对比验证”的双轨设计,系统性拆解了人类与算法视觉注意力的本质差异。其核心价值在于提出人类驾驶注意力的三阶段量化划分框架,并证实:算法视觉理解的核心缺陷是缺乏“语义显著性提取能力”,而融入人类检查阶段的语义注意力,能以经济高效的方式填补专业算法的“语义鸿沟”与大模型的“接地鸿沟”,无需依赖大规模预训练。

崔东树:中国汽车海外产销体系完成从“产品出海”到“产业出海”关键转型

2026年2月21日 17:57
2月21日,乘联分会秘书长崔东树发文表示,2025年海关数据显示中国汽车整车出口达832万台,这一数据虽引发市场部分质疑,但跳出单一整车出口的统计维度,从全球化全产业链视角来看,中国制造及自主品牌汽车的海外销量已突破900万台。这一数值涵盖自主品牌整车直接出口、国际车企中国基地出口、KD件海外组装量及中国自主品牌海外基地产销量,不仅是中国汽车产业全球化的阶段性成果,更标志着中国汽车海外产销体系完成了从“产品出海”到“产业出海”的关键转型。当前,中国汽车正以整车、零部件、海外基地协同发展的格局,沿着全球汽车产业的发展规律稳步前行,向海外销量2000万台的目标迈进,这一征程不仅将重塑中国汽车的全球地位,更将推动中国制造向全球价值链中高端持续攀升。

iOS + AI ,国外一个叫 Rork Max 的项目打算替换掉 Xcode

2026年2月21日 17:34

最近看到一个很有意思的项目,它是一个由国外 Rork 团队推出的 AI 移动应用开发平台,宣称是“全球首个在浏览器中构建原生 Swift 应用的 AI 工具”,也就是,你可以不需要 Mac 和 Xcode ,同时一次性完成 iPhone、手表、iPad、电视和 Vision Pro 的应用,甚至还有 AR 和 3D 支持。

所以它的产品逻辑是:用户只需在浏览器中输入自然语言描述,AI 就会自动生成 SwiftUI 代码,然后编译并在云端模拟器中运行,最后支持一键发布到 App Store。

什么 swift 版本 uniapp ?

听起来有点玄乎,但是实际上其实就是 Rork 在后端部署了大量的物理 Mac 节点或 Mac 云实例,当你开始一个项目时,系统就会动态分配一台运行着 XcodeiOS SDK 的 Mac 给对应会话。

也就是所有的编译、链接、资产打包过程都在真实的 macOS 环境下完成,生成的是 100% 的原生 Swift/SwiftUI 代码。

所以实际上就是:Cloud 版本的 Xcode/Mac ,然后搭配 Claude Code 和 Opus 4.6 ,然后生成对应的 iOS App 并提交 Apple Store 审核。

而 Rork 在这里也是采用了类似于云游戏的实时视频流协议(低延迟传输),所以你在浏览器里的每一次点击都会传回云端 Mac 的模拟器,画面变化再实时推送到前端

实际上就是一个远程主机,本质和 AI Studio 类似。

当然,Rork Max 的核心肯定还是他们的 Agent 管理和产品流程,这里的 AI Agent 除了利用 Opus 4.6 写代码之外,还要管理它的所有报错,测试运行和工程管理,同时 Rork 内置了 App Store Connect 的自动化流程,用户登录 Apple ID 后,AI 可以代理证书配置、App 打包和提审等流程。

从这里看,Rork Max 的客户更多的可能是非开发者,所以它的目标是将复杂的工程基座(Mac 硬件 + Xcode SDK + 苹果证书体系)完全抽象化,让开发者只需要关注逻辑和创意

另外,这里 Rork 自己强调了“非模版化”。它不是通过预设模版拼凑应用,而是通过大模型实时推理,通过自己实现的“持续上下文注入”的技术,让 AI 记住你之前所有对 UI 的微调,确保跨平台迁移时风格的一致性。

实际上它更多是一个从零构建、测试、安装并上架的 Apple 体系生产平台。它直接把“idea → 上架 App Store 的原生 Swift 应用”压缩成一个网页操作,从而大幅度降低了门槛。

官方演示视频中,从零到可玩的游戏原型大概 30–60 分钟:

另外 Rork 也表示后续会支持直接导入老项目的功能,不过对于这种场景,基本都是已经有开发者维护的项目场景,我比较怀疑是否会有受众,虽然貌似真的有:

目前已经有一些 Rork max 用户开始体验,反馈褒贬不一,但是我是没真实体验的,因为 Rork Max 的价格还是挺感人:

为什么不体验其他的?因为我看到所有说不好用的回复里,官方都是问:你是否打开了 Rork Max

当然,觉得它有意思的原因,也是它这个产品形态或者是未来的代表之一,开发者不再需要装什么 IDE 或者 SDK ,甚至都不需要纠结是 win 还是 mac 甚至 linux ,只需要一个入口,就可以完成需要开发,当然,那时候如果真的到来的话,也许开发者也不是开发者了,可能更多只是 token 账单的消费者。

春节档票房超50%来自三四线城市

2026年2月21日 17:33
据网络平台数据,2026年春节档档期总票房(含预售)破40亿。如今,在春节假期看电影已经成为越来越多基层群众的新年俗。随着我国电影市场持续向纵深发展,“下沉”市场在春节档展现出了不俗的消费潜力。据票务平台数据显示,三、四线城市已连续三年成为春节档票房的绝对主力。除了影片内容更贴合地域偏好等传统原因,今年春节档,AI购票的全新方式也让‌购票更加便捷。电影数据分析师陈晋表示:“截至目前,三、四线城市贡献了今年春节档总票房的53.72%,较去年大幅增长,同时也是历年春节档最高贡献率。”(央视财经)

React组件通信:从零开始掌握Props传递

2026年2月21日 17:07

React组件通信:从零开始掌握Props传递

前言

在React开发中,组件化开发是核心思想。就像搭积木一样,我们把页面拆分成一个个独立的组件,然后再组合起来。但是,这些组件之间如何交流呢?今天我们就来深入浅出地学习React组件通信的基础 —— Props。

第一章:认识组件化开发

什么是组件?

组件是React应用的最小开发单元,它可以是一个按钮、一个卡片、一个弹窗,甚至是整个页面。通过组件化,我们可以:

  • 复用代码:写好一个组件,多处使用
  • 便于协作:团队成员可以并行开发不同组件
  • 易于维护:每个组件独立,修改一个不影响其他

看一个最简单的组件:

// Greeting.jsx
function Greeting() {
  return <h1>你好,React!</h1>
}

第二章:Props基础入门

2.1 什么是Props?

Props是React中父组件传递给子组件的数据。就像你在调用函数时传递参数一样:

// 父组件 App.jsx
function App() {
  return (
    <div>
      {/* 像传参一样传递props */}
      <Greeting name="张三" message="欢迎学习React" />
    </div>
  )
}

// 子组件 Greeting.jsx
function Greeting(props) {
  console.log(props) // { name: "张三", message: "欢迎学习React" }
  return (
    <div>
      <h1>你好,{props.name}</h1>
      <p>{props.message}</p>
    </div>
  )
}
  • 效果图

image.png

2.2 解构Props让代码更优雅

上面的写法中,每次都要写props.xxx比较繁琐。我们可以使用ES6的解构赋值:

function Greeting({ name, message }) {
  return (
    <div>
      <h1>你好,{name}</h1>
      <p>{message}</p>
    </div>
  )
}

第三章:Props进阶技巧

3.1 条件渲染与默认值

在实际开发中,我们经常需要根据条件渲染不同内容,或者给props设置默认值:

// Greeting.jsx
function Greeting({ name, message = "欢迎你", showIcon = false }) {
  return (
    <div>
      {/* 只有showIcon为true时才显示表情 */}
      {showIcon && <span>👋</span>}
      <h1>你好,{name}</h1>
      <p>{message}</p>
    </div>
  )
}

// 使用
<Greeting name="张三" message="欢迎" showIcon />
<Greeting name="李四" /> {/* 使用默认message */}
  • 效果图

image.png

3.2 Props类型检查(PropTypes)

随着项目变大,类型检查变得重要。

  • 首先我们需要先安装一个依赖包
npm i prop-types  //在集成终端输入
  • 然后我们就可以在项目中使用
import PropTypes from 'prop-types'

function Greeting({ name, message, showIcon }) {
  // ...组件逻辑
}

Greeting.propTypes = {
  name: PropTypes.string.isRequired,  // 必填的字符串
  message: PropTypes.string,           // 可选的字符串
  showIcon: PropTypes.bool,            // 可选的布尔值
}

Greeting.defaultProps = {
  message: '欢迎你',  // 设置默认值
  showIcon: false
}

第四章:高级模式 - 组件复合

4.1 children属性

children是一个特殊的prop,它代表组件的"内容":

// Card.jsx - 一个通用的卡片组件
function Card({ children, className = '' }) {
  return (
    <div className={`card ${className}`}>
      {children}     {/* 这里渲染传入的内容 */}
             
    </div>
  )
}

// 使用Card组件
<Card className="user-card">
  <h2>张三</h2>
  <p>高级前端工程师</p>
  <button>查看详细</button>
</Card>
  • 效果图

image.png

4.2 组件作为Props

更高级的用法是传递整个组件作为props:

// Modal.jsx - 可定制的弹窗
function Modal({ HeaderComponent, FooterComponent, children }) {
  return (
    <div style={styles.overlay}>
      <div style={styles.modal}>
        {/* 使用传入的头部组件 */}
        <HeaderComponent />
        
        <div style={styles.content}>
          {children}
        </div>
        
        {/* 使用传入的底部组件 */}
        <FooterComponent />
      </div>
    </div>
  )
}

// 自定义头部和底部
const MyHeader = () => (
  <h2 style={{ margin: 0, color: 'blue' }}>自定义标题</h2>
)

const MyFooter = () => (
  <div style={{ textAlign: 'right' }}>
    <button onClick={() => alert('关闭')}>
      关闭
    </button>
  </div>
)

// 使用
<Modal 
  HeaderComponent={MyHeader}
  FooterComponent={MyFooter}
>
  <p>这是一个弹窗</p>
  <p>你可以在这显示任何JSX。</p>
</Modal>
  • 效果图

image.png

第五章:样式处理

5.1 传统CSS(以Card组件为例)

创建独立的CSS文件:

/* Card.css */
.card {
  background-color: #ffffff;
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  padding: 20px;
  margin: 16px auto;
  max-width: 400px;
  transition: all 0.3s ease;
}

.card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}

.card h2 {
  margin-top: 0;
  font-size: 1.5rem;
  color: #333;
}

.card button {
  margin-top: 12px;
  padding: 8px 16px;
  border: none;
  border-radius: 6px;
  background-color: #0070f3;
  color: white;
  cursor: pointer;
}

在组件中引入:

import './Card.css'

function Card({ children, className = '' }) {
  return (
    <div className={`card ${className}`}>
      {children}
    </div>
  )
}

5.2 CSS-in-JS(以Modal组件为例)

直接在JavaScript中写样式:

const styles = {
  overlay: {
    backgroundColor: 'rgba(0,0,0,0.5)',
    position: 'fixed',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
  },
  modal: {
    backgroundColor: '#fff',
    padding: '1rem',
    borderRadius: '8px',
    width: '400px',
  }
}

第六章:常见陷阱与注意事项

6.1 className vs class

在JSX中,因为JSX本质是js,class是JS关键字,所以要使用className:

{/* 错误 */}
<div class="card">...</div>

{/* 正确  */}
<div className="card">...</div>

6.2 Props是只读的

重要:Props是只读的,子组件不能修改props:

// 错误 ❌ - 不能修改props
function Child({ count }) {
  count = count + 1; // 这会导致错误
  return <div>{count}</div>
}

// 正确 ✅ - 如果要修改数据,应该由父组件处理
function Child({ count, onIncrement }) {
  return (
    <div>
      {count}
      <button onClick={onIncrement}>增加</button>
    </div>
  )
}

6.3 注释的写法

在JSX中,注释需要写在花括号里:

<div>
  {/* 这是正确的注释 */}
  {/* 
    这是多行注释
    可以写多行内容
  */}
  <Greeting name="张三" />
</div>

总结

通过本文的学习,我们掌握了:

  1. 组件化思想:把UI拆分成独立的、可复用的组件
  2. Props基础:父组件通过props向子组件传递数据
  3. Props进阶:默认值、类型检查、解构赋值
  4. 高级模式:children属性和组件作为props
  5. 样式方案:传统CSS和CSS-in-JS
  6. 注意事项:className、props只读性等

Props是React组件通信的基础,掌握好Props,就迈出了React开发的重要一步。下一篇文章,我们将学习State(状态)管理,敬请期待!


如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!

在 JavaScript 中,生成器函数(Generator Function)

2026年2月21日 16:43

在 JavaScript 中,生成器函数(Generator Function) 是一种特殊的函数,它允许你暂停和恢复代码的执行。这与传统的函数“一运行到底”的模式完全不同。

我们可以从以下几个维度来深入了解它:

1. 语法特征

生成器函数通过在 function 关键字后面加一个星号 * 来定义,并在内部使用 yield 关键字来暂停执行。

function* myGenerator() {
  console.log("开始执行");
  yield "第一站"; 
  console.log("继续执行");
  yield "第二站";
  return "终点";
}

2. 核心机制:它是如何工作的?

当你调用一个生成器函数时,它不会立即执行函数体内的代码,而是返回一个特殊的迭代器对象(Generator Object)

你需要通过调用这个对象的 .next() 方法来控制函数的执行:

  • 第一次调用 .next():函数开始执行,直到遇到第一个 yield。它会暂停,并返回一个对象 { value: "yield后的值", done: false }
  • 再次调用 .next():函数从上次暂停的地方继续往后走,直到遇到下一个 yield
  • 直到遇到 return 或执行完毕:返回 { value: "返回值", done: true },此时生成器宣告完成。

3. 代码演示

const gen = myGenerator(); // 此时代码一行都没跑,只是拿到了一个控制器

console.log(gen.next()); 
// 输出: 开始执行
// 返回: { value: '第一站', done: false }

console.log(gen.next()); 
// 输出: 继续执行
// 返回: { value: '第二站', done: false }

console.log(gen.next()); 
// 返回: { value: '终点', done: true }

4. 为什么需要生成器?(使用场景)

# A. 惰性求值(节省内存)

如果你需要一个无限的序列(比如斐波那契数列),你不需要一次性把它们全存在数组里(那会撑爆内存),而是用生成器“要一个给一个”。

function* fibonacci() {
  let [prev, curr] = [0, 1];
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

# B. 可迭代协议

生成器返回的对象默认实现了迭代器协议,所以你可以直接用 for...of 循环它:

for (let value of myGenerator()) {
  console.log(value); // 会自动依次打印 yield 出来的值
}

# C. 异步编程的基石

async/await 普及之前,生成器配合 yield 是处理异步回调地狱的主流方案(比如著名的 co 库)。它能让你用同步的写法写异步代码。虽然现在大多使用 async/await,但 async/await 的底层原理正是生成器 + Promise

总结

  • 普通函数:一旦开始,不可停止,直到结束。
  • 生成器函数:像一个可以随时按“暂停键”的播放器,由外部(.next())来决定什么时候播下一段。

如果你正在处理大量数据计算,或者需要编写复杂的迭代逻辑,生成器是一个非常优雅的工具。

在 JavaScript 生成器中,除了常用的 .next(),还有两个非常重要的原型方法:.return().throw()。它们允许你从外部更精细地控制生成器内部的状态。

1. generator.return(value):强制终结

.return() 方法会立即结束生成器,就像在生成器内部执行了一行 return 语句一样。

  • 效果:生成器直接进入“完成”状态(done: true)。
  • 返回值:返回 { value: 你传入的值, done: true }

# 代码演示:

function* numbers() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = numbers();

console.log(gen.next());     // { value: 1, done: false }
// 提前终结
console.log(gen.return('手动结束')); // { value: '手动结束', done: true }
console.log(gen.next());     // { value: undefined, done: true }

# 特殊情况:try...finally

如果生成器内部有 try...finally 块,即使外部调用了 .return()finally 块里的代码依然会执行,执行完后再彻底终结。

function* demo() {
  try {
    yield "运行中";
  } finally {
    console.log("清理工作已完成");
  }
}

const g = demo();
g.next();
g.return(); // 控制台会先打印 "清理工作已完成",然后返回 { value: undefined, done: true }

2. generator.throw(error):注入错误

.throw() 极其强大,它允许你从外部向生成器内部抛出一个异常。这个错误会在生成器当前暂停的 yield 表达式位置被“引爆”。

  • 效果:如果在内部被捕获(try...catch),生成器可以继续运行;如果未被捕获,生成器会异常关闭。

# 场景 A:内部捕获错误(继续运行)

这是生成器处理异步错误最优雅的地方,它可以让异步错误看起来像同步错误一样被 catch。

function* genTask() {
  try {
    yield "第一步";
  } catch (e) {
    console.log("内部捕获到了错误:", e);
  }
  yield "第二步";
}

const g = genTask();
console.log(g.next().value); // "第一步"

// 外部注入错误
console.log(g.throw("出Bug了").value); 
// 输出: 内部捕获到了错误: 出Bug了
// 返回: "第二步" (因为 catch 之后函数继续运行到了下一个 yield)

# 场景 B:内部未捕获(生成器死亡)

如果生成器内部没有 try...catch,错误会冒泡到外部,且生成器状态变为 done: true

3. 三个方法的横向对比

这三个方法本质上都是在向生成器“发送指令”,只是目的不同:

方法

外部操作

内部对应行为

最终状态

.next(val)

传递值

yield 表达式返回 val

继续运行或完成

.return(val)

强制停止

在当前位置执行 return val

立即完成

.throw(err)

注入错误

在当前位置抛出 throw err

捕获则继续,否则崩溃

总结建议

  • .return() 常用于资源清理,比如你不再需要一个无限序列时,手动关闭它。
  • .throw() 是异步流程控制的核心,配合 Promise 使用时,它是 async/await 能够捕获异步错误(try...catch)的底层原理。

从安装到实测:基于 Claude Code + GLM-4.7 的前端生成与评测实战

作者 Lethehong
2026年2月21日 15:35

引言

近一年来,代码生成类工具逐渐从“写几行示例代码”走向“完整功能交付”,但真正落到工程实践时,很多工具仍停留在 Demo 阶段:要么跑不起来,要么改动成本过高。 本次评测的核心目标并不是追求“炫技”,而是站在开发者真实使用场景出发,验证一套组合方案是否具备以下能力:

  • 是否能在本地环境中快速跑通

  • 是否能端到端生成可演示、可交付的前端成果

  • 是否减少重复劳动,而不是制造新的维护负担

因此,本文选择了 Claude Code + 蓝耘 MaaS 平台 这一组合,从命令行工具****接入开始,结合多个真实前端需求案例,对模型在网页应用、小游戏以及 3D 可视化等场景下的表现进行实测分析。 评测重点不在“模型参数”或“理论能力”,而在于:它到底能不能帮开发者省时间、少踩坑。

最大输出和最大输入一比一,编码能力放在下面了,个人觉得是挑不出毛病的好吧。不信你试试

一、命令行使用 Claude Code(安装与配置)

步骤一:安装 Claude Code(命令行)

前提

  • Node.js ≥ 18(建议使用 nvm 管理版本以避免权限问题)。

  • macOS:推荐用 nvm 或 Homebrew 安装 Node.js,不建议直接双击 pkg 安装(可能有权限问题)。

  • Windows:请先安装 Git for Windows。

安装

npm install -g @anthropic-ai/claude-code

安装完成后验证:

claude --version

步骤二:配置蓝耘MaaS平台

1、注册 / 登录:访问**蓝耘MaaS平台**,完成账号注册并登录。

2、在「API KEY 管理」中创建 API Key,并复制备用。

在本机设置环境变量(推荐方式:编辑配置文件)

  • macOS / Linux:~/.claude/settings.json

  • Windows:%USERPROFILE%/.claude/settings.json

示例 settings.json(请替换your_lanyun_maas_api_key):

{
  "env": {
    "ANTHROPIC_AUTH_TOKEN": "your_lanyun_maas_api_key",
    "ANTHROPIC_BASE_URL": "https://maas-api.lanyun.net/anthropic",
    "API_TIMEOUT_MS": "3000000",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": 1,
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "/maas/deepseek-ai/DeepSeek-V3.2",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "/maas/deepseek-ai/DeepSeek-V3.2",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "/maas/deepseek-ai/DeepSeek-V3.2"
  }
}

  • 同时创建(或确认)~/.claude.json

    { "hasCompletedOnboarding": true }

生效提示

  • 配置完成后请打开一个新的终端窗口以载入新的环境变量。

  • 启动 claude,首次会询问是否使用该 API key(选择 Yes),并请在第一次访问时同意信任工作目录(允许读取文件以便代码功能)。

步骤三:常见排查

  • 若手动修改 ~/.claude/settings.json 后不生效:

    • 关闭所有 Claude Code 窗口,重新打开新的终端。

    • 若仍不生效,尝试删除该文件并重新生成配置(注意备份原文件)。

    • 检查 JSON 格式是否正确(可用在线 JSON 校验工具)。

  • 检查版本与更新:

    claude --version claude update

二、编码工具中使用 claude-code:三个端到端案例(含提示与实测评价)

每个案例先给出“需求 + 提示词”示例,然后给出对模型产出(代码/效果)的实测评价,评价尽量贴近工程实践:是否能直接运行、需要手工修改的点、功能完整性、性能与安全注意项。

案例 1:交互式个人血压记录网页 — 前端端到端生成

需求:希望 GLM-4.7 能够生成一个简单的个人血压记录网页应用,包括录入血压数据的前端界面和一个数据可视化大屏展示页面,要求界面美观,且支持单人登录功能。

提示词:我们向 GLM-4.7 输入了如下的自然语言提示:

请用 HTML、CSS 和 JavaScript 创建一个完整的个人血压记录网页应用。要求包括:1) 用户登录界面;2) 血压数据录入表单(收缩压、舒张压、测量日期);3) 数据可视化大屏界面,以图表展示历史血压记录;4) 整体界面风格现代简洁,配色协调美观。5) 将前端代码与样式、脚本整合在一个 HTML 文件中,方便直接运行。

实测评价(工程视角)

  • 可运行性:生成的单文件 HTML 通常能在本地直接打开并运行,图表(如用 Chart.js)能正常渲染——基本可直接跑通

  • 需要人工补充/注意点:持久化通常仅用 localStorage,真实生产需后端与加密;登录为前端模拟(不安全),若要求真登录需接入后端 API 与认证方案。

  • 代码质量:结构清晰但注释与边界检查(表单验证、异常处理)需补充;样式可直接用但对响应式与无障碍要进一步优化。

  • 总结:非常适合原型与内部演示;若要上线需补后端、认证与输入校验、数据导出等工程工作。

案例 2:Web 双人对战小游戏(Joy-Con 风格)

需求:开发一个基于 Web 的双人对战小游戏,界面风格模仿 Nintendo Switch 主机的 Joy-Con 手柄,包括左右两个虚拟手柄和中间的游戏屏幕。要求实现基本的游戏逻辑和简单的控制功能。

提示词:我们向 GLM-4.7 输入了如下提示:

请用 HTML5 Canvas 和 JavaScript 编写一个双人对战小游戏。界面要求模仿 Nintendo Switch 的 Joy-Con 手柄:左侧蓝色手柄,右侧红色手柄,中间为游戏屏幕。玩家 1 使用键盘 A/D 移动,J 攻击,K 跳跃;玩家 2 使用键盘 U/I/O 分别释放技能。游戏要求有基本的角色移动和攻击判定逻辑,界面风格统一美观。请将所有代码整合在一个 HTML 文件中,确保在浏览器中打开即可运行。

实测评价(工程视角)

  • 可运行性:模型生成的 Canvas 游戏通常包含主循环、碰撞/判定的基本实现,能够进行本地试玩;帧率在普通浏览器和单页面逻辑下表现正常。

  • 需要人工补充/注意点:物理判定、碰撞响应和输入去抖(debounce)常是“粗糙实现”,需手动修正以避免卡顿或误判;网络对战未实现(仅本地双人)。

  • 代码质量:逻辑上可读,但没有模块化(全部放在全局),不利于维护;建议拆分为模块或使用简易引擎封装。

  • 总结:适合快速原型与教学演示;若做成产品需重构输入处理、物理/判定逻辑、以及添加资源管理与关卡数据。

案例 3:前端可视化组件生成

需求:创建一个基于 Three.js 的 3D 场景,包含一个华丽的宝塔和周围盛开的樱花树,场景要求视觉精美、结构清晰,且支持用户通过鼠标或手势进行交互控制(如旋转场景、缩放视图)。

提示词:我们向 GLM-4.7 输入了如下提示:

请用 Three.js 编写一个包含宝塔和樱花树的 3D 场景。要求:1) 宝塔位于场景中央,装饰华丽;2) 周围环绕盛开的樱花树,营造花园氛围;3) 场景使用等轴测或俯视视角,光影柔和,有适当的环境光和定向光以产生投影;4) 支持鼠标拖动旋转场景和滚轮缩放查看;5) 所有代码整合在一个 HTML 文件中,使用 CDN 引入 Three.js 及其依赖,确保直接打开即可运行。

实测评价(工程视角)

  • 可运行性:多数生成结果能在现代浏览器中打开并展示场景(依赖 CDN 的 Three.js),基础交互(OrbitControls)通常可用。

  • 需要人工补充/注意点:模型与细节(如樱花树的粒子/贴图)可能是简单几何或贴图替代,若追求视觉精细需要自行替换高质量模型/贴图与烘焙光照或使用 PBR 材质;阴影与性能在低端设备上需做 LOD/简化处理。

  • 代码质量:示例代码多为教学风格,未必包含资源加载进度管理与错误处理;建议加上纹理压缩、异步加载与内存释放逻辑。

  • 总结:适合演示级视觉效果与交互交付;商业级视觉需投入美术资源并改造渲染管线与性能优化。

三、补充建议(快速 checklist)

  • 环境:Node.js 用 nvm 管理、macOS 权限使用 sudo 谨慎;Windows 使用 PowerShell / Git Bash 测试命令。

  • 配置:编辑 ~/.claude/settings.json 时注意 JSON 语法(逗号、引号、转义);每次修改后重启终端。

  • 模型选择:通过 ~/.claude/settings.json 修改 ANTHROPIC_DEFAULT_*_MODEL 字段来切换模型;切换后启动 claude 并在交互中用 /status 确认。

  • 安全/上线:所有“示例仅前端”场景上线前必须接入安全认证、后端存储与输入验证(避免注入与隐私泄露)。

总结

从本次实际使用和多个案例的结果来看,Claude Code 在接入蓝耘 MaaS 后,已经具备“工程可用级”的生成能力,尤其在以下几个方面表现比较稳定:

  • 端到端能力明确:在单文件 HTML、前端 Demo、Canvas 游戏、Three.js 场景等任务中,生成结果大多可直接运行,减少了大量“拼代码”的前期工作。

  • 适合作为原型与验证工具:非常适合用在需求验证、内部演示、方案评审和教学场景中,而不是一开始就手写全部代码。

  • 开发者心智成本低:命令行方式接入,不改变现有工作流,比网页对话式工具更符合日常编码习惯。

当然,也需要客观看待它的边界:

  • 生成代码在安全性、模块化、性能优化方面仍需要人工介入;

  • 登录、数据存储、多人协作等生产级能力仍需配合后端体系完善;

  • 更复杂的项目仍然离不开开发者的架构设计与工程判断。

整体来看,这套方案的价值并不在于“替代程序员”,而在于把开发者从重复、低价值的样板工作中解放出来,让时间更多地投入到业务逻辑、架构设计和体验打磨上。

如果你的目标是: 更快做出可运行的东西,而不是从零写样板代码,那么 Claude Code + 蓝耘 MaaS,已经是一个值得放进工具箱里的选项。

❌
❌