0%

Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。

所谓运行时runtime,是指程序声明周期中从开始执行到完成退出的阶段,除了运行时,还时有提及的编译阶段是 compile time,链接阶段是 link time,在前面的阶段预先做了通常在后面才方便做的事叫 ahead of time

Node.js 是一个异步事件驱动运行时 (asynchronous event-driven JavaScript runtime) 与

面试补充(2026-07-22)

本节为后续补充,用于资深软件工程师基础面试复习;上文保留原始笔记。

V8、Node.js bindings、libuv 与操作系统

可以把一次 Node.js I/O 调用理解为多层协作:

  • V8 解析、编译和执行 JavaScript,管理 JavaScript 堆与垃圾回收。
  • Node.js C/C++ bindings 把 JavaScript API 连接到操作系统能力、内置库和 libuv。
  • libuv 提供事件循环、跨平台异步抽象和线程池等能力。
  • 操作系统 提供网络、文件、进程、计时器等底层机制。网络 I/O 常利用 epoll、kqueue、IOCP 等系统能力;并非每个异步操作都占用一个 libuv 工作线程。

因此,“Node.js 异步是因为有一个线程池包办所有 I/O”不准确。文件系统、部分 DNS、压缩和部分加密任务会使用 libuv 线程池;网络 socket 通常由操作系统的事件通知机制驱动。

“Node.js 是单线程”的准确边界

通常所说的单线程,是指一个 Node.js isolate 中的 JavaScript 回调在同一时刻由一个主线程执行。这带来两个结果:

  1. 普通业务代码不必像共享内存多线程那样为每次对象访问加锁。
  2. 一个长时间占用 CPU 的 JavaScript 回调会阻塞同一事件循环上的所有连接。

但 Node.js 进程内部还可能有 V8 辅助线程、libuv 线程池和 Worker Threads,操作系统也在并发处理 I/O。多进程部署时,每个进程有独立的 V8 堆和事件循环。面试回答应区分“JavaScript 执行模型”和“整个运行时只有一个线程”。

事件循环、nextTick 与微任务

libuv 事件循环包含 timers、pending callbacks、poll、check、close callbacks 等阶段。面试中更值得掌握的是稳定关系:

  • 当前 JavaScript 调用栈必须先清空,其他回调才有机会执行。
  • Promise reaction 和 queueMicrotask() 属于微任务,会在进入下一个事件循环阶段前被清空。
  • process.nextTick() 是 Node.js 的特殊队列,优先于常规微任务处理;递归安排它可能让事件循环和 I/O 长时间得不到机会。
  • setImmediate() 的回调进入 check 阶段;timer 在达到阈值后才有资格执行,不是精确实时调度。
  • 在顶层代码里比较 setTimeout(fn, 0)setImmediate(fn) 的先后不可靠;在特定 I/O 回调中通常更容易观察到 setImmediate() 先进入 check 阶段。

不同 Node.js/libuv 版本曾调整 timer 的阶段行为,运行环境和周围 I/O 也会影响输出。资深回答应解释为什么顺序成立、哪些关系稳定,并能用最小实验确认目标版本,而不是背一串脱离上下文的日志。

I/O 密集和 CPU 密集任务

异步 I/O 提高的是等待期间的并发利用率,不会让 CPU 计算自动变快。选择方案前先给任务分类:

  • 数据库、网络和磁盘等待为主:用异步 API、连接池、超时、背压和并发上限。
  • JSON 大对象解析、图像处理、路径规划或压缩计算为主:测量 CPU 与事件循环延迟,必要时拆分、使用 Worker Threads、独立服务或原生实现。
  • 大量细碎任务:即使单次很快,也可能因队列无界和对象分配造成延迟与 GC 压力。

不要用“接口平均响应很快”替代诊断。至少同时观察延迟分位数、吞吐量、事件循环延迟、CPU、内存、GC、下游耗时和队列长度。

Worker、子进程与多进程

