本节摘要:React Native 用 JavaScript 或 TypeScript 写逻辑,用 JSX 描述界面。本节讲清现代 JavaScript 的核心语法(变量、箭头函数、解构、模板字符串、异步)、TypeScript 的类型系统如何补强 JavaScript,以及 JSX 的本质——它是 JavaScript 的语法扩展,最终经转译器编译为函数调用,而不是 HTML 模板。
阅读完本节,你应当能够:
先抛一个反常识的事实:React Native 里没有 HTML。你在 JSX 里写的 <Text> 不是网页标签,而是描述"这里放一段原生文本"的语法糖。很多前端转过来的开发者,第一反应是"这不就是 HTML 吗",然后开始找 div 和 span——结果一无所获,因为 React Native 的世界里只有 View、Text、Image 这些原生组件。
这引出一个更根本的问题:为什么 React Native 选择了 JavaScript 这门"网页语言"来写原生应用?答案在于人才与生态。JavaScript 是 Web 世界最大的语言,前端开发者数以百万计;React Native 的价值恰恰是让这批人无需学 Swift 或 Kotlin,就能进入移动开发。同时 JavaScript 生态积累了海量第三方库,RN 可以借力。这本质上是一场"用人才池换性能上限"的交易,理解了这个背景,你就能理解 JSX 为什么长这样。
React Native 的代码建立在 ES6 及以后的 JavaScript 特性上,有几个你几乎每天都会用到的:
变量声明。用 let(可重新赋值)与 const(常量)取代老旧的 var。它们解决了 var 的函数级作用域与变量提升问题,让代码行为更可预期。默认优先 const,需要重新赋值才用 let,这是社区共识。
箭头函数。简洁的函数写法,并且自动绑定外层 this。在组件回调和事件处理里,箭头函数几乎是唯一的选择:
const handlePress = () => { console.log('pressed'); };
解构赋值。从对象或数组里提取值,在组件里取 props 是标配用法:
const { userName, isLoggedIn } = props;
模板字符串。用反引号包裹,支持多行与嵌入表达式,字符串拼接一目了然。比如构建欢迎语可以用 欢迎你,${name} 而不是一串加号。
Promise 与 async/await。现代异步处理方式,网络请求、文件操作都靠它,避免了回调地狱。第 6 章的网络请求会大量用到。
模块化。用 import/export 组织与复用代码,项目结构因此清晰。一个组件一个文件,导入导出各司其职,是 React Native 项目的默认组织方式。
map 渲染列表。JSX 里渲染一组数据,最常用的是数组的 map 方法:
const items = ['苹果', '香蕉', '橙子']; return ( <View> {items.map((item) => <Text key={item}>{item}</Text>)} </View> );
这七个点不是并列的随机知识点,它们共同服务一个目标:让代码更接近"描述意图"而不是"控制流程"。箭头函数缩短了回调的写法,解构缩短了取数据的写法,map 缩短了渲染列表的写法——都是把"怎么做"简化,突出"做什么"。这正好呼应 3.3 节声明式 UI 的思想:现代前端在整体上越来越"声明式",从语言特性到 UI 框架都是这个趋势。
JavaScript 是弱类型语言,写起来灵活,但大型项目里一个拼错的属性名、一个传错的参数类型,往往要到运行时才暴露。TypeScript 是 JavaScript 的超集,它补上了静态类型检查:变量、函数参数、返回值都声明类型,很多低级错误在写代码时就被抓住。
TypeScript 的核心类型要素包括:基本类型(string、number、boolean 等)、数组类型、用 interface 或 type 定义的对象结构、函数类型、联合类型与交叉类型、泛型。配合 VS Code,类型信息能带来强大的自动补全与重构能力。
💡 关键直觉:TypeScript 不是"另一门语言",而是"JavaScript 加类型"。它不改变运行时行为,只在编译期做检查。新项目强烈建议直接用 TypeScript 模板起步——前期多写几个类型注解,后期少救几场生产事故。
移动应用里大量操作是异步的:请求网络、读写存储、等待用户操作。JavaScript 的异步模型是事件循环加回调,而现代写法已经收敛到 Promise 与 async/await。看懂这两种形态,是读懂真实 RN 代码的前提。
// Promise 链式写法 fetch('https://api.example.com/data') .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error(error)); // async/await 更线性的写法 async function loadData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } }
两种写法行为等价,async/await 只是把链式调用"拍平"成更像同步的代码。第 6 章的网络请求会基于这套机制展开。现在先建立认知:异步代码不是按书写顺序执行的结果,而是按完成顺序——这条直觉能解释很多"为什么数据没更新"的谜团。
JSX 是 JavaScript 的语法扩展,让你能"在 JS 里写类似 HTML 的结构"。但理解它的关键是一句话:JSX 不是模板语言,它会被编译成 JavaScript 函数调用。
<View style={styles.container}> <Text>你好</Text> </View>
这段 JSX 经转译器(Babel)处理后,大约会变成:
React.createElement(View, { style: styles.container }, React.createElement(Text, null, '你好') );
所以 JSX 本质上是一层语法糖:你写得像 HTML,实际执行的是函数调用,产出的是组件树描述。这个编译过程发生在构建时,对开发者透明。理解它,你就明白为什么 JSX 里能直接嵌入 JavaScript 表达式(花括号内)、能写条件渲染、能调 map 方法——因为整段东西本来就是 JavaScript。
| 对比项 | JSX | HTML |
|---|---|---|
| 标签 | View、Text、Image 等组件 | div、span、img 等元素 |
| 样式 | style 属性传对象 | class 属性传类名 |
| 属性名 | camelCase(onPress、fontSize) | 小写或连字符(onclick、font-size) |
| 表达式 | 花括号嵌入 JS | 无 |
| 事件 | 传函数引用 | 字符串指令 |
这张表的每一行都是新手的坑。比如把 class 写成 className 之外的命名、把 onclick 写成 onPress 之外的形式,都会报错。记住核心差异:JSX 是 JavaScript,HTML 是标记语言。
了解了 JSX 的编译机制后,一个自然的问题是:为什么不直接用 React.createElement 写代码?答案是可读性。createElement 嵌套调用长成圣诞树,人眼几乎没法读;JSX 把它还原成"看起来像界面"的层级结构,读代码就像看界面的缩略图。这是"语法糖"最大的价值——让代码形状接近问题形状。界面的问题天然是层级嵌套的,JSX 让代码也长成嵌套的树,认知负担就大幅降低。这个"代码形状贴近问题形状"的原则,在 Flutter 的 Widget 树里会再次出现,值得留意。
把前面的语法串起来,看一个真实的最小组件:
import React from 'react'; import { Text, View, StyleSheet } from 'react-native'; function Greeting({ userName, isLoggedIn }) { return ( <View style={styles.container}> {isLoggedIn ? <Text style={styles.hi}>欢迎,{userName}</Text> : <Text style={styles.login}>请登录</Text>} </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center' }, hi: { fontSize: 20, color: '#2FA84F' }, login: { fontSize: 20, color: '#C0392B' }, }); export default Greeting;
这里浓缩了本节的所有知识点:函数组件、解构取 props、JSX 条件渲染、表达式嵌入、StyleSheet 创建样式。能独立写出并解释每一行,本节就算掌握了。
逐行读一遍这个组件,注意它是如何"描述"而不是"操作"的:没有任何一行代码去"设置文本颜色"或"决定显示哪段文字",所有决定都由 isLoggedIn 这个值推导出来。这就是声明式写法的雏形——第 3 章后半部分我们会把这条线完整展开。
⚠️ 常见坑:把 JSX 当成模板字符串拼接。JSX 里写 if 语句会报错——因为它会被编译成函数调用,if 是语句不是表达式。条件渲染要写成语法的三元表达式或逻辑与,这是新手最先踩的坑。
💡 关键直觉:看到 JSX 花括号,就默念"这是 JavaScript"。花括号是 JS 与 JSX 的边界——边界里的一切都是普通 JS,可以用任何 JS 语法。
别背语法清单。最好的方式是打开第 2 章建的项目,改 App.js,把上面的 Greeting 组件贴进去,跑起来,改条件、改样式、加列表,看界面怎么变。语法只有在你亲手改出 bug 又修好时,才会真正长在身上。
写 React Native 的前几周,有几类错误几乎人人都会撞上,提前列出对照表能省不少排查时间:
| 错误现象 | 原因 | 修正 |
|---|---|---|
| 控制台报"findDOMNode is deprecated" | 用了老 API | 改用新渲染方式 |
| 样式没生效 | style 写成字符串 | 用对象或 StyleSheet 引用 |
| 点按钮没反应 | onPress 传了函数调用而非引用 | 写 onPress={() => handler()} |
| 列表 key 重复警告 | map 里 key 不唯一 | 用唯一 id 作 key |
| 组件渲染两次 | 忘记解构或依赖变化 | 检查 hooks 依赖 |
这张表第一列的每一条,对应到 2.1 到 2.4 的某个知识点:样式问题对应 JSX 与 HTML 的差异,事件问题对应"传函数引用"的细节,key 问题对应列表渲染的约束。把表和本节内容对着看,等于把语法要点过了一遍实战演练。
问:不会 TypeScript,能不能先学 JavaScript 版本? 可以。JS 版本完全能开发 React Native,很多教程也用 JS。但新项目建议至少认识 TypeScript 的好处,从模板创建时就选择 TS 版本,边学边用,成本最低。
问:JSX 里到底能不能写 if? 不能直接写在返回的 JSX 结构里,但可以在函数体里先算好再嵌入。比如先 let content;再用 if 给 content 赋值,最后把 content 放进花括号。本质是"表达式 vs 语句"的区别,掌握这一条就能自由组合。
问:为什么我的组件没有重新渲染? 最常见原因是直接修改了对象或数组而没有产生新引用。React 通过引用比较判断是否更新,原地改数据不会触发重渲染。解决方法是创建新数组或新对象再赋值。这个问题到第 5 章状态管理时会反复出现,现在先留个印象。
语言基础掌握后,真正的分水岭是"用声明式思维写组件"。初学时你可能把 JSX 当模板、把状态当变量,写出的组件还是命令式思路。突破这个瓶颈的办法是刻意练习:每次写界面都先写"这个界面依赖哪些状态",再写 JSX 结构。练过几个组件后,你会发现思考方式自然从"怎么操作界面"切换到"界面是状态的映射"——这时再看 3.3 节的概念,会有完全不同的体会。语法是工具,思维是方向,工具随手可得,方向需要自己转。
判断一个组件是否该拆分,有个简单标准:它是否承担了超过一个职责。一个组件既渲染列表、又处理表单、还管理导航状态,就该拆。每个组件最好只回答一个"界面问题"。这不是教条,而是可维护性的实际需要——拆得好的组件,单个文件短、测试容易、改动影响面小。看开源项目时留意它的组件划分,比背任何设计模式都直观。
语法地基打好,下一节看另一位选手的语言——Dart 与 Widget 树。你会发现"描述界面"的思路殊途同归,只是表达方式换了。