你可能已经听说过,从 5.2 版本开始,NativeScript CLI 可以构建基于 Vue 的原生移动应用。对于一个小版本的发布来说,这是一个很棒的额外功能,我认为这是朝着正确方向迈出的一步。
我一直是 TypeScript 的粉丝,它在企业中被广泛使用,并且在 2019 年也得到了 VueJS 社区的认可。因此,听到 NativeScript-Vue 最近也获得了 TypeScript 支持,我感到非常高兴。
虽然我们正在等待 NativeScript CLI 为其 Vue 项目提供 TypeScript 选项,但仍然有一种替代方法可以创建 NativeScript-Vue 项目,从而获得 TypeScript 支持,并允许你使用类组件。
让我们看看如何在 NativeScript-Vue 中利用类组件。
我们需要创建一个 NativeScript 项目,同时支持 Vue 和 TypeScript。我在 YouTube 上发布的最近的视频中展示了如何做到这一点。以下是步骤。
我假设你已经在你的机器上安装了最新的NativeScript 必备组件。
首先,通过 npm 全局安装 Vue CLI 和 cli-init 工具
npm install -g @vue/cli @vue/cli-init
使用 Vue CLI 和 cli-init 工具以及 nativescript-vue vue-cli-template 生成一个新的 NativeScript 项目。
vue init nativescript-vue/vue-cli-template <project-name>
运行此 Vue CLI 命令时,你会得到一个交互式向导,它会询问你一些问题。
- Project name
- Project description
- Application...
等等...
你需要选择的语言选项是
- Select the programming language
- javascript
- typescript <- this one
按照向导的其余步骤设置项目,在此之后,你选择的任何选项都可以。
这将创建一个新的 NativeScript 项目,并在 package.json 文件中包含正确的包,一个正确配置的 tsconfig.json
,以及最重要的:一个正确配置的 webpack.config.js
文件。还有一个 types
文件夹,其中包含环境的额外 TypeScript 声明、Vue shims 和 nativescript 平台引用。
app
文件夹中有一个 main.ts
文件。是的,你看到的是 .ts
扩展名。
并且你的单文件 Vue 组件有一个代码块,指定 TypeScript 作为语言。请注意 script
标签上 lang
属性的 ts
值。
<script lang="ts">
export default {
data() {
return {
msg: 'Hello World!'
}
}
}
</script>
顺便说一句,如果你愿意,可以省略此 lang 属性并在此处只编写纯 JavaScript。例如,这对于从 JavaScript 过渡到 TypeScript 的项目很有用。
运行项目时,不要忘记你需要使用 --bundle
标志。否则,你将百思不得其解,抓耳挠腮,想知道发生了什么。
tns run ios --bundle
或者
tns run android --bundle
如果你来自 Angular 背景,那么你将对带有装饰器的基于类的组件感到宾至如归。这是切换到使用 TypeScript 后的下一步,也是 Angular 开发人员的自然选择。
为什么要使用类组件?很简单 - 语法更简洁。类属性自动成为数据属性。无需使用由数据属性返回的奇怪函数语法,**并且**你也不必担心 this
。
开箱即用,新的 nativescript-vue cli 模板创建对象表示法 TypeScript 组件。如果你想获得类组件**和**TypeScript,则需要执行一些手动步骤。希望这些步骤将来也会被集成到模板中。
在项目的 package.json
文件中,添加以下依赖项以支持 Vue 类组件和装饰器
"dependencies": {
...
"vue-class-component": "^6.3.2",
"vue-property-decorator": "^7.3.0",
...
},
运行 npm install
你可以立即开始使用类组件。让我们尝试创建一个简单的临时组件,以便我们可以了解它们的工作原理。创建一个单文件组件文件,我们将其命名为 MyComp.vue
并添加以下代码
<template>
<Label class="message" :text="msg" col="0" row="0"/>
</template>
<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";
@Component
export default class MyComp extends Vue {
@Prop() private msg: string;
}
</script>
请注意,默认导出不是 JavaScript 对象,而是导出一个类。并且我们不是从 vue
中导入 Vue
,而是从 vue-property-decorator
中导入它。
不要忘记类上的 @Component
装饰器。
在这里,我们将稍微改变一下方向,创建一个新的示例,以便我们可以检查在 NativeScript-Vue 中使用类组件的所有方面。
类组件 .vue
文件与它们的 object notation .vue
文件并没有太大区别。
以下是我们将要使用的示例。
<template>
<StackLayout class="form">
<StackLayout class="input-field">
<Label text="First Name" class="label font-weight-bold m-b-5"/>
<TextField v-model="firstName" hint="First Name" class="input input-border"/>
</StackLayout>
</StackLayout>
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
@Component
export default class PersonClassComponent extends Vue {
}
</script>
这里需要注意的两件事是,我们导出一个继承自 Vue
的类,并且我们使用 @Component
装饰器修饰了该类,这与我们在 Angular 中的操作类似。
如果你认真考虑在你的 NativeScript VueJS 应用中使用 TypeScript 的类组件,你需要了解以下五件事。我创建了一个YouTube 视频,详细展示了每一件事。
通过使用 vue-property-decorator
中的 @Prop
装饰器,我们只需修饰类属性即可声明输入属性。
<template>
<StackLayout class="form">
<StackLayout class="input-field">
<Label text="First Name" class="label font-weight-bold m-b-5"/>
<TextField v-model="firstName" hint="First Name" class="input input-border"/>
</StackLayout>
</StackLayout>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
@Component
export default class PersonClassComponent extends Vue {
@Prop() whatToSay: string;
}
</script>
如果你来自 object notation Vue,那么你习惯了这样的代码
export default {
props: {
whatToSay: {
type: String
}
}
};
使用类组件时,data
非常简单。它只是类上的属性
<template>
<StackLayout class="form">
<StackLayout class="input-field">
<Label text="First Name" class="label font-weight-bold m-b-5"/>
<TextField v-model="firstName" hint="First Name" class="input input-border"/>
</StackLayout>
</StackLayout>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
@Component
export default class PersonClassComponent extends Vue {
@Prop() whatToSay: string;
//Data
counter = 1;
firstName = "Donna";
initialLastName = "Summer";
lastName = this.initialLastName;
}
</script>
以下是 object notation 中 data
的样子 - 我认为它没有那么容易理解
data() {
return {
counter: 1,
firstName: "Donna",
initialLastName: "Summer",
lastName: "Summer"
};
},
Watcher 可能最复杂的部分,因为它们被定义为带有 @Watch
装饰器的类方法。@Watch
装饰器必须指定我们正在监视哪个属性。
<template>
<StackLayout class="form">
<StackLayout class="input-field">
<Label text="First Name" class="label font-weight-bold m-b-5"/>
<TextField v-model="firstName" hint="First Name" class="input input-border"/>
</StackLayout>
</StackLayout>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
@Component
export default class PersonClassComponent extends Vue {
@Prop() whatToSay: string;
counter = 1;
firstName = "Donna";
initialLastName = "Summer";
lastName = this.initialLastName;
//Here we define a Watch
@Watch("firstName")
onFirstNameChanged() {
this.lastName = this.initialLastName + this.counter++;
}
}
</script>
但是,它仍然比 object notation 更简洁,object notation 看起来像这样
watch: {
firstName: {
handler() {
this.lastName = this.initialLastName + this.counter++;
console.log("first name changed");
}
}
}
三重嵌套对象绝对不容易查看或理解。再说一遍,这是我的观点。
Computed 是我最喜欢的,因为它们在类中以 getter(和 setter)属性的方式执行。
<template>
<StackLayout class="form">
<StackLayout class="input-field">
<Label text="First Name" class="label font-weight-bold m-b-5"/>
<TextField v-model="firstName" hint="First Name" class="input input-border"/>
</StackLayout>
<StackLayout class="input-field">
<Label text="Full Name" class="label font-weight-bold m-b-5"/>
<Label :text="fullName"/>
</StackLayout>
</StackLayout>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
@Component
export default class PersonClassComponent extends Vue {
@Prop() whatToSay: string;
counter = 1;
firstName = "Donna";
initialLastName = "Summer";
lastName = this.initialLastName;
//Computed
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
@Watch("firstName")
onFirstNameChanged() {
this.lastName = this.initialLastName + this.counter++;
}
}
</script>
由于我们使用的是类,所以猜猜看!方法只是类方法!在模板中声明一个事件处理程序,然后在你的类中编写一个方法即可。
<template>
<StackLayout class="form">
<StackLayout class="input-field">
<Label text="First Name" class="label font-weight-bold m-b-5"/>
<TextField v-model="firstName" hint="First Name" class="input input-border"/>
</StackLayout>
<StackLayout class="input-field">
<Label text="Full Name" class="label font-weight-bold m-b-5"/>
<Label :text="fullName"/>
</StackLayout>
<Button text="SPEAK" @tap="speak"/>
</StackLayout>
</template>
<script lang="ts">
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
@Component
export default class PersonClassComponent extends Vue {
@Prop() whatToSay: string;
counter = 1;
firstName = "Donna";
initialLastName = "Summer";
lastName = this.initialLastName;
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
@Watch("firstName")
onFirstNameChanged() {
this.lastName = this.initialLastName + this.counter++;
}
//And here is the method
speak() {
alert("This is " + this.fullName + " speaking. " + this.whatToSay);
}
}
</script>
我知道类组件并不适合所有人,有些人真的喜欢使用纯 JavaScript,这完全没问题。这只是另一种方法,如果你确实要在 NativeScript-vue 应用中使用 TypeScript,那么 Vue 中的类组件是自然之选。尤其是在那些来自面向对象编程背景的人 - .NET 和 Angular 开发人员,包括你。
你想要更多 NativeScript-Vue 视频吗?我有一个NativeScript-Vue 入门课程,另一位 NativeScript 开发专家 Paul Halliday 也提供了一个全新的课程,专门讲解使用 NativeScript-Vue 和 Vuex 进行状态管理。