机制 适合场景 主要代价与边界
Worker Threads 同一应用内的 CPU 密集 JavaScript,可传输对象或使用共享内存 仍在同一进程,需设计任务池、取消、崩溃处理和数据传输
Child Process 调用外部程序、隔离不可信或易崩溃任务、使用不同运行时 IPC 和启动成本更高,但地址空间和故障隔离更清晰
多 Node.js 进程 利用多核承载独立请求或消费者 会话、定时任务和连接状态不能默认只存在一份
Cluster 多进程共享监听端口的 Node.js 内置方案 需要理解调度和进程生命周期,生产中也可由容器或进程管理器承担

不要为了“多核”把每个请求临时创建为一个 Worker。生产服务通常使用有上限的池,并定义排队长度、超时、取消和过载策略。机器人或工业控制命令还要考虑顺序和状态,不能为了并行吞吐破坏设备级串行约束。

Buffer、堆外内存与内存诊断

Buffer 表示字节数据,常用于网络、文件和二进制协议。其底层内存不一定全部计入 V8 的 heapUsed,因此只看 JavaScript 堆可能漏掉问题。

排查进程内存增长时至少区分:

  • heapUsed 持续增长:检查被集合、闭包、缓存、监听器或定时器长期引用的对象。
  • external/arrayBuffers 增长:检查 Buffer、TypedArray、原生模块和二进制数据生命周期。
  • RSS 增长但堆稳定:还可能来自原生分配、线程栈、内存碎片和运行时保留。
  • GC 频繁且延迟升高:可能是分配速率过高,不一定是对象永远无法回收。

正确流程是先用指标确认趋势和关联请求,再通过 heap snapshot、allocation profile、CPU profile、诊断报告或受控压测定位引用链。不要一看到内存高就手动调用 GC 或直接增大堆上限。

错误边界、超时和取消

Promise rejection 应在能够决定恢复策略的边界被 await、返回或捕获。EventEmitter'error' 事件若无人监听可能终止进程;流和回调 API 也有各自的错误通道。混用这些模型时要明确错误如何汇总。

下面的超时只限制单次网关请求:

1
2
3
4
5
6
7
8
9
async function loadDeviceState(deviceId: string): Promise<unknown> {
const response = await fetch(`http://device-gateway/devices/${deviceId}`, {
signal: AbortSignal.timeout(2000)
})
if (!response.ok) {
throw new Error(`Gateway returned ${response.status}`)
}
return response.json()
}

必须区分三个概念:

  • 超时限制愿意等待多久,但不一定撤销已经到达设备的命令。
  • 取消尝试终止本地或下游工作,需要调用链真正传播 AbortSignal 才有效。
  • 重试再次发起操作,只能在失败分类、次数、退避和幂等语义明确时使用。

读取状态通常容易做幂等重试;机械臂移动、PLC 写入或扣款等命令可能已经部分执行,盲目重试会产生第二次动作。需要命令 ID、状态查询、去重或业务补偿,而不是一个通用重试装饰器。

未捕获异常和未处理 rejection 表明应用越过了预期错误边界。不要依赖某个 Node.js 版本的默认行为继续提供服务;通常应记录足够上下文,触发受控停机并由外部管理器重启。uncaughtException 是最后的清理边界,不是恢复到健康状态的证据。

优雅停机、幂等与可观测性

收到 SIGTERM 等停止信号后,一个服务通常按以下顺序收敛:

  1. 标记 readiness 为不可接流量,并停止接受新任务。
  2. 为在途请求和任务设置总截止时间,允许完成、主动取消或记录未完成状态。
  3. 停止消息消费者,关闭设备连接、数据库连接池和其他资源。
  4. 尽力刷新关键日志、审计记录和指标,但不能无限等待。
  5. 正常完成时退出 0;无法安全收敛或发生致命错误时退出非 0,由外部管理器决定重启。

