使用 Vite 与 NativeScript

Vite 是一款现代化的构建工具,能够提供快速的开发服务器和优化的生产构建。接下来,我们看看如何在 NativeScript 中使用 Vite。

从 NativeScript 9 开始,我们可以使用 Vite 作为构建工具。具体来说,Vite 开发服务器支持热模块替换(HMR),它通过 HTTP 以原生 ES 模块的形式提供你的应用,使得设备和模拟器可以通过 URL 加载模块。这让 Vite 能够实现快速、精确的 HMR 更新,成为现代开发的绝佳选择。在本篇博客中,我们将探讨如何在 NativeScript 中配置 Vite,并充分利用其功能,打造无缝的开发体验。

将项目切换为使用 Vite

只需三个简单步骤,即可将现有的 NativeScript 项目切换为使用 Vite。

步骤 1:安装 Vite 插件

bash 复制代码
npm install --save-dev @nativescript/vite

步骤 2:在 nativescript.config.ts 中启用 Vite

请按如下方式更新你的 nativescript.config.ts 文件:

ts 复制代码
export default {
  // ...
  bundler: 'vite',
  bundlerConfigPath: 'vite.config.ts',
} as NativeScriptConfig;

步骤 3:初始化 @nativescript/vite

运行以下命令以在项目根目录初始化 Vite 配置:

bash 复制代码
npx nativescript-vite init
npm install

该命令会在项目的根目录下生成一个包含必要配置的vite.config.ts文件,同时还会执行其他一些便捷操作,例如向package.json中添加支持 HMR(热模块替换)的开发脚本。现在你可以像往常一样使用ns debug ios --no-hmrns debug android --no-hmr进行开发,实现标准的应用重载;也可以直接使用package.json中新添加的支持 HMR 的脚本。接下来,让我们看看如何使用 Vite 的 HMR 功能!

探索 Vite 的 HMR 功能

配置好 Vite 后可以在开发过程中利用先进的热模块替换(HMR)功能。使用以下命令启动应用:

bash 复制代码
npm run dev:ios
# 或者
npm run dev:android

这些命令会同时启动两个并行进程:

  1. 第一个进程通过 vite serve 启动 Vite 开发服务器。
  2. 第二个进程启动 NativeScript CLI,它会调用 Vite 来构建应用,并在其中嵌入 HMR 客户端。

当应用在目标平台设备上启动时,应用内的 HMR 客户端会连接并监听来自 Vite 开发服务器的更新。首次使用 Vite 的 HMR 启动应用时屏幕上会非常短暂地显示一个占位符界面,看起来像这样:

此时应用正在连接 Vite 开发服务器,通过 HTTP 加载应用的各个模块。完成这次初始连接后,应用将像往常一样启动,随后在修改代码时动态地通过 HTTP 加载更新后的模块。

这正是 HMR 在 Web 浏览器中的工作原理,但现在我们可以在原生 iOS、Android 和 visionOS 开发中实现同样的效果!

使用 onHmrUpdate 启用自定义 HMR

在某些场景下应用可能受益于使用onHmrUpdateAPI 来自定义 HMR 更新的执行方式。例如可能希望在模块更新时保留应用中的某些状态,或者希望重置某个视图(这通常由 NativeScript 的生命周期事件如loaded处理),而该事件在 HMR 更新期间可能不会被重新触发。这个项目 就演示了这样一个案例:我们需要在多次 HMR 更新时更新 TabView 的iosBottomAccessory。我们可以利用onHmrUpdateAPI 来监听模块更新,并在每次更新后重新应用iosBottomAccessoryonHmrUpdateAPI 的文档请查看此处

在 HMR 更新间保持实例持久化

一种常见的做法是将数据或实例存储在 import.meta.hot.data 上。NativeScript 团队即将支持这种模式(参考 此 PR)。鉴于 HMR 仅用于本地开发,你也可以将实例存储在global对象上,并通过交换其原型(prototype)在模块重新评估时进行更新。由于 Vite 的 HMR(特别是在 NativeScript 中,模块是通过 HTTP 获取/更新并重新执行的),重新评估一个模块会重新运行其顶层代码。

如果采用"常规"写法:

js 复制代码
export const tabCustomizer = new TabCustomizer();

那么每次 HMR 更新都会创建一个新实例。然而 NativeScript 应用中会有大量地方持有旧实例的引用:

  • 事件处理器
  • 你之前注册的回调(JS 端或桥接到原生)
  • 定时器、监听器、loaded 处理器、onHmrUpdate 处理器等,它们捕获了旧实例的原生对象,间接地让 JS 对象保持存活

这些引用不会自动重定向到新创建的实例。因此你会遇到常见的 HMR 问题:"为什么我的更新方法没运行?"、"为什么它被附加了两次?"、"为什么状态不一致?"等等。将实例放在global上可以让实例在模块替换后继续存在,这样现有的运行时引用就会继续指向同一个对象。

为什么交换原型(swapping the prototype)有效?

当更新代码时,类TabCustomizer { ... }的定义会被替换,这会创建一个新的TabCustomizer.prototype对象,其中包含新的方法实现。更新前创建的现有实例,其内部仍然指向旧的原型,因此方法查找仍会继续找到旧的方法。这行代码可以修复这个问题:

javascript 复制代码
Object.setPrototypeOf(existing, TabCustomizer.prototype);

因为 JavaScript 的方法分派机制如下:

  1. 先在对象本身上查找 existing.resetAccessory
  2. 如果找不到,则沿着 existing. 向上查找,并使用找到的函数。

通过更改[[Prototype]]旧实例能够"看到"新方法,同时又不改变实例的身份或其当前状态(如 tabView、标志位、定时器等)。

nstudio.io/blog/using-...

相关推荐
前端Hardy1 小时前
GitHub 爆火!Three.js + React + ECharts 打造最强数据大屏
前端·javascript
如果超人不会飞1 小时前
TinyRobot AI 对话组件库全组件使用指南
前端·vue.js
lichenyang4531 小时前
ArkTS 资源与暗色模式:为什么我手机切暗色,App 内容区却不变
前端
老王以为1 小时前
Claude Code 的产品哲学:当价值观成为架构
前端·claude·vibecoding
程序员黑豆2 小时前
AI全栈开发 - Java:变量
java·前端·ai编程
tedcloud1232 小时前
HyperFrames部署教程:用HTML生成MP4视频
前端·数据库·人工智能·html·音视频
江米小枣tonylua2 小时前
真多线程!Bun作者要给JS大手术
前端
YIAN2 小时前
# 从入门到封装:一文搞懂 Fetch API 所有用法(新手友好)
前端·javascript
Slice_cy2 小时前
基于node实现服务端内核引擎
前端·后端