8.7 发布—WinterCG 兼容性第一部分
了解更多

注意

此部分仅适用于 @nativescript/webpack 版本 5.0.0 及以上版本。如果您使用的是旧版本,请考虑升级。

所有 NativeScript 应用程序都使用 webpack 打包。为了管理所需的配置,我们维护了 @nativescript/webpack 包。

所有新项目都带有预配置的用于构建 NativeScript 应用程序的基本 webpack.config.js

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  // Learn how to customize:
  // https://docs.nativescript.cn/webpack

  return webpack.resolveConfig()
}

上述配置配置了打包 NativeScript 应用程序所需的大多数内容。在内部,它使用 webpack-chain 生成最终传递给 webpack 的配置。

在某些情况下,您可能希望扩展配置,这可以通过应用程序的 API 和插件的 插件 API 来实现。此页面包含许多您可能希望在 配置示例部分 中更改的常见事物的示例 - 对于此处未提及的任何其他内容,请参阅 webpack-chain 文档

CLI 标志

运行 NativeScript 应用程序时,以下标志会影响最终的 webpack 配置

--no-hmr

禁用 HMR(默认情况下启用)

--env.verbose

打印详细日志并在构建前打印内部配置

--env.replace=from:to

添加文件替换规则。对于源文件(.js.ts),这将向配置添加一个新的别名,对于所有其他文件,它将添加一个新的复制规则。

示例

cli
--env.replace=./src/environments/environment.ts:./src/environments/environment.prod.ts

将添加一个别名,因此以下代码

ts
import { environment } from './environments/environment.ts'

将导入路径解析为 ./environments/environment.prod.ts

--env.appComponents

允许为 Android 传递其他应用程序组件。

例如,如果您在 myCustomActivity.ts 中定义了一个自定义活动,您可以传递 --env.appComponents=myCustomActivity.ts 以将其包含在构建中。

--env.production

启用生产模式。这将使用 Terser 压缩包。

--env.report

使用 BundleAnalyzerPlugin 生成报告

--env.profile

生成一个 webpack.stats.json 以在 https://webpack.github.io/analyse/ 上进行分析

--env.watchNodeModules

启用监视 node_modules 中的更改。在调试插件并在 node_modules 中直接进行更改时很有用。

--env.e2e

启用 E2E(端到端)模式 - 这目前在 @nativescript/core 中启用了 testID 属性。

其他标志

通常由 CLI 自动传递的其他 env 标志

  • --env.appPath - 应用程序源代码的路径(与 nativescript.config.ts 中的 appPath 相同)
  • --env.appResourcesPath - App_Resources 的路径(与 nativescript.config.ts 中的 appResourcesPath 相同)
  • --env.nativescriptLibPath - 当前正在运行的 CLI 的库的路径。
  • --env.android - 在 Android 上运行时为 true
  • --env.ios - 在 iOS 上运行时为 true
  • --env.platform=<platform> - 用于指定要使用的平台。将来可以是 androidios,或自定义平台。
  • --env.hmr - 启用 HMR 构建时为 true

全局“魔法”变量

我们定义了一些有用的全局可用变量,您可以使用它们来更改应用程序中的逻辑。

  • __DEV__ - webpack 在开发模式下构建时为 true
    ts
    if (__DEV__) {
      // we are running a dev build
    }
  • global.isAndroid,(也可作为 __ANDROID__) - 平台为 Android 时为 true
    ts
    if (global.isAndroid) {
      // we are running on android
    }
  • global.isIOS,(也可作为 __IOS__) - 平台为 iOS 时为 true
    ts
    if (global.isIOS) {
      // we are running on iOS
    }