“关闭 HTTP server”只是不再接受新连接,不能自动处理所有 WebSocket、队列消费者和后台定时器。服务必须维护资源所有权,并让关闭流程可重复调用、具有截止时间且可测试。

日志需要关联请求、设备、命令和任务。可使用 AsyncLocalStorage 传播 correlation ID,但仍要控制敏感字段和日志量。指标至少覆盖请求量、错误率、延迟分位数、事件循环延迟、内存、队列深度和下游依赖;trace 用于解释一次请求跨服务的耗时路径。

工业遥测网关的诊断场景

假设 Node.js 网关接收机器人和 PLC 遥测,运行数小时后延迟上升。应先区分:

  1. 事件循环延迟升高且 CPU 高:查找同步解析、大循环、频繁序列化、日志格式化和热点函数。
  2. 事件循环延迟正常但下游耗时高:检查数据库、消息代理、设备连接和网络,并设置超时与隔离。
  3. 堆持续增长:检查按设备保存但无淘汰的 Map、监听器、未完成 Promise 和缓存。
  4. external/RSS 增长:检查 Buffer、图像帧、二进制协议和原生模块。
  5. 队列持续增长:生产速度超过消费速度,应有背压、合并、丢弃策略或有界队列,不能只扩容内存。

测量后再选择 Worker、Stream、批处理、限流、过载丢弃或多进程。对于安全相关设备状态,还要区分哪些消息允许采样或覆盖旧值,哪些报警与命令必须可靠保留。

常见追问与回答边界

Node.js 适合高并发,是否意味着适合所有高负载任务?

不意味着。它擅长大量 I/O 等待并发;CPU 密集回调会阻塞事件循环,需要隔离计算或选择更合适的执行方式。

增加 libuv 线程池大小能否解决所有性能问题?

不能。它只影响使用该线程池的部分 API,还可能增加资源竞争;网络 I/O、JavaScript CPU 热点和无界队列不会因此自动消失。

为什么平均延迟正常,用户仍感觉偶尔卡死?

平均值会掩盖尾延迟。应看 p95/p99、事件循环延迟、GC pause、慢依赖和队列等待,并关联具体请求 trace。

捕获 uncaughtException 后为什么不建议继续运行?

异常可能发生在修改一半的状态或资源操作中,进程是否仍满足不变量无法确认。处理器适合记录和受控退出,不适合作为长期恢复机制。

线程

Q: java线程池和cpu线程数的关系

文心一言:Java线程池与CPU线程数之间的关系主要体现在线程池如何根据系统的需求和硬件环境灵活地控制线程的数量,以及如何通过线程池管理线程来提高系统的运行效率并降低系统的运行压力。

首先,线程池通过在初始化时创建一个线程集合,然后在需要执行新任务时重用这些线程,而不是每次都新建线程。这种方式减少了线程的创建和销毁开销,从而提高了系统的性能。

其次,线程池可以控制并发的线程数量,这有助于避免因为线程数量过多而导致的系统资源耗尽或性能下降。线程池会根据任务的提交情况和系统的负载情况,动态地调整线程的数量,确保系统的稳定运行。

在Java中,线程数通常模拟的是CPU的核心数,但需要注意的是,线程数并不等于CPU的物理核心数。一个CPU核心可以对应一个或多个线程,这取决于是否使用了超线程技术。通过超线程技术,一个物理核心可以模拟出两个逻辑线程,从而提高了CPU的并行处理能力。

在设置线程池的大小时,需要考虑任务的性质。对于CPU密集型任务,如加密、解密、压缩和计算等,最佳线程数通常是CPU核心数的1~2倍。设置过多的线程并不会提升性能,反而可能导致性能下降,因为过多的线程会导致不必要的上下文切换。

另一方面,对于耗时IO型任务,线程池的大小可能需要更大,以充分利用系统的IO能力。但是,这仍然需要考虑到系统的整体负载和资源限制。

总的来说,Java线程池与CPU线程数之间的关系是一个动态平衡的过程,需要根据具体的任务需求、系统负载和硬件环境进行灵活的调整和优化。

