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

本教程通过引导您构建一个具有基本功能的示例应用程序,向您介绍使用 Vue 构建 NativeScript 应用程序的基础知识。

本教程将教您以下内容:

  • 使用 NativeScript 组件构建布局
  • 使用手势为您的应用程序添加交互性
  • 使用内置的 Vue 指令创建和修改您的视图
  • 使用路由在不同视图之间导航

先决条件

为了充分利用本教程,您应该已经对 Vue 框架有基本的了解。如果您完全不了解 Vue,您可能需要先查看他们的官方指南

示例应用程序概述

组件构成 Vue 应用程序的基本构建块。组件代表用户交互的页面和视图。NativeScript Vue 遵循相同的概念,区别主要在于模板及其样式。

您将构建一个主从应用程序,该应用程序显示音乐剧列表,并允许您导航到详细信息页面以查看有关每个音乐剧的更多信息。

Example app preview

您可以在GitHub上找到应用程序的完整源代码。

设置您的环境

要设置您的开发环境,请按照文档环境设置部分中的说明进行操作。

创建一个新的 NativeScript Vue 应用程序

要创建一个新的 NativeScript Vue 应用程序,请运行 CLI 命令 ns create,后跟应用程序的名称,再后跟 --vue--ts

cli
ns create example-app --vue --ts

NativeScript CLI 创建一个新目录,其根文件夹名为 example-app,其中包含一个初始的骨架应用程序项目,并安装必要的包和依赖项。这可能需要几分钟时间,安装完成后即可运行。

运行应用程序

转到项目的目录并运行以下命令以在相应的平台上运行它。

cli
cd example-app

// run on iOS
ns run ios

// run on Android
ns run android

ns run 命令构建应用程序并在连接的 Android 设备或 Android 模拟器上启动应用程序(对于 Android)以及连接的 iOS 设备或 iOS 模拟器上启动应用程序(对于 iOS)。默认情况下,它会侦听代码中的更改,同步这些更改并刷新所有选定的设备。

文件夹结构

基于 Vue 初始应用程序,我们将为我们的应用程序创建以下文件/文件夹结构。

app
  |- assets
      |- anastasia.png
      |- beetlejuicemusical.png
      |- bookofmormon.png
  |- components
      |- Home.vue
      |- Details.vue
  |- models
      |- Flick.ts
  |- services
      |- FlickService.ts
  |- app.scss
  |- app.ts

创建主页

让我们从创建主页功能的文件开始,其内容如下:

vue
<!-- app/components/Home.vue -->
<template>
  <Page> </Page>
</template>

<script lang="ts">
import Vue from 'nativescript-vue'

export default Vue.extend({})
</script>

路由设置

我们将把主页组件设置为应用程序启动时的默认路由。打开 app.ts 并添加以下代码:

typescript
// app/app.ts

import Vue from 'nativescript-vue'
import Home from './components/Home.vue'

declare let __DEV__: boolean

// Prints Vue logs when --env.production is *NOT* set while building
Vue.config.silent = !__DEV__

new Vue({
  // Add this (this might be added already by the template) 👇
  render: h => h('frame', [h(Home)])
}).$start()

主页 UI

在创建主页的 UI 之前,让我们先创建 FlickModelFlickService。这将允许我们直接在模板中使用数据。

FlickModel 将包含每个电影对象的形状。在 app 内创建 models 目录,并创建一个名为 Flick.ts 的新文件。打开新的 Flick.ts 并添加以下 interface

typescript
// app/models/Flick.ts

export interface FlickModel {
  id: number
  genre: string
  title: string
  image: string
  url: string
  description: string
  details: {
    title: string
    body: string
  }[]
}

然后,我们将在 FlickService 中使用 FlickModel 来返回我们的电影数据。在 app 内创建 services 目录,并创建一个名为 FlickService.ts 的新文件。打开新的 FlickService.ts 并添加以下内容:

typescript
// app/services/FlickService.ts

import { FlickModel } from '../models/Flick'

