在babel构建阶段传递环境变量以导入不同的文件

时间:2018-09-28 22:46:38

标签: react-native webpack babel

我正在为项目构建网络(reactwebpack)和移动应用(babelreact-native)。因此,我为业务逻辑和redux / api库创建了一个公共库。

网络和移动设备之间的某些代码会稍有不同。就我而言,它是localStorage vs AsyncStorage,我将其用于身份验证...

我正在尝试为构建阶段传递环境变量,以切换某些文件的导入,以便为每个构建加载正确的文件,这些文件只是路径链接(即没有预构建)我图书馆的expo)例如:

import '../mylib'

尝试1

if(PLATFORM === 'mobile'){ import StorageModule from './mobile-storage-module` } else { import StorageModule from './mobile-storage-module` } export default StorageModule 说说它是移动设备还是网络设备,以便它根据不同的构建导入不同的库,如下所示:

我的@babel/preset-env拥有:

.babelrc

然后在本地存储文件中执行以下操作:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "platform": "mobile"
      }
    ]
  ]
}

那没用,这对我也没用。

尝试2

我安装了react-native-dotenv并使用以下内容创建了export default () => { const platform = process.env.platform if (platform === 'mobile') { return import './storage-modules/storage-mobile' } return import './storage-modules/storage-web' } 文件: .env 并在我的PLATFORM=mobile中设置插件:

.babelrc

在示例文件中,我尝试了此操作:

{
  "presets": [
    "babel-preset-expo",
    "react-native-dotenv"
  ]
}

但是现在我的构建无法正常工作。知道我如何在适用于react-native应用程序和Webpack构建(也使用babel)的babel的构建过程中进行动态导入吗?

3 个答案:

答案 0 :(得分:2)

首先,@babel/preset-env并没有您认为的那样。这不是为了指定您自己的变量,而是一个插件,可以针对您要支持的浏览器自动使用正确的目标和填空信息。

获取环境变量的最简单方法是使用webpack define插件(它是webpack的一部分,因此无需安装任何额外的东西)

只需将其添加到您的webpack配置中即可。

plugins: [
    new webpack.DefinePlugin({
        'process.env': {
            platform: 'mobile',
        },
    }),
],

接下来,您不能在ifs内使用普通的import语句。 import在运行任何代码之前都已解决,无论是通过webpack生成,还是在脚本加载受支持的环境中。 要在运行时导入某些内容,您需要使用动态导入。

这是一个看起来像这样的例子。

export default new Promise(async resolve => {
    resolve(
        process.env.platform === 'mobile'
            ? (await import('./mobile.js')).default
            : (await import('./desktop.js')).default
    );
});

您现在可以像往常一样从该文件导入,但是请注意,默认导出是一个承诺。

答案 1 :(得分:1)

问题标题为“ 在babel构建阶段”,我假设您想为台式机和移动设备创建不同的版本(而不是为两者构建一个版本,而是在运行时动态加载所需的模块)。所以我会这样:

package.json中为台式机和移动设备定义运行脚本:

"scripts": {
  "devmobile": "cross-env NODE_ENV=development PLATFORM=mobile webpack --progress",
  "dev": "cross-env NODE_ENV=development webpack --progress",
}

...,或者您可以为台式机和移动设备版本创建两个不同的webpack.config.js文件,但我认为上述操作更容易...

然后npm run devmobile构建用于移动设备,npm run dev构建用于桌面。

由于我在Windows上,因此我使用cross-env package,但这是建议的独立于操作系统的方法。

然后我将使用Webpack的NormalModuleReplacementPlugin
(基于this exmaple

在您的webpack.config.js中:

// defining the wanted platform for the build (comfing form the npm run script)
const targetPlatform = process.env.PLATFORM || 'desktop';

// then use the plugin like this
plugins: [
  new webpack.NormalModuleReplacementPlugin(/(.*)-PLATFORM(\.*)/, function(resource) {
    resource.request = resource.request.replace(/-PLATFORM/, `-${targetPlatform}`);
  }),
]

...那么,如果您具有这两个文件:

./storage-modules/storage-mobile.js
./storage-modules/storage-desktop.js

像这样在脚本中导入所需的内容:

import './storage-modules/storage-PLATFORM';

这样,生成的构建将仅包含用于构建过程的当前PLATFORM所需的文件。

另一种可能的解决方案可能是ifdef-loader,但我尚未对其进行测试。也许值得尝试,似乎很简单。


如果您希望构建一个版本并动态导入所需的模块,则可以在app.js(或其他任何方法)中执行以下操作:

// this needs to have defined when the app is running
const targetPlatform = process.env.PLATFORM || 'desktop';

import(
    /* webpackChunkName: "[request]" */
    `./storage-modules/storage-${targetPlatform}`
).then(storageModule => {
    // use the loaded module
});

或:

(async () => {
    const storageModule = await import(
        /* webpackChunkName: "[request]" */
        `./storage-modules/storage-${targetPlatform}`
    );
    // use the loaded module
})();

要使其正常运作,Babel必须为configured
有关具有动态导入here的Webpack的更多信息。

答案 2 :(得分:0)

您可以使用transform-inline-environment-variables来将平台传递给babel

"build-mobile": "PLATFORM=mobile ...",
"build-app": "PLATFORM=app ...",