返回博客首页
← 所有文章

如何更新 NativeScript 7 的插件

2020年8月31日 — 作者:技术指导委员会 (TSC)

作为 NativeScript 插件作者,在更新您的插件以适应 NativeScript 7 时,需要了解 6 个要点

  1. dependencies 更新
  2. es2017 及更高版本目标 (仅限基于 TypeScript 的插件)
  3. @NativeClass() 装饰器 (仅限基于 TypeScript 的插件)
  4. 删除所有 tns-core-modules 的出现,并将所有导入扁平化为仅来自 @nativescript/core
  5. 删除所有 tns-platform-declarations 的出现,并替换为 @nativescript/types
  6. 您的插件是否提供 Angular 特定的内容?需要调整源代码和构建。

我们将很快推出一个新的插件种子,它具有改进的架构(优于当前的插件种子)、构建工具和设置,以帮助您更轻松地维护所有插件。它还允许您轻松地将现有的插件导入并使用它(我们将在其可用后的两周内发布一篇博客文章)。

让我们分别解决这些问题。

1. 包依赖项更新

"devDependencies": {
	"@nativescript/core": "~7.0.0",
	"@nativescript/types": "~7.0.0",
  "@nativescript/webpack": "~3.0.0",
	"typescript": "~3.9.0",

2. es2017 目标

更新您的 tsconfig.json。这里突出显示了关键部分,因此仅显示最重要的部分。

之前

"compilerOptions": {
  "target": "es5",
  "module": "commonjs",

之后

"compilerOptions": {
  "target": "ES2017",
  "module": "esnext",
  "moduleResolution": "node",

3. @NativeClass() 装饰器

对于您使用 TypeScript 的 extends SomeNativeClass 语法扩展的任何原生类,您都希望进行装饰。例如

之前

iOS

export class MyPluginDelegateImpl extends NSObject {

Android

export class ListViewItemClickListenerImpl extends java.lang.Object

之后

iOS

@NativeClass()
export class MyPluginDelegateImpl extends NSObject {

Android

@NativeClass()
export class ListViewItemClickListenerImpl extends java.lang.Object {

NativeClass 装饰器是 @nativescript/core 全局的一部分,因此您无需从任何地方导入它,因为它只要您使用 @nativescript/core 就应该可用。

注意:根据您的使用情况,请参阅 此处的 NativeClass 最佳实践,其中概述了一些其他有用的说明。

确保 NativeClass 被处理

为了使您的插件能够正确处理 NativeClass 装饰器,您可以通过 ts-patch 包向 typescript 和 tsconfig.json 添加一个不错的扩展。这与来自最新 @nativescript/webpack3.0.0 及更高版本)的新转换器结合使用,这也是上述 devDependencies 中列出该包的原因。

  1. npm i ts-patch -D

  2. 修改 package.json 脚本以在清理插件后包含安装。

这是一个示例

"scripts": {
  "setup": "npm i && ts-patch install"
}
  1. 修改 tsconfig.json 以支持转换(在 compilerOptions 中的任何位置添加 plugins 部分)
"compilerOptions": {
  "plugins": [{
    "transform": "@nativescript/webpack/transformers/ns-transform-native-classes",
    "type": "raw"
  }]
}

现在,当您使用 tsc 构建插件时,任何使用 NativeClass 装饰的类都将被正确编译以供 NativeScript 运行时使用。

4. 删除所有 tns-core-modules 的出现,并将所有导入扁平化为仅来自 @nativescript/core

您现在可以使用 @nativescript/core 进行所有导入。一些符号已被封装到方便的汇总中,以避免在代码库中出现太多通用符号。您可以 使用此导入参考指南来合并用法

这是一个示例

之前

import * as app from 'tns-core-modules/application';
import { messageType, write } from 'tns-core-modules/trace';
import { ObservableArray } from 'tns-core-modules/data/observable-array';
import { GridLayout } from 'tns-core-modules/ui/layouts/grid-layout';
import { KeyedTemplate, View } from 'tns-core-modules/ui/core/view';
import { layout } from 'tns-core-modules/utils/utils';

// sample code
const rootView = app.getRootView();

write('Some message', 'a category');

layout.getDisplayDensity();

alert('Hi');

之后

import { 
  Application, 
  Trace,
  ObservableArray, 
  GridLayout, 
  KeyedTemplate, 
  View,
  Dialogs,
  Utils
} from '@nativescript/core';

// sample code
const rootView = Application.getRootView();

Trace.write('Some message', 'a category');

Utils.layout.getDisplayDensity();

// you can still use global alert just fine but to better identify {N} dialog usage you can now use the `Dialogs` rollup which contains all the dialog methods and interfaces
Dialogs.alert('Hi');

5. 删除所有 tns-platform-declarations 的出现,并替换为 @nativescript/types

在您的 references.d.ts

之前

/// <reference path="./node_modules/tns-platform-declarations/ios.d.ts" />
/// <reference path="./node_modules/tns-platform-declarations/android.d.ts" />

之后

/// <reference path="./node_modules/@nativescript/types/index.d.ts" />

如果需要,您还可以根据需要包含特定的平台类型;例如,假设您想要不同的 Android sdk 类型

/// <reference path="./node_modules/@nativescript/types-ios/index.d.ts" />
/// <reference path="./node_modules/@nativescript/types-android/lib/android-29.d.ts" />

6. Angular 处理

NativeScript 7 带来了官方的 Angular 10 支持。这一点很重要,因为 Angular 10 取消了官方的 es5 支持,并且还针对 es2017+。

为了编译您的 Angular 特定插件部分,我们可以使用 ng-packagr 来协助构建。

npm i ng-packagr -D

使用 ng-packagr 有几个有趣的事情,即 Angular 部分需要自包含,因此从其实际的捆绑导入包名称引用其他“外部”依赖项。例如

import { MyPluginClass } from '../some/vanilla-plugin-code';

这将不起作用,因为 ../ 将代码引入 Angular 构建中,而该代码不是“打包”的 dist 的一部分。要修复,这将变为

import { MyPluginClass } from '@nativescript-community/your-plugin';

因此,您可以对插件组织做出几个架构选择,这也是我们引入新的插件种子以在此提供帮助的原因之一。

A. 您可以创建与插件的 src 文件夹平行的 src-angular 文件夹,并且它将依赖于您的插件本身。B. 您可以将 angular 文件夹保留在插件的 src 内部,但应该以单存储库样式架构构建您的插件,这有助于以更智能的方式管理依赖项以及构建配置设置。

我们最喜欢选项 B,因为它不仅优雅地解决了该问题,而且还解决了其他许多问题,例如简化维护多个插件的便捷性,这些插件都是从依赖项和构建配置的单一事实来源开发的。

无论您选择哪种方向,在插件的 Angular 源代码中,您都希望为 ng-packagr 提供以下内容

{
	"name": "nativescript-community-your-plugin-angular",
	"ngPackage": {
		"lib": {
			"entryFile": "index.ts",
			"umdModuleIds": {
				"@nativescript/core": "ns-core",
				"@nativescript/angular": "ns-angular",
				"@nativescript-community/your-plugin": "ns-community-your-plugin"
			}
		},
		"whitelistedNonPeerDependencies": [
			"."
		]
	}
}

名称可以是任何标识符,但我们只是使用插件名称的标准扁平化并在末尾附加 -angular 来表示这是插件的 Angular 部分。

entryFile 在这里至关重要,为了安全起见,它应该命名为 index.ts。您可以将其命名为其他名称,但如果将其命名为与插件本身相同的名称,则它将失败,因此只需使用 index.ts

以下是我们自己的内部插件 @nativescript/datetimepickerpackage.json 结构示例

datetimepicker
  / angular
        index.ts
        nativescript-datetimepicker.accessors.ts
        nativescript-datetimepicker.directives.ts

以及 index.ts 的内容 可以在这里看到

请注意,它从其完整的插件桶名称 @nativescript/datetimepicker 而不是相对导入路径导入普通 {N} 插件代码。这对于正确打包 Angular 构建非常重要。

一旦 ng-packagr 完成构建,它会在 Angular 源代码内部默认放置一个 dist 目录,其内容可以复制到插件的输出中,以准备发布。

您可以按如下方式启动 Angular 源代码构建

ng-packagr -p angular/package.json

如前所述,我们将引入一个新的插件种子,其中包含已准备就绪的架构来扩展插件开发(以及导入现有插件),并比以前的插件种子更好地处理此类细节。请在未来两周内关注此公告。