export default class FlickService {
  private flicks: FlickModel[] = [
    {
      id: 1,
      genre: 'Musical',
      title: 'Book of Mormon',
      image: '~/assets/bookofmormon.png',
      url: 'https://nativescript.cn/images/ngconf/book-of-mormon.mov',
      description: `A satirical examination of the beliefs and practices of The Church of Jesus Christ of Latter-day Saints.`,
      details: [
        {
          title: 'Music, Lyrics and Book by',
          body: 'Trey Parker, Robert Lopez, and Matt Stone',
        },
        {
          title: 'First showing on Broadway',
          body: 'March 2011 after nearly seven years of development.',
        },
        {
          title: 'Revenue',
          body: 'Grossed over $500 million, making it one of the most successful musicals of all time.',
        },
        {
          title: 'History',
          body: 'The Book of Mormon was conceived by Trey Parker, Matt Stone and Robert Lopez. Parker and Stone grew up in Colorado, and were familiar with The Church of Jesus Christ of Latter-day Saints and its members. They became friends at the University of Colorado Boulder and collaborated on a musical film, Cannibal! The Musical (1993), their first experience with movie musicals. In 1997, they created the TV series South Park for Comedy Central and in 1999, the musical film South Park: Bigger, Longer & Uncut. The two had first thought of a fictionalized Joseph Smith, religious leader and founder of the Latter Day Saint movement, while working on an aborted Fox series about historical characters. Their 1997 film, Orgazmo, and a 2003 episode of South Park, "All About Mormons", both gave comic treatment to Mormonism. Smith was also included as one of South Park\'s "Super Best Friends", a Justice League parody team of religious figures like Jesus and Buddha.',
        },
        {
          title: 'Development',
          body: `During the summer of 2003, Parker and Stone flew to New York City to discuss the script of their new film, Team America: World Police, with friend and producer Scott Rudin (who also produced South Park: Bigger, Longer & Uncut). Rudin advised the duo to see the musical Avenue Q on Broadway, finding the cast of marionettes in Team America similar to the puppets of Avenue Q. Parker and Stone went to see the production during that summer and the writer-composers of Avenue Q, Lopez and Jeff Marx, noticed them in the audience and introduced themselves. Lopez revealed that South Park: Bigger, Longer & Uncut was highly influential in the creation of Avenue Q. The quartet went for drinks afterwards, and soon found that each camp wanted to write something involving Joseph Smith. The four began working out details nearly immediately, with the idea to create a modern story formulated early on. For research purposes, the quartet took a road trip to Salt Lake City where they "interviewed a bunch of missionaries—or ex-missionaries." They had to work around Parker and Stone\'s South Park schedule. In 2006, Parker and Stone flew to London where they spent three weeks with Lopez, who was working on the West End production of Avenue Q. There, the three wrote "four or five songs" and came up with the basic idea of the story. After an argument between Parker and Marx, who felt he was not getting enough creative control, Marx was separated from the project.[10] For the next few years, the remaining trio met frequently to develop what they initially called The Book of Mormon: The Musical of the Church of Jesus Christ of Latter-day Saints. "There was a lot of hopping back and forth between L.A. and New York," Parker recalled.`,
        },
      ],
    },
    {
      id: 2,
      genre: 'Musical',
      title: 'Beetlejuice',
      image: '~/assets/beetlejuicemusical.png',
      url: 'https://nativescript.cn/images/ngconf/beetlejuice.mov',
      description: `A deceased couple looks for help from a devious bio-exorcist to handle their haunted house.`,
      details: [
        {
          title: 'Music and Lyrics',
          body: 'Eddie Perfect',
        },
        {
          title: 'Book by',
          body: 'Scott Brown and Anthony King',
        },
        {
          title: 'Based on',
          body: 'A 1988 film of the same name.',
        },
        {
          title: 'First showing on Broadway',
          body: 'April 25, 2019',
        },
        {
          title: 'Background',
          body: `In 2016, a musical adaptation of the 1988 film Beetlejuice (directed by Tim Burton and starring Geena Davis as Barbara Maitland, Alec Baldwin as Adam Maitland, Winona Ryder as Lydia Deetz and Michael Keaton as Betelgeuse) was reported to be in the works, directed by Alex Timbers and produced by Warner Bros., following a reading with Christopher Fitzgerald in the title role. In March 2017, it was reported that Australian musical comedian Eddie Perfect would be writing the music and lyrics and Scott Brown and Anthony King would be writing the book of the musical, and that another reading would take place in May, featuring Kris Kukul as musical director. The musical has had three readings and two laboratory workshops with Alex Brightman in the title role, Sophia Anne Caruso as Lydia Deetz, Kerry Butler and Rob McClure as Barbara and Adam Maitland.`,
        },
      ],
    },
    {
      id: 3,
      genre: 'Musical',
      title: 'Anastasia',
      image: '~/assets/anastasia.png',
      url: 'https://nativescript.cn/images/ngconf/anastasia.mov',
      description: `The legend of Grand Duchess Anastasia Nikolaevna of Russia.`,
      details: [
        { title: 'Music and Lyrics', body: 'Lynn Ahrens and Stephen Flaherty' },
        {
          title: 'Book by',
          body: 'Terrence McNally',
        },
        {
          title: 'Based on',
          body: 'A 1997 film of the same name.',
        },
        {
          title: 'Background',
          body: `A reading was held in 2012, featuring Kelli Barret as Anya (Anastasia), Aaron Tveit as Dmitry, Patrick Page as Vladimir, and Angela Lansbury as the Empress Maria. A workshop was held on June 12, 2015, in New York City, and included Elena Shaddow as Anya, Ramin Karimloo as Gleb Vaganov, a new role, and Douglas Sills as Vlad.
        The original stage production of Anastasia premiered at the Hartford Stage in Hartford, Connecticut on May 13, 2016 (previews). The show was directed by Darko Tresnjak and choreography by Peggy Hickey, with Christy Altomare and Derek Klena starring as Anya and Dmitry, respectively.
        Director Tresnjak explained: "We've kept, I think, six songs from the movie, but there are 16 new numbers. We've kept the best parts of the animated movie, but it really is a new musical." The musical also adds characters not in the film. Additionally, Act 1 is set in Russia and Act 2 in Paris, "which was everything modern Soviet Russia was not: free, expressive, creative, no barriers," according to McNally.
        The musical also omits the supernatural elements from the original film, including the character of Rasputin and his musical number "In the Dark of the Night", (although that song’s melody is repurposed in the new number "Stay, I Pray You"), and introduces instead a new villain called Gleb, a general for the Bolsheviks who receives orders to kill Anya.`,
        },
      ],
    },
  ]