以下变量也已定义,但主要用于 NativeScript Core 内部或希望使用这些变量的插件。
  • __NS_WEBPACK__ - 使用 webpack 构建时始终为 true
  • __NS_ENV_VERBOSE__ - 设置 --env.verbose 时为 true
  • __NS_DEV_HOST_IPS__ - 在 development 模式下主机计算机(运行构建的计算机)的 IP 地址数组,在生产模式下为空数组。
  • __CSS_PARSER__ - NativeScript Core 使用的 CSS 解析器。该值根据 nativescript.config.ts 中的 cssParser 值设置,默认为 css-tree
  • __UI_USE_XML_PARSER__ - NativeScript Core 使用的标志,在不使用 XML 解析器时禁用它
  • __UI_USE_EXTERNAL_RENDERER__ - NativeScript Core 使用的标志,在使用外部渲染器时禁用注册全局模块。

使用 DotEnv 文件

DotEnv 预先配置为允许定义在运行时可用的环境变量。您可以在项目根目录中创建一个 .env 文件,并定义在运行时可用于应用程序的值。

如果您需要多个环境,您可以使用 .env.<name> 的命名约定创建其他 env 文件(例如 .env.prod.env.staging)。

加载环境文件时使用以下逻辑

  • 如果存在,则默认加载 .env
  • --env.env=<name> 传递给构建/运行命令并且 .env.<name> 存在时,加载 .env.<name>,否则回退到加载 .env(如果存在)

作为一般做法,.env 文件不应提交到您的源代码存储库,因为它们可能包含敏感值。通常会提交一个 .env.example,其中包含所有变量名称,省略了值,然后每个开发人员都会将其复制到 .env.env.<name> 文件中并填写值。

DotEnv 文件示例

项目根目录中的以下文件将使 MY_API_ENDPOINTMY_API_SECRET 在应用程序代码库中可用。

bash
# example .env file
MY_API_ENDPOINT=https://staging-api-host/api/v2
MY_API_SECRET=supersecrettoken
bash
# example .env.prod file
MY_API_ENDPOINT=https://production-api-host/api/v2
MY_API_SECRET=verysuperverysecretverytoken

运行应用程序将允许您通过 process.env.<VARIABLE_NAME> 访问这些变量。

注意

请注意,DotEnv 的工作方式是它在内部使用 webpack 的 DefinePlugin 来定义 process.env.<VARIABLE_NAME> 值,这意味着它们本质上是在打包的代码中静态替换的。这一点很重要,因为无法对 processprocess.env 进行解构或循环遍历。

有关限制的详细信息,请参阅 DotEnv 文档

ts
// default, no flags - loaded from .env by default

console.log(process.env.MY_API_ENDPOINT) // https://staging-api-host/api/v2
console.log(process.env.MY_API_SECRET) // supersecrettoken
ts
// With --env.env=prod: loaded from .env.prod

console.log(process.env.MY_API_ENDPOINT) // https://production-api-host/api/v2
console.log(process.env.MY_API_SECRET) // verysuperverysecretverytoken
ts
// With --env.env=nonexistent: falls back to .env

console.log(process.env.MY_API_ENDPOINT) // https://staging-api-host/api/v2
console.log(process.env.MY_API_SECRET) // supersecrettoken

配置示例

以下是一些您可能希望在 webpack.config.js 中执行的常见示例。

请注意,配置是使用 webpack-chain 构建的,因此下面所有示例中的 config 变量都是可链接配置的实例。您也可以在 webpack-chain 的文档中找到更多示例,并阅读 基本配置的源代码 以了解我们如何实现它们。

添加复制规则

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  // Example: copy all markdown files to the build directory
  webpack.Utils.addCopyRule('**/*.md')

  // Example: copy all files from a dependency
  webpack.Utils.addCopyRule({
    from: '@nativescript/webpack/stubs',
    to: 'custom/location',
    // the context of the "from" rule, in this case node_modules
    // we used the getProjectFilePath util here, but this could have been
    // a path.resolve(__dirname, 'node_modules') too.
    context: webpack.Utils.project.getProjectFilePath('node_modules'),
  })

  return webpack.resolveConfig()
}

对于您可以传递的所有有效选项,请参阅 CopyWebpackPlugin 文档

添加插件

js
const webpack = require('@nativescript/webpack')

