You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用babel-plugin-require-context-hook搭配Expo时出现Metro错误:__requireContext is not defined

使用babel-plugin-require-context-hook搭配Expo时出现Metro错误:__requireContext is not defined

我来帮你拆解这个问题,你遇到的情况其实是典型的「为测试环境做的配置,不小心影响了生产/开发环境的打包流程」,咱们一步步解决:

问题根源分析

你为了让Jest支持require.context(),在babel.config.js里全局启用了require-context-hook插件,同时在代码里加了条件注册逻辑。但这里有两个核心矛盾:

  1. Metro不认识插件注入的__requireContext:当Expo用Metro打包时,Babel会把所有require.context()调用转换成__requireContext,但Metro运行环境里没有这个函数的定义(你的注册代码只在test环境执行,Metro的NODE_ENV是development或production,所以注册逻辑没跑)。
  2. Metro本身不原生支持require.context():即使你去掉Babel插件,直接在Expo代码里用require.context(),Metro也会报错说这个方法不存在。

解决方案(按推荐优先级排序)

1. 拆分Babel配置,仅在测试环境启用插件

最稳妥的方式是让require-context-hook插件只对Jest生效,Metro打包时完全不碰它。修改你的babel.config.js:

module.exports = function (api) {
  api.cache(true);
  // 检测当前是否为测试环境
  const isTest = process.env.NODE_ENV === 'test';

  return {
    presets: ['babel-preset-expo'],
    plugins: [
      [
        "@babel/plugin-proposal-optional-chaining-assign",
        { "version": "2023-07" }
      ],
      // 仅在测试环境加载require-context-hook插件
      ...(isTest ? ["require-context-hook"] : [])
    ]
  };
};

然后保留你代码里的条件注册逻辑:

if (process.env.NODE_ENV === 'test') {
  require('babel-plugin-require-context-hook/register')();
}

这样Jest测试时会正常处理require.context(),而Metro打包时不会转换这个方法。但如果你的require.context()在Expo运行时也需要执行,Metro会报错require.context is not a function——这时候需要用Metro支持的方式替代。

2. 用Metro原生支持的import.meta.glob替代require.context()

Expo SDK 49+的Metro已经支持import.meta.glob(Vite风格的批量导入),这是官方推荐的动态批量导入方案,完全替代require.context()。

比如你之前用:

const components = require.context('./components', true, /\.jsx$/);

可以替换为兼容测试和Expo环境的代码:

let components;
if (process.env.NODE_ENV === 'test') {
  require('babel-plugin-require-context-hook/register')();
  components = require.context('./components', true, /\.jsx$/);
} else {
  // 非测试环境用Metro支持的批量导入
  const componentModules = import.meta.glob('./components/**/*.jsx', { eager: true });
  // 包装成和require.context()一致的API格式
  components = (path) => componentModules[`./components/${path}`].default;
  components.keys = () => Object.keys(componentModules).map(key => key.replace('./components/', ''));
}

这样不管是测试环境还是Expo环境,都能正常工作,无需修改Babel全局配置。

3. 给Metro环境注入__requireContext实现(不推荐)

如果你坚持要在Expo环境用require.context(),可以在Metro环境全局注册__requireContext函数。在你的Expo入口文件(比如App.js最顶部)添加:

if (process.env.NODE_ENV !== 'test') {
  // 模拟__requireContext的实现,适配你的实际需求
  global.__requireContext = (baseDir, recursive, regExp) => {
    const files = import.meta.glob(`${baseDir}/**/*`, { eager: true });
    const matchedKeys = Object.keys(files)
      .filter(key => regExp.test(key.replace(`${baseDir}/`, '')))
      .map(key => key.replace(`${baseDir}/`, ''));
    
    const context = (key) => files[`${baseDir}/${key}`].default;
    context.keys = () => matchedKeys;
    return context;
  };
}

这个方案需要你自己维护__requireContext的实现,后续如果require.context()的API变化,你还要同步修改,所以优先级最低。

验证步骤

  1. 运行npm test或yarn test,确认Jest测试仍然正常通过
  2. 运行expo start,等待Metro打包完成,确认__requireContext is not defined错误消失,Expo app能正常启动

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 10:07:59