配置
配置 Webpack
NativeScript 应用程序使用 webpack 打包,使您可以灵活地根据需要配置它。
注意
此部分仅适用于 @nativescript/webpack
版本 5.0.0
及以上版本。如果您使用的是旧版本,请考虑升级。
所有 NativeScript 应用程序都使用 webpack 打包。为了管理所需的配置,我们维护了 @nativescript/webpack
包。
所有新项目都带有预配置的用于构建 NativeScript 应用程序的基本 webpack.config.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
),这将向配置添加一个新的别名,对于所有其他文件,它将添加一个新的复制规则。
示例
--env.replace=./src/environments/environment.ts:./src/environments/environment.prod.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>
- 用于指定要使用的平台。将来可以是android
或ios
,或自定义平台。--env.hmr
- 启用 HMR 构建时为true
全局“魔法”变量
我们定义了一些有用的全局可用变量,您可以使用它们来更改应用程序中的逻辑。
__DEV__
- webpack 在开发模式下构建时为true
tsif (__DEV__) { // we are running a dev build }
global.isAndroid
,(也可作为__ANDROID__
) - 平台为 Android 时为true
tsif (global.isAndroid) { // we are running on android }
global.isIOS
,(也可作为__IOS__
) - 平台为 iOS 时为true
tsif (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_ENDPOINT
和 MY_API_SECRET
在应用程序代码库中可用。
# example .env file
MY_API_ENDPOINT=https://staging-api-host/api/v2
MY_API_SECRET=supersecrettoken
# example .env.prod file
MY_API_ENDPOINT=https://production-api-host/api/v2
MY_API_SECRET=verysuperverysecretverytoken
运行应用程序将允许您通过 p
访问这些变量。
注意
请注意,DotEnv 的工作方式是它在内部使用 webpack 的 DefinePlugin 来定义 p
值,这意味着它们本质上是在打包的代码中静态替换的。这一点很重要,因为无法对 process
或 p
进行解构或循环遍历。
有关限制的详细信息,请参阅 DotEnv 文档
// 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
// 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
// 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 的文档中找到更多示例,并阅读 基本配置的源代码 以了解我们如何实现它们。
添加复制规则
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 文档
添加插件
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 文档转换而来,该文档声明如下
new webpack.BannerPlugin({
banner: 'hello world',
})
添加解析器插件
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()
}
添加加载器
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()
}
添加外部依赖
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
别名。
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 选项
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()
}
更改现有规则
要更改现有规则,首先了解其设置方式很有用
ns prepare android|ios --env.verbose
# Note: we plan to add a separate command to just print the internal config
将打印已解析的内部配置,并在每个规则上方添加有用的注释,您可以抓取并使用这些注释。例如
// ...
/* config.module.rule('js') */
{
test: /\.js$/,
exclude: [
/node_modules/
],
use: [
/* config.module.rule('js').use('babel-loader') */
{
loader: 'babel-loader',
options: {
generatorOpts: {
compact: false
}
}
}
]
},
// ...
要添加新的加载器,我们可以使用上面添加新加载器时使用的相同语法
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()
}
修改现有加载器选项
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
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
列为依赖项,因此我们必须指定要使用的基础配置。
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()
}
抑制警告
如果您的构建产生了您想要隐藏的警告,您可以使用以下方法。
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
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
在解析最终配置时将包含这些配置。
例如,插件可以注册它需要的新的加载器
/**
* 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
webpack.init(env: IWebpackEnv): void
必需:初始化在整个配置构建过程中使用的内部 env
对象。
传递的 env 应该是一个包含键值对的对象。这通常由 webpack 处理。
useConfig
webpack.useConfig(config: string | false): void
可选:指定基础配置 - 默认情况下为自动发现。
传递 false
将选择退出使用基础配置,但是通常不建议这样做。
chainWebpack
webpack.chainWebpack(chainFn, options?): void
可选:向内部链添加一个新的 chainFn
,该链将在解析最终配置时被调用。
chainFn
应该是一个接受两个参数的函数——config
和 env
。
options
是一个可选的对象,包含以下可选属性
order: number
:控制应用chainFn
的顺序。当相关插件依赖于以正确的顺序进行的更改时很有用。例如,
plugin1
可以指定order: 1
,而plugin2
可以指定order: 2
- 这将保证首先调用plugin1
的chainFn
,并且plugin2
可以依赖于plugin1
设置的值。
示例:强制生产模式
webpack.chainWebpack((config, env) => {
config.mode('production')
})
示例:最后运行配置
设置 order: 10
并不能保证 chainFn
最终会被应用,因为对 chainWebpack
的其他调用可能会指定更高的数字。我们建议不要设置更高的值,并使用 10
作为常规的“最后”。
webpack.chainWebpack(
(config, env) => {
config.set('somethingThatShouldBeSetLast', true)
},
{ order: 10 }
)
mergeWebpack
webpack.mergeWebpack(mergeFnOrObject): void
可选:将对象(或函数返回的对象)合并到解析的链配置中。
示例
// 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
webpack.resolveChainableConfig(): ChainableConfig // Config from webpack-chain
解析已应用所有链函数的内部链配置的新实例。
resolveConfig
webpack.resolveConfig(): Config // Config from webpack
解析已应用所有链函数和合并的“最终”配置。
这将返回 webpack 可以处理的配置。