在 Next.js 中运行 react-native-reanimated:next-example 开发、构建与 Bundle 分析实战指南
【免费下载链接】react-native-reanimatedReact Native's Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimated
导读
react-native-reanimated 本身是面向 React Native 的动画库,但在本仓库中,它通过react-native-web与 Next.js 深度集成,构成了一个可运行在浏览器中的 Web 动画示例应用。本文以 apps/next-example/README.md 为核心,完整讲解该示例应用的开发启动、生产构建、Bundle 体积分析与关闭压缩构建等全部命令与实战用法,并结合仓库内的next.config.js、babel.config.js、页面源码与 Cypress 端到端测试,剖析每条命令背后的执行机制与适用场景,帮助你在自己的 Next.js + React Native Web 项目中复现同样的一整套工作流。
一、示例应用概览:Next.js 中的 Reanimated 是如何跑起来的
next-example是仓库apps/目录下的一个独立 Next.js 应用(应用清单见 apps/next-example/package.json),它演示了 Reanimated 在 Web 平台上的能力边界。其技术栈非常明确:
next16.2.12(Next.js 框架本体);react-native0.87.0 与react-native-web0.21.2(用 react-native-web 把 React Native 组件渲染到 DOM);react-native-reanimated(workspace:*,直接引用仓库内本包源码)与react-native-worklets(workspace:*,Reanimated 底层依赖的工作线程库);@expo/next-adapter6.0.0 与webpack5.105.4(负责将 Expo/React Native 生态接入 Next.js 打包链路);cypress15.4.0(浏览器端端到端测试)。
应用内页面全部用react-native的组件书写,再渲染为网页:入口页 pages/index.js 提供到 SSG、SSR 与 e2e 测试页的导航;pages/ssg.js 演示构建期静态生成页面内的withRepeat(withTiming(...))循环动画;pages/ssr.js 通过getServerSideProps在服务端随机生成目标宽度并驱动动画;pages/test.js 则是一个专为 Cypress e2e 准备的动画测试页。可以说,这是一个专门用于验证"Reanimated 在 Next.js 各渲染模式下是否正常工作"的实验场。
二、开发模式:一条命令启动热更新开发服务器
按 README 的说明,开发模式只需在apps/next-example目录下执行:
yarn dev这条命令在 package.json 中映射为:
"dev": "next"即直接调用 Next.js 的开发服务器,默认监听http://localhost:3000,支持热模块替换(HMR)。对于 Reanimated 的 Web 调试,这一模式的核心价值在于:
- 即时反馈动画改动:修改 pages/test.js 这类页面中的
withTiming(300, { duration: 1000 })等动画参数,浏览器会立即刷新,便于快速调参; - 开发期构建未压缩:
next开发服务器不做生产级压缩,报错堆栈可读性强,方便定位 worklet 序列化或样式注入问题。
需要说明的是,Reanimated 的 worklet 代码必须经过 Babel 插件转换才能在 Web 上运行,该转换由 babel.config.js 中的react-native-worklets/plugin完成(见下文"配置深挖"小节),因此请确保该文件未被改动或移除。
三、生产构建与启动:验证压缩与优化的真实产物
README 给出了生产环境的两步走:
yarn build yarn start值得注意的是,这里的yarn build在 package.json 中是一个空脚本("build": ""),真正的构建命令是:
"build:next": "next build"也就是说,如果你需要完整执行一次生产构建,实际应运行:
yarn build:next yarn start其中yarn start映射为next start,用于以生产模式启动已构建好的产物(默认端口仍是3000)。之所以区分build与build:next,是因为 README 中的其他两条构建命令(build:analyze、build:disable-minification)都是基于next build加环境变量实现的,仓库把基础构建单独命名为build:next以保持脚本语义清晰。
生产构建会执行压缩、Tree Shaking 与静态优化,ssg.js页面会在构建期被预渲染为静态 HTML,ssr.js页面则在每次请求时由服务端渲染。执行完yarn build:next && yarn start后,可以打开http://localhost:3000,观察 SSG/SSR 页面中PinwheelIn进场动画与withRepeat循环动画在生产模式下是否表现正常——这是验证压缩后 worklet 代码仍能正确序列化与执行的最直接手段。
四、Bundle 体积分析:用 Next Bundle Analyzer 定位体积瓶颈
yarn build:analyze该命令对应 package.json 中的:
"build:analyze": "ANALYZE_BUNDLE=1 next build"它的工作机理在 next.config.js 中清晰可见:
const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE_BUNDLE === '1', }); module.exports = withPlugins([withBundleAnalyzer, withExpo], { ... });即通过环境变量ANALYZE_BUNDLE=1触发@next/bundle-analyzer(依赖版本见 package.json 中的@next/bundle-analyzer: 15.5.4)在构建完成后生成各 chunk 的可视化报告,并自动在浏览器中打开。报告会按模块展示每个包(尤其是react-native-reanimated、react-native-worklets、react-native-web、expo等)的体积占比。
实战建议:分析前先运行一次yarn build:next生成基线产物,再运行yarn build:analyze对比差异。若发现 Reanimated 相关包体积异常,可优先检查 next.config.js 中的transpilePackages配置——该数组列出了需要被转译的包(react-native-reanimated、react-native-worklets、react-native、expo),这决定了哪些依赖会以源码形式进入 Webpack 打包链路,直接影响最终 Bundle 构成。
五、关闭压缩构建:手动审查产物内容
yarn build:disable-minification对应脚本:
"build:disable-minification": "DISABLE_MINIFICATION=1 next build"其实现位于 next.config.js:
const disableMinification = process.env.DISABLE_MINIFICATION === '1'; webpack(config) { if (disableMinification) { config.optimization.minimizer = []; } ... }当DISABLE_MINIFICATION=1时,构建会清空 Webpack 的optimization.minimizer,即跳过代码压缩与混淆,产出的 JS 文件保留原始变量名与注释。README 明确指出该模式的用途是"手动审查创建的 bundle"(原文:This is useful when you want to manually review the created bundle)。
典型场景包括:
- 排查压缩混淆后 worklet 函数名丢失导致的运行时异常,用未压缩产物对照报错堆栈;
- 审查
react-native-reanimated等包的代码是否被正确打包进 chunk; - 检查 Tree Shaking 是否生效,确认未使用的 Reanimated 模块是否被剔除。
注意:该命令只影响构建产物,不改动应用源码,审查完毕后应重新执行yarn build:next生成正式的生产构建。
六、配置深挖:理解命令背后的四条关键配置
6.1 next.config.js:Expo 适配、转译与别名
apps/next-example/next.config.js 是整个应用能否把 Reanimated 跑起来的关键:
const { withExpo } = require('@expo/next-adapter'); const withPlugins = require('next-compose-plugins'); module.exports = withPlugins([withBundleAnalyzer, withExpo], { typescript: { ignoreBuildErrors: true }, transpilePackages: [ 'react-native-reanimated', 'react-native-worklets', 'react-native', 'expo', ], turbopack: { resolveExtensions: [ '.web.tsx', '.web.ts', '.web.jsx', '.web.js', '.tsx', '.ts', '.jsx', '.js', '.mjs', '.cjs', '.json', ], }, webpack(config) { if (disableMinification) config.optimization.minimizer = []; config.resolve.alias.react = require('path').resolve(__dirname, '..', '..', 'node_modules', 'react'); return config; }, });要点解读:
withExpo(来自@expo/next-adapter)负责打通 Expo/React Native 生态与 Next.js 的 Webpack 配置;transpilePackages必须包含react-native-reanimated与react-native-worklets,否则这两个包含 worklet 转换代码的包不会被 Babel 处理,动画在 Web 上无法运行;turbopack.resolveExtensions将.web.tsx/.web.js等平台后缀置于解析优先级前列,保证解析到 Web 专用实现(Reanimated 源码中大量使用.web.ts等平台文件,可参考 packages/react-native-reanimated/src 下的平台特定实现);webpack回调把react显式 alias 到仓库根node_modules下的 React,避免因 monorepo 中多个 React 副本导致 hooks 状态错乱。
6.2 babel.config.js:worklet 转换与平台别名
apps/next-example/babel.config.js 决定了 Reanimated worklet 能否在 Web 端工作:
const workletsPluginOptions = { strictGlobal: true }; module.exports = { presets: ['next/babel'], plugins: [ ['babel-plugin-transform-define', { __DEV__: process.env.NODE_ENV !== 'production' }], [ 'module-resolver', { extensions: ['.js', '.jsx', '.ts', '.tsx'], alias: { 'react-native': '../../node_modules/react-native-web' }, }, ], ['react-native-worklets/plugin', workletsPluginOptions], ], };react-native-worklets/plugin是 worklets 的 Babel 插件,负责把useAnimatedStyle、useSharedValue回调中的 worklet 函数序列化为可在 UI 线程执行的代码,并配合strictGlobal: true启用严格全局模式;module-resolver把react-native导入重写为react-native-web,这是 Web 渲染的前提;- 配置文件里还留有一行注释提示:如需让 Reanimated 支持 fast-refresh(快速刷新),可取消
react-native-reanimated指向../../packages/react-native-reanimated/src的别名注释。
6.3 _app.js 与 _document.js:Web 渲染的启动链路
pages/_document.js 遵循 react-native-web 的官方根元素设置:通过AppRegistry.registerComponent('main', () => Main)与AppRegistry.getApplication('main')收集 React Native 样式,并注入一段全局样式让#__next填满视口高度;pages/_app.js 则在客户端水合完成后设置globalThis.__NEXT_HYDRATED__ = true——这个标记被 Cypress 测试用作水合完成信号。
6.4 根级 expo 依赖说明
示例应用的dependencies中直接包含expo: 54.0.13,这是因为@expo/next-adapter需要 expo 运行时环境才能正确解析其配置;在自行搭建项目时,若未使用 expo 全家桶,可评估裁剪withExpo包装与对应依赖。
七、端到端验证:用 Cypress 检验动画结果
README 未直接提及测试命令,但 package.json 提供了两条 e2e 脚本,恰好是验证上述构建流程是否成功的闭环手段:
"e2e": "start-server-and-test \"next start\" http://localhost:3000 \"cypress open --e2e\"", "e2e:headless": "start-server-and-test \"next start\" http://localhost:3000 \"cypress run --e2e\""e2e:先启动生产服务器(next start),等待localhost:3000可访问后再打开 Cypress 交互式界面;e2e:headless:同样的启动流程,但以无头模式运行全部测试,适合 CI。
测试用例定义在 cypress/e2e/simpleAnimationTest.cy.ts:
describe('Simple animation Test', () => { const waitForHydration = () => cy.window().should('have.property', '__NEXT_HYDRATED__', true); it('Visits the test page and tests component width before and after animation', () => { cy.visit('http://localhost:3000'); waitForHydration(); cy.contains('e2e test page').click(); cy.url().should('include', '/test'); waitForHydration(); cy.get('[data-testid=box]').should('have.css', 'width', '10px'); cy.contains('Start animation').click(); cy.wait(1000); cy.get('[data-testid=box]').should('have.css', 'width', '300px'); }); });它对应的页面是 pages/test.js:useSharedValue(10)初始化为 10,点击按钮后执行withTiming(300, { duration: 1000 }),测试断言动画前后 box 宽度从10px变为300px——这完整验证了 worklet 在 Web 端从初始化、序列化到执行的全链路正确性。若在生产构建后运行yarn e2e:headless全部通过,即证明前三节所述构建流程是健康可用的。Cypress 的基础配置见 cypress.config.js。
八、命令速查与选用场景
| 命令 | 脚本定义 | 作用 | 适用场景 |
|---|---|---|---|
yarn dev | next | 启动开发服务器(HMR、未压缩) | 日常开发调试动画 |
yarn build:next | next build | 执行生产构建(压缩优化) | 生成上线产物 |
yarn build | (空脚本) | README 中的构建占位名,实际需用build:next | 按 README 操作时的注意点 |
yarn start | next start | 以生产模式运行已构建产物 | 验证/部署构建结果 |
yarn build:analyze | ANALYZE_BUNDLE=1 next build | 构建后生成 Bundle 可视化报告并自动打开 | 定位包体积瓶颈 |
yarn build:disable-minification | DISABLE_MINIFICATION=1 next build | 关闭压缩混淆的构建 | 手动审查 bundle 代码 |
yarn e2e:headless | start-server-and-test ... cypress run --e2e | 生产模式下无头运行动画 e2e 测试 | CI 回归验证 |
结语
next-example虽小,却是 Reanimated Web 能力的完整实验台:yarn dev支撑日常调试,yarn build:next+yarn start产出并运行生产版本,yarn build:analyze与yarn build:disable-minification分别解决"体积从哪来"与"产物里是什么"两个问题,而 Cypress 测试则为这一切提供了可量化的验收标准。理解每条命令在 next.config.js 与 babel.config.js 中的落点,你就能把这套工作流原样迁移到自己的 Next.js + react-native-web 项目中,让 Reanimated 动画在 Web 端稳定、可控、可度量地运行。
【免费下载链接】react-native-reanimatedReact Native's Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimated
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考