32位/64位操作系统

32位、64位指的是CPU的GPRs(General-Purpose Registers,通用寄存器)如今计算机CPU基本上都是64位架构,运算能力是一次性64bit的整数运算,支持64位地址寻址和更大的内存

当代码编译成exe时,x86平台编译出来的exe(可执行文件)或dll(动态链接库)都是32位的。类似的,x64对应的则是64位的。

x86平台下编译的应用程序,而它所依赖的一个项目(或动态链接库)是由x64位平台编译出来的,则会提示“未能加载文件或程序集…或它的某一个依赖性。试图加载格式不正确的程序。”之类的错误。这是因为32位程序不能加载64位的dll,更不能调用其中的类、方法和对象等。

如dotnet publish -f win-x86 打包的wpf应用,在已装有vcredist x64的计算机上无法启动,查看windows evnet viewer可看到.net运行时报错:
CoreCLR Version: 6.0.2523.51912
.NET Version: 6.0.25
Description: The process was terminated due to an unhandled exception.
Exception Info: System.IO.FileNotFoundException
而安装vcredist x86后修复

Material UI

Bootstrap

Ant Design

Elastic UI

Elastic UI 框架 (EUI) 是 Elastic 使用的一个设计库,用于构建需要分享我们美学的内部产品。它分发 UI React 组件和静态资源,用于构建 Web 布局。
使用高对比度、色盲安全调色板,并通过大多数辅助技术进行测试。
可配置性足以满足各种环境的需求,同时保持品牌和低级别的一致性。

作者:快跑啊小卢_
链接:https://juejin.cn/post/7296345202238636072
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

REAVIZ

REAVIZ 是一个模块化图表组件库,它原生利用 React 来渲染组件,同时在底层使用 D3js 进行计算。该库提供了一种简单的方法来开始创建图表,而无需牺牲定制能力。

ArcoDesign

ArcoDesign 是由字节跳动 GIP UED 团队和架构前端团队联合推出的企业级设计系统,打磨沉淀 3 年之后开源,ArcoDesign 与 TDesign 开源也就是前后脚的事,让我们通过这两套开源组件,看到大厂在前端完备的设计理念和工整的生产流程。
这套 UI 库,基于 ArcoDesign 设计规范,Arco 同时提供了 React 和 Vue 两套 UI 组件库。

Semi Design

Semi Design是由抖音前端团队,MED 产品设计团队设计、开发维护的设计系统。它作为全面、易用、优质的现代企业级应用解決方案,从字节各业务线的复杂场景提炼而来,服务字节内外部 10 万+用户。经过近两年的迭代,在各类不同形态的业务落地验证后,Semi Design 已成为跨部门级的基础设施,围绕组件库形成丰富的工具链和生态。

Growth Mindset 成长思维, 是一个重要的心理学概念。斯坦福大学的行为心理学教授Carol S. Dweck是这一概念的提出者。她认为人的思维模式主要分为两种:成长型思维和固定型思维。

拥有固定型思维的人认为,人的特质和能力是天生的,后天无法改变。而拥有成长型思维的人则认为,任何能力和技能都可以通过后天努力而得到发展。他们更乐于接受挑战,并且积极提升自己的能力和技能。这种成长型思维的人更加坚韧,对挑战持积极态度,并相信自己的能力可以通过持续努力而提升。

相较于固定型思维模式公司的主管,成长型思维模式公司主管对员工的评价明显更为正面。他们更倾向相信其雇员有管理潜能,并赞扬员工富有创新精神,善于与人合作,并且笃实好学,努力成长。

总之,Growth mindset是一个积极的概念,它鼓励人们以开放和发展的眼光看待自己和他人,相信能力和技能是可以通过努力和学习来提升的。这种心态对于个人的成长和发展,以及组织的繁荣和成功都具有重要意义。

ENOENT
“error no entry” 通常是 “No such file or directory”