// import the plugin first
const { BannerPlugin } = require('webpack')

module.exports = (env) => {
  webpack.init(env)

  // first we add our callback to the internal chain
  webpack.chainWebpack((config) => {
    // we add the plugin
    config.plugin('BannerPlugin').use(BannerPlugin, [
      {
        banner: 'hello world',
      },
    ])
  })

  return webpack.resolveConfig()
}

.use 调用的第二个参数是要传递给插件的参数数组。例如,上述示例是从官方 BannerPlugin 文档转换而来,该文档声明如下

js
new webpack.BannerPlugin({
  banner: 'hello world',
})

添加解析器插件

js
const webpack = require('@nativescript/webpack')
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    config.resolve.plugin('TsconfigPathsPlugin').use(TsconfigPathsPlugin)
  })

  return webpack.resolveConfig()
}

添加加载器

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    // add a new rule for *.something files
    config.module
      .rule('something')
      .test(/\.something$/)
      .use('something-loader')
      .loader('something-loader')
      .options({
        example: true,
      })
  })

  return webpack.resolveConfig()
}

添加外部依赖

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    config.externals(
      // make sure to keep pre-defined externals
      config.get('externals').concat([
        // add your own externals
        'some-external-dependency',
      ])
    )
  })

  return webpack.resolveConfig()
}

添加路径别名

您可以为特定的源目录定义 import 别名。

js
const webpack = require('@nativescript/webpack')
const { resolve } = require('path')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    // change the "@" alias to "app/libs"
    config.resolve.alias.set('@', resolve(__dirname, 'app/libs'))
  })

  return webpack.resolveConfig()
}

扩展 DefinePlugin 选项

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    config.plugin('DefinePlugin').tap((args) => {
      Object.assign(args[0], {
        'global.isProduction': !!env.production,
        'global.someNumber': 42,
        'global.someString': JSON.stringify('some string value'),
      })

      return args
    })
  })

  return webpack.resolveConfig()
}

更改现有规则

要更改现有规则,首先了解其设置方式很有用

cli
ns prepare android|ios --env.verbose
# Note: we plan to add a separate command to just print the internal config

将打印已解析的内部配置,并在每个规则上方添加有用的注释,您可以抓取并使用这些注释。例如

js
// ...
/* config.module.rule('js') */
{
  test: /\.js$/,
  exclude: [
    /node_modules/
  ],
  use: [
    /* config.module.rule('js').use('babel-loader') */
    {
      loader: 'babel-loader',
      options: {
        generatorOpts: {
          compact: false
        }
      }
    }
  ]
},
// ...

要添加新的加载器,我们可以使用上面添加新加载器时使用的相同语法

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    config.module
      .rule('js')
      .use('something-loader')
      .loader('something-loader')
      .options({
        example: true,
      })
  })

  return webpack.resolveConfig()
}

修改现有加载器选项

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    config.module
      .rule('scss')
      .use('sass-loader')
      .options({ sassOptions: { indentedSyntax: true } })
  })

  return webpack.resolveConfig()
}

修改现有插件配置

让我们修改上面添加的 BannerPlugin

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    config.plugin('BannerPlugin').tap((args) => {
      // args is and Array of all the arguments passed to the BannerPlugin constructor

      // args[0] is the first argument, which we set above.
      // be careful when accessing an array index
      // and do proper checks before writing to
      // avoid errors
      args[0].banner = 'changed banner.'

      // should always return all the arguments that should be passed to the plugin constructor
      // in some cases you may want to remove an argument - you can do that by returning an array
      // with that argument removed from it.
      return args
    })
  })

  return webpack.resolveConfig()
}

显式设置基础配置

在某些情况下,您可能希望显式设置要使用的基础配置。

例如,在 NativeScript-Vue 存储库中,sample 应用未将 nativescript-vue 列为依赖项,因此我们必须指定要使用的基础配置。

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  // set the base config
  // can be false to opt out from using a base config (used mostly in tests)
  // or can be one of the base configs: base, angular, javascript, react, svelte, typescript, vue
  webpack.useConfig('vue')

  return webpack.resolveConfig()
}