  getFlicks(): FlickModel[] {
    return this.flicks
  }

  getFlickById(id: number): FlickModel | undefined {
    return this.flicks.find((flick) => flick.id === id) || undefined
  }
}

/app/assets/ 目录添加到您的项目中,并将示例项目此处的 3 个静态图像复制过来。

接下来,让我们分解主页的布局和 UI 元素。

Home page layout breakdown

主页可以分为两个主要部分:带有标题的 ActionBar 和带有卡片的可滚动主要内容区域(我们将在下一节中讨论卡片)。让我们从创建带有标题的 ActionBar 开始。打开 Home.vue 并添加以下代码:

vue
<!-- app/components/Home.vue -->

<template>
  <Page>
    <!-- Add this 👇 -->
    <ActionBar title="NativeFlix" />
  </Page>
</template>

<script lang="ts">
import Vue from 'nativescript-vue'

export default Vue.extend({})
</script>

由于我们有一个要显示的电影数组,因此我们可以使用 NativeScript 的ListView 组件。ListView 是一个 NativeScript UI 组件,可以有效地在垂直或水平滚动列表中渲染项目。让我们首先在主页组件中创建一个名为 flick 的变量,我们将将其用作 ListView 的数据源。打开 Home.vue 并添加以下内容:

vue
<!-- app/components/Home.vue -->

<template>
  <Page>
    <ActionBar title="NativeFlix" />
  </Page>
</template>

<script lang="ts">
import Vue from 'nativescript-vue'
import FlickService from '../services/FlickService'

const flickService = new FlickService()

export default Vue.extend({
  // Add this 👇
  data() {
    return {
      flicks: flickService.getFlicks()
    }
  }
})
</script>

接下来,添加 ListView 组件:

vue
<!-- app/components/Home.vue -->

<template>
  <Page>
    <ActionBar title="NativeFlix" />
    <!-- Add this 👇 -->
    <ListView height="100%" for="item in flicks">
      <v-template>
        <Label :text="item.title" />
      </v-template>
    </ListView>
  </Page>
</template>

<script lang="ts">
import Vue from 'nativescript-vue'
import FlickService from '../services/FlickService'

const flickService = new FlickService()

export default Vue.extend({
  data() {
    return {
      flicks: flickService.getFlicks()
    }
  }
})
</script>

Vue 中的 ListView 使用 for 属性作为其数据源。在上面的代码段中,我们将 for 属性设置为 item in flicks。这将循环遍历 flicks 数组,并为每个条目渲染 v-template 内的内容。如果您现在运行该应用程序,您应该会看到一个电影标题列表。

创建电影卡片