应用及案例

Qt是跨平台UI框架
WPS Autodesk Maya 极品飞车是Qt开发的
医疗设备软件选择Qt开发 缘于极致性能及可靠性

slot vs signal

  • connect
  • function

Qt creator

Delegate Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
ListView {

id: listview

Layout.fillWidth: true

height: 80

orientation: ListView.Horizontal

spacing: 15

displaced: Transition {

NumberAnimation {

properties: "x,y"

easing.type: Easing.OutQuad

}

}

model: DelegateModel {

id: visualModel

model: selectedItems

delegate: DropArea {

id: delegateRoot

width: tt.width

height: tt.height

onEntered: function (drag) {

visualModel.items.move(drag.source.curIndex, tt.curIndex)

}

property int visualIndex: DelegateModel.itemsIndex

DraggableIcon {

id: tt

dragParent: listview

curIndex: delegateRoot.visualIndex

sourceImg: source

keyImg: key

}

}

}

}

官方推荐的打包分发工具。

2026-07 修订:Electron Forge 现在更适合作为新项目的默认入口:它覆盖创建项目、开发启动、打包、maker、publisher,并且官方模板已明显转向 Vite / TypeScript 等现代前端工具链。electron-builder 仍然可用,尤其适合已有项目和复杂 NSIS/自动更新配置,但新项目可以先从 Forge 起步。

已有 Electron 项目迁移到 Forge:

1
2
npm install --save-dev @electron-forge/cli
npx electron-forge import

或使用 Electron Forge 脚手架创建项目:

1
npm init electron-app@latest my-app -- --template=vite-typescript

--template=webpack-typescript 仍可用于历史项目或 Webpack 生态依赖较重的场景,但新项目优先考虑 vite-typescript,开发启动更轻,和现代前端项目更贴近。

常用命令

1
2
3
npm start
npm run package
npm run make

2026-07 新增:项目结构关注点

  • main:窗口、菜单、系统能力、自动更新、原生模块。
  • preload:通过 contextBridge 暴露安全白名单 API。
  • renderer:React/Vue/Angular/Svelte 等前端页面。
  • makers:为不同平台生成安装包,如 Windows Squirrel/NSIS、macOS zip/dmg、Linux deb/rpm。
  • publishers:发布到 GitHub Releases 或企业内部更新源。

Why React Native

React Native 和前端技术生态重合度很高,学习成本低, 一份源码可以同时编译成 Android 和 iOS 原生应用. 适合更新迭代较快的项目
同时 其底层架构师根植于移动端原生API 可以自定义原生模块 以及优化性能

本质(nature)

>

在 Android 开发中是使用 Kotlin 或 Java 来编写视图;在 iOS 开发中是使用 Swift 或 Objective-C 来编写视图。在 React Native 中,则使用 React 组件通过 JavaScript 来调用这些视图。在运行时,React Native 为这些组件创建相应的 Android 和 iOS 视图。由于 React Native 组件就是对原生视图的封装,因此使用 React Native 编写的应用外观、感觉和性能与其他任何原生应用一样。我们将这些平台支持的组件称为原生组件。

在较早的React Native中通过称为Bridge的异步机制完成js与原生代码之间的通信,0.74版本开始默认使用Bridgeless mode,调用JSI访问原生代码从而提高性能和响应速度

React Native 允许您为 Android 和 iOS 构建自己的 Native Components(原生组件),以满足您开发应用程序的独特需求。React Native 还包括一组基本的,随时可用的原生组件,您可以使用它们来构建您的应用程序。这些是 React Native 的核心组件

环境配置

React Native项目

1
2
3
npx @react-native-community/cli@latest init AwesomeProject
yarn start # 启动 Metro 构建工具
yarn android

Android设备

  • USB连接Android手机
  • AVD(Android Virtual Device)模拟器

Android Studio—>工具栏Device Manager—>Add a new device—>Create Virtual Device—>选择任意设备—>Next—>UpsideDownCake API Level 34 image