抑制警告

如果您的构建产生了您想要隐藏的警告,您可以使用以下方法。

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  webpack.chainWebpack((config) => {
    config.set(
      'ignoreWarnings',
      (config.get('ignoreWarnings') || []).concat([
        /a regex that matches the warning to suppress/,
      ])
    )
  })

  return webpack.resolveConfig()
}

将选项合并到配置中

对于简单的事情,您可以将对象合并到最终配置中,而不是使用 chainWebpack

js
const webpack = require('@nativescript/webpack')

module.exports = (env) => {
  webpack.init(env)

  // merge a simple object
  webpack.mergeWebpack({ mode: 'production' })

  // using a function
  webpack.mergeWebpack((env) => {
    // return the object to be merged
    return {
      mode: 'production',
    }
  })

  return webpack.resolveConfig()
}

插件 API

NativeScript 插件可以在其根文件夹(与 package.json 相邻)中提供一个 nativescript.webpack.js 文件,并且 @nativescript/webpack 在解析最终配置时将包含这些配置。

例如,插件可以注册它需要的新的加载器

js
/**
 * This optionally provides typehints
 * this requires "@nativescript/webpack" to be a dependency (dev)
 *
 * @param {typeof import("@nativescript/webpack")} webpack
 */
module.exports = (webpack) => {
  // same API as the user configs
  // for example make changes to the internal config with webpack-chain
  webpack.chainWebpack(
    (config, env) => {
      // as an example - add a new rule for svg files
      config.module
        .rule('something')
        .test(/\.something$/)
        .use('something-loader')
        .loader('something-loader')
    } /*, options */
  )
}

API

init

ts
webpack.init(env: IWebpackEnv): void

必需:初始化在整个配置构建过程中使用的内部 env 对象。

传递的 env 应该是一个包含键值对的对象。这通常由 webpack 处理。


useConfig

ts
webpack.useConfig(config: string | false): void

可选:指定基础配置 - 默认情况下为自动发现。

传递 false 将选择退出使用基础配置,但是通常不建议这样做。


chainWebpack

ts
webpack.chainWebpack(chainFn, options?): void

可选:向内部链添加一个新的 chainFn,该链将在解析最终配置时被调用。

chainFn 应该是一个接受两个参数的函数——configenv

options 是一个可选的对象,包含以下可选属性

  • order: number:控制应用 chainFn 的顺序。

    当相关插件依赖于以正确的顺序进行的更改时很有用。例如,plugin1 可以指定 order: 1,而 plugin2 可以指定 order: 2 - 这将保证首先调用 plugin1chainFn,并且 plugin2 可以依赖于 plugin1 设置的值。

示例:强制生产模式

js
webpack.chainWebpack((config, env) => {
  config.mode('production')
})

示例:最后运行配置

设置 order: 10 并不能保证 chainFn 最终会被应用,因为对 chainWebpack 的其他调用可能会指定更高的数字。我们建议不要设置更高的值,并使用 10 作为常规的“最后”。

js
webpack.chainWebpack(
  (config, env) => {
    config.set('somethingThatShouldBeSetLast', true)
  },
  { order: 10 }
)

mergeWebpack

ts
webpack.mergeWebpack(mergeFnOrObject): void

可选:将对象(或函数返回的对象)合并到解析的链配置中。

示例

js
// merge an object into the internal config
webpack.mergeWebpack({
  something: true,
})
// or pass a function that returns an object
webpack.mergeWebpack((env) => {
  return {
    something: true,
  }
})

resolveChainableConfig

ts
webpack.resolveChainableConfig(): ChainableConfig // Config from webpack-chain

解析已应用所有链函数的内部链配置的新实例。


resolveConfig

ts
webpack.resolveConfig(): Config // Config from webpack

解析已应用所有链函数和合并的“最终”配置。

这将返回 webpack 可以处理的配置。

下一页
动画