在深入创建下面的卡片之前,让我们为将在应用程序中使用的背景和文本颜色创建一些类。由于这将在整个应用程序中共享,因此让我们将其添加到 app.scss 中。打开 app.scss 并添加以下内容:

scss
// src/app.scss

// applied when device is in light mode
.ns-light {
  .bg-primary {
    background-color: #fdfdfd;
  }
  .bg-secondary {
    background-color: #ffffff;
  }
  .text-primary {
    color: #444;
  }
  .text-secondary {
    color: #777;
  }
}

// applied when device is in dark mode
.ns-dark {
  .bg-primary {
    background-color: #212121;
  }
  .bg-secondary {
    background-color: #383838;
  }
  .text-primary {
    color: #eee;
  }
  .text-secondary {
    color: #ccc;
  }
}

Home page cards breakdown

如上图所示,每个卡片都由 3 个组件组成:预览图像、标题和描述。我们将使用 GridLayout 作为我们的容器,并使用 ImageLabel 组件作为预览图像和文本。打开您的 Home.vue 并添加以下内容:

xml
<!-- app/components/Home.vue -->

<template>
  <Page>
    <ActionBar title="NativeFlix" />
    <!-- Update this 👇 -->
    <ListView height="100%" separatorColor="transparent" for="item in flicks">
      <v-template>
        <GridLayout
          height="280"
          borderRadius="10"
          class="bg-secondary"
          rows="*, auto, auto"
          columns="*"
          margin="5 10"
          padding="0"
        >
          <Image row="0" margin="0" stretch="aspectFill" :src="item.image" />
          <Label
            row="1"
            margin="10 10 0 10"
            fontWeight="700"
            class="text-primary"
            fontSize="18"
            :text="item.title"
          />
          <Label
            row="2"
            margin="0 10 10 10"
            class="text-secondary"
            fontSize="14"
            textWrap="true"
            :text="item.description"
          />
        </GridLayout>
      </v-template>
    </ListView>
  </Page>
</template>

<script lang="ts">
  import Vue from 'nativescript-vue'
  import FlickService from '../services/FlickService'

  const flickService = new FlickService()

  export default Vue.extend({
    data() {
      return {
        flicks: flickService.getFlicks()
      }
    }
  })
</script>

检查点

如果您一直遵循到目前为止的操作,则在任何平台上运行应用程序都应该会生成一个类似于此屏幕截图中的应用程序,其中列表可垂直滚动。

Home page

创建详细信息页面

让我们从创建详细信息功能的文件开始,其内容如下:

vue
<!-- app/components/Details.vue -->

<template>
  <Page></Page>
</template>

<script lang="ts">
import Vue from 'nativescript-vue'

export default Vue.extend({})
</script>

设置从主页到详细信息组件的导航

我们将使用 nativescript-vue 中的 $navigateTo 函数从主页组件导航到详细信息组件。除了路由名称之外,我们还将把电影的 id 作为 prop 传递给我们的 $navigateTo 函数。我们将在详细信息组件中使用此 id 来访问有关电影的更多信息。打开 Home.vue 并添加以下内容:

vue
<!-- app/components/Home.vue -->

<template>
  <Page>
    <ActionBar title="NativeFlix" />
    <ListView height="100%" separatorColor="transparent" for="item in flicks">
      <v-template>
        <GridLayout
          height="280"
          borderRadius="10"
          class="bg-secondary"
          rows="*, auto, auto"
          columns="*"
          margin="5 10"
          padding="0"
        >
          <Image row="0" margin="0" stretch="aspectFill" :src="item.image" />
          <Label
            row="1"
            margin="10 10 0 10"
            fontWeight="700"
            class="text-primary"
            fontSize="18"
            :text="item.title"
          />
          <Label
            row="2"
            margin="0 10 10 10"
            class="text-secondary"
            fontSize="14"
            textWrap="true"
            :text="item.description"
          />
        </GridLayout>
      </v-template>
    </ListView>
  </Page>
</template>

<script lang="ts">
import Vue from 'nativescript-vue'
import FlickService from '../services/FlickService'
import Details from './Details.vue'

const flickService = new FlickService()

export default Vue.extend({
  data() {
    return {
      flicks: flickService.getFlicks()
    }
  },
  methods: {
    // Add this 👇
    onFlickTap(args) {
      const id = args.item.id
      this.$navigateTo(Details, {
        props: { id }
      })
    }
  }
})
</script>