开发

Android Studio开发js/ts很不习惯,可以使用vscode调试,添加react-native-tool,配置emulator到环境变量以使能被vscode调起

1
2
3
4
npm i -g react-native
react-native init mobileApp
npm run android
npm run ios

项目终端执行 npm run android 输出

1
2
3
4
5
6
7
$ react-native run-android
info Launching emulator
...
info Installing the app
> IDLE
> IDLE
...

安装完毕后
会另外启动四个窗口

  • NodeJS 窗口
  • emulator\crashpad_handle.exe
  • emulator\qemu\windows-x86_64\qemu-system-x86_64.exe
  • Android Emulator 移动设备界面

调试
在Android Emulator界面上按 Ctrl + M 弹出Emulator菜单,在菜单中点击Open DevTools启动React Native Cli自带DevTools

打包

1
2
cd android
./gradlew assembleRelease

android emulator

命令行输入emulator —help查看工具参数手册

1
2
emulator -list-avds
...

对于应用crash导致系统崩溃,尝试不加载snapshot后冷启动

1
2
3
4
emulator -avd myavd -no-snapshot-load -no-snapshot-save
emulator -avd myavd -cold-boot
# 擦除用户数据(恢复设置)
emulator -avd myavd -no-snapshot-load -wipe-data

将本地文件放入虚拟设备

1
avd push D:\Download\Game.apk /sdcard/Download/Game.apk

Internet

Platform

1
2
3
4
5
import {Platform, StyleSheet} from 'react-native';

const styles = StyleSheet.create({
height: Platform.OS === 'ios' ? 200 : 100,
});

Platform.OS返回’ios’ 或者’android’
Platform.version返回OS版本号
Platform.select根据平台返回以平台字符串为key的map中对应的value

1
2
3
4
5
6
const Component = Platform.select({
ios: () => require('ComponentIOS'),
android: () => require('ComponentAndroid'),
})();

<Component />;

Metro

The JavaScript bundler for React Native 打包器

视图

视图是React Native UI的基本组成。视图的概念可以小到屏幕上的一个小矩形元素、可用于显示文本、图像或响应用户输入。甚至应用程序最小的视觉元素(例如一行文本或一个按钮)也都是各种视图。View可以嵌套View。全部都是视图。

核心组件

  • View
  • Text
  • Image
  • ScrollView
  • TextInput
  • StyleSheet
  • Button
  • Switch
  • FlatList
  • SectionList

样式

与网页的不同

  • 子元素不继承样式
  • 驼峰命名fontSize
  • 不使用单位(px等)

StyleSheet:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { StyleSheet, Text } from 'reat-native'

const styles = StyleSheet.create({
sectionTitle: {
fontSize: 24
}
})

return (
<>
<Text style={{styles.sectionTitle}}>
</>
)

css px是逻辑像素,物理像素/逻辑像素叫设备像素比 这里css省略px,强调逻辑像素,具体显示的物理像素数量由设备根据屏幕种类换算

PPI(pixels per inch)每英寸物理像素点

RN中的Flexbox规则和Web中Css FlexBox基本一致,主要的不同就是flex-direction默认不同,css中默认是水平方向,rn中默认是垂直方向。

阴影样式

1
2
3
4
5
6
7
8
shadowBorder:{
shadowColor:'#000',
shadowOffset:{width:0, height:0},
shadowOpacity:0.6,
shadowRadius:8,

elevation:1
}

对应css

1
box-shadow: '0 0 8px 8px #000a'

即iOS 通过 shadowOffset shadowOpacity shadowRadius 来实现阴影,而 Android 则通过 elevation 来实现阴影,
ios

React Native Navigation
React Native 中导航变得很复杂,原因在于页面url不会加入window.history

  • Stack Navigator
  • Tab Navigator
  • Drawer Navigator
1
2
3
4
yarn add @react-navigation/native
yarn add react-native-screens react-native-safe-area-context

yarn add @react-navigation/native-stack --> stack导航器

编辑位于 android/app/src/main/java// 下的 MainActivity.kt 或 MainActivity.java 文件。
添加以下代码

1
2
3
4
5
6
7
8
9
10
//...
import android.os.Bundle;

class MainActivity: ReactActivity() {
// ...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(null)
}
// ...
}

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Stack = createNativeStackNavigator();

function App(): React.JSX.Element {

return (
// <SafeAreaView style={backgroundStyle}>
<NavigationContainer>
<Stack.Navigator initialRouteName='Home'>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="List" component={PatientList} />
<Stack.Screen name="Detail" component={PatientDetail} />
</Stack.Navigator>
</NavigationContainer>

// </SafeAreaView>
);
}

响应式

Dimensions API

1
2
3
4
import { Dimensions } from 'react-native'

const { width, height } = Dimensions.get('screen')//包括状态栏和底部导航
const { width, height } = Dimensions.get('window')//Android不包括, IOS上与screen相同

监听屏幕尺寸改变(横竖屏切换,折叠屏)

1
2
3
4
5
6
7
8
9
10
11
useEffect(() => {
const subscription = Dimensions.addEventListener(
'change',
({ window }) => {
setWindowSize({
width: window.width,
height: window.height,
})
})
return () => subscription?.remove() //组件unmount时移除监听
}, [])

AndroidManifest.xml

声明安卓应用版本信息,元数据,权限声明等,相应的ios应用是Info.plist

1
<uses-permission android:name="android.permission.CAMERA" />

build.gradle

配置应用的构建过程,包含依赖的仓库和插件等等
使用react-native-vector-icons需要配置build.gradle

1
apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle")

使用icons:

1
2
3
import Icon from "react-native-vector-icons/FontAwesome"

<Icon name="camera" color="#ccc" size={36} onPress={openCamera} />

从命令行安装安卓模拟器

不使用Android Studio IDE, 从命令行安装安卓模拟器

下载cmdline-tools其中包含 sdkmanager 工具和 avdmanager 工具

cmdline-tools解压到一个D:/Environment/android-sdk目录(实际上要再加一层latest),并配置到环境变量

1
2
export ANDROID_HOME=D:/Environment/android-sdk
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin

使用sdkmanager安装 platform-tools、emulator、platforms、system-images,(注sdkmanager依赖 jdk 17)

1
2
3
4
5
6
7
8
9
10
11
sdkmanager --sdk_root="D:\Environment\android-sdk" "platform-tools" "emulator"  "platforms;android-34" "system-images;android-34;google_apis;x86_64"

# 创建并启动模拟器 命名为ExpoTest
# 现在可以直接运行(自动读取 ANDROID_HOME)
avdmanager create avd `
-n ExpoTest `
-k "system-images;android-34;google_apis;x86_64" `
-d "pixel_5" `
--force
emulator -avd ExpoTest -gpu swiftshader_indirect -no-audio
npx expo start # 按 a 自动连接

Android是一个基于Linux内核的开源操作系统, 由谷歌开发.它被广泛用于各种设备, 包括智能手机, 平板电脑, 电视和智能手表.

目前, 安卓是世界上移动设备使用最多的操作系统

Kotlin是由JetBrains开发的一种编程语言. 由谷歌推荐, 无论你是不是安卓新手, 都应该把 Kotlin 作为首选 见Android 的 Kotlin 优先方法

Kotlin Doc

Jetpack Compose

Flip

  • First 元素的初始状态(比如位置、透明度等等)
  • Last 元素的最终状态
  • Invert
  • Play

A 状态到 B 状态,不用手动计算每一帧的改变,而是计算从 B 到 A 的反向动画,然后下一帧直接切换 B 状态,在把反向动画应用在 B 上。
其实真实元素是直接从 A->B,但是由于在 B 状态上加了反向动画,所以用户看起来像是过渡。

不明白 暂时没看明白