接下来,让我们将点击事件添加到 ListView 项目中。打开 Home.vue 并添加以下内容:

xml
<!-- app/components/Home.vue -->

<template>
  <Page>
    <ActionBar title="NativeFlix" />
    <StackLayout height="100%">
      <ListView
        height="100%"
        separatorColor="transparent"
        for="item in flicks"
        @itemTap="onFlickTap"
      >
        <!-- 👈  Add this -->
        <v-template>
          <GridLayout
            height="280"
            borderRadius="10"
            class="bg-secondary"
            rows="*, auto, auto"
            columns="*"
            margin="5 10"
            padding="0"
          >
            <Image row="0" margin="0" stretch="aspectFill" :src="item.image" />
            <Label
              row="1"
              margin="10 10 0 10"
              fontWeight="700"
              class="text-primary"
              fontSize="18"
              :text="item.title"
            />
            <Label
              row="2"
              margin="0 10 10 10"
              class="text-secondary"
              fontSize="14"
              textWrap="true"
              :text="item.description"
            />
          </GridLayout>
        </v-template>
      </ListView>
    </StackLayout>
  </Page>
</template>

<script lang="ts">
  import Vue from 'nativescript-vue'
  import FlickService from '../services/FlickService'
  import Details from './Details.vue'

  const flickService = new FlickService()

  export default Vue.extend({
    data() {
      return {
        flicks: flickService.getFlicks()
      }
    },
    methods: {
      onFlickTap(args) {
        const id = args.item.id
        this.$navigateTo(Details, {
          props: { id }
        })
      }
    }
  })
</script>

访问导航 props

我们在上一节中将用户点击的电影卡片的 id 作为 prop 传递,因为我们导航到详细信息组件。我们可以使用 props 属性获取传入的 id。然后,我们可以使用 id 获取选定的电影信息,以便在详细信息组件的模板中显示。打开 Details.vue 并添加以下内容:

vue
<!-- app/components/Details.vue -->

<template>
  <Page></Page>
</template>

<script lang="ts">
import Vue from 'nativescript-vue'
// Add this 👇
import FlickService from '../services/FlickService'

// Add this 👇
const flickService = new FlickService()

export default Vue.extend({
  // Add this 👇
  props: ['id'],
  data() {
    // Add this 👇
    return {
      flick: flickService.getFlickById(this.id)
    }
  }
})
</script>

详细信息 UI

让我们分解详细信息页面的布局和 UI 元素。

Details page layout breakdown

详细信息页面可以分为三个主要部分:带有电影标题的 ActionBar、英雄图像以及带有电影详细信息的主要内容。我们将使用 flicks 对象中的 details 数组填充电影详细信息部分。details 数组包含带有 titlebody 的对象,它们以统一的方式渲染,每个都有自己的样式。我们可以使用 Vue 的 v-for 指令循环遍历数组,并为数组中的每个条目创建一个 UI 元素或一组元素。打开 Details.vue 并添加以下代码:

vue
<!-- app/components/Details.vue -->

<template>
  <Page>
    <!-- Add this 👇 -->
    <ActionBar :title="flick.title" />

    <!-- Add this 👇 -->
    <ScrollView height="100%">
      <StackLayout>
        <Image margin="0" stretch="aspectFill" :src="flick.image" />
        <StackLayout padding="10 20">
          <StackLayout v-for="detail in flick.details" :key="detail.id">
            <Label
              marginTop="15"
              fontSize="16"
              fontWeight="700"
              class="text-primary"
              textWrap="true"
              :text="detail.title"
            />
            <Label
              fontSize="14"
              class="text-secondary"
              textWrap="true"
              :text="detail.body"
            />
          </StackLayout>
        </StackLayout>
      </StackLayout>
    </ScrollView>
  </Page>
</template>

<script lang="ts">
import Vue from 'nativescript-vue'
import FlickService from '../services/FlickService'

const flickService = new FlickService()

export default Vue.extend({
  props: ['id'],
  data() {
    return {
      flick: flickService.getFlickById(this.id)
    }
  }
})
</script>

检查点

现在,在任何一个平台上运行应用程序都应该会得到一个类似于此屏幕截图中的应用程序,并且能够在主页和详情页之间导航。

Details page

下一步

恭喜!您已经构建了第一个可在 iOS 和 Android 上运行的 NativeScript 应用程序。您可以继续添加更多 NativeScript UI 组件(或构建您自己的自定义 UI 组件),或者可以添加一些 原生功能。可能性是无限的!