教程
使用 Svelte 构建大师-详情应用程序
本教程将通过指导您构建一个具有一些基本功能的示例应用程序来向您介绍使用 Svelte 的 NativeScript 基础知识。
本教程将教您以下内容
- 使用 NativeScript 组件构建布局
- 使用手势为您的应用程序添加交互性
- 使用路由在不同视图之间导航
先决条件
要充分利用此教程,你应该已经基本了解 Svelte。如果你完全不了解 Svelte,你可能需要先尝试官方Svelte 教程。
示例应用程序概述
你将构建一个主从应用程序,该应用程序显示音乐剧列表,并允许你转到详细信息页面查看有关每个音乐剧的更多信息。
你可以在 GitHub 上找到该应用程序的完整源代码
设置你的环境
要设置你的开发环境,请按照文档的环境设置部分中的说明进行操作。
创建新的 NativeScript 应用程序
我们将为此教程使用 TypeScript。要创建新的 NativeScript TypeScript 应用程序,请运行 CLI 命令 ns create
,后面跟上应用程序的名称,再跟上 --svelte
。
ns create example-app --svelte
NativeScript CLI 创建一个新的目录,根文件夹名为 example-app
,其中包含一个初始骨架应用程序项目,并安装了必要的包和依赖项。这可能需要几分钟时间,完成后应可运行。
运行应用程序
转到项目的目录并运行以下命令,以在各自的平台上运行它。
cd example-app
// run on iOS
ns run ios
// run on Android
ns run android
ns run
命令会构建该应用程序并在连接的安卓设备或安卓模拟器(安卓系统)上启动该应用程序,或在连接的 iOS 设备或 iOS 模拟器(iOS 系统)上启动该应用程序。默认情况下,它侦听你的代码中的更改,同步这些更改,并刷新所有选中的设备。
文件夹结构
基于 TypeScript starter 应用程序,我们将为我们的应用程序创建以下文件/文件夹结构。
app
|- assets
|- anastasia.png
|- beetlejuicemusical.png
|- bookofmormon.png
|- pages
|- Home.svelte
|- Details.svelte
|- models
|- flick.ts
|- services
|- flickService.ts
|- app.css
|- App.svelte
|- app.ts
创建主页
让我们从使用以下内容创建主页的文件开始
<!-- app/pages/Home.svelte -->
<page> </page>
<script></script>
路由设置
当应用程序启动时,我们将把主页设置为我们的默认路由。我们可以通过在 App.svelte
文件中导入主页组件,并将其设置为根的 frame
组件的 defaultPage
来设置默认路由。打开 App.svelte
并添加以下代码
<!-- app/App.svelte -->
<page>
<frame id="rootFrame" defaultPage="{Home}"></frame>
</page>
<script>
import Home from './pages/Home.svelte'
</script>
主页 UI
在创建主页面的用户界面之前,我们先来创建我们的 FlickModel
和 FlickService
。这将允许我们直接在模板中使用数据。
FlickModel
将包含每个轻弹对象的外观。在 app
内创建一个名为 models
的目录,并创建一个名为 flick.ts
的新文件。打开新的 flick.ts
,并添加以下 interface
:
// 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
,并添加以下内容:
// app/services/flickService.ts
import { FlickModel } from '../models/flick'
export 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.`,
},
],
},
]
static getInstance(): FlickService {
return FlickService._instance
}
private static _instance: FlickService = new FlickService()
getFlicks(): FlickModel[] {
return this.flicks
}
getFlickById(id: number): FlickModel | undefined {
return this.flicks.find((flick) => flick.id === id) || undefined
}
}
为项目添加一个 /app/assets/
目录,并从示例项目复制这 3 张静态图片,在此处。
接下来,我们来分解主页面的布局和用户界面元素。
主页可以分为两个主要部分:带标题的 ActionBar 和带卡片的可滚动主内容区域(我们将在下一节介绍卡片)。我们从创建带有标题的 ActionBar 开始。打开 Home.svelte
,并添加以下代码:
<!-- app/pages/Home.svelte -->
<page>
<actionBar title="NativeFlix" />
</page>
<script></script>
由于我们有一个要显示的轻弹数组,因此我们可以使用 NativeScript 的ListView
组件。ListView
是一个 NativeScript 用户界面组件,可以高效地在垂直或水平滚动列表中呈现项目。我们先在我们的家庭组件中创建一个名为 flick 的变量,我们将其用作我们的 ListView
数据源。打开 Home.svelte
,并添加以下内容:
<!-- app/pages/Home.svelte -->
<page>
<actionBar title="NativeFlix" />
</page>
<script>
// Add the following 👇
import { FlickService } from '../services/FlickService'
let flicks = FlickService.getInstance().getFlicks()
</script>
接下来,添加 ListView
组件:
<!-- app/pages/Home.svelte -->
<page>
<actionBar title="NativeFlix" />
<!-- Add this 👇 -->
<stackLayout height="100%">
<listView height="100%" separatorColor="transparent" items="{flicks}">
<Template let:item>
<label text="{item.title}" />
</Template>
</listView>
</stackLayout>
</page>
<script>
// Add this 👇
import { Template } from 'svelte-native/components'
import { FlickService } from '../services/FlickService'
let flicks = FlickService.getInstance().getFlicks()
</script>
NativeScript Svelte 中的 ListView
使用 items
属性作为数据源。在上面的代码段中,我们将 items
属性设置为 flicks
。这会遍历 flicks
observable 数组,并针对每个条目在 Template
内呈现内容。如果您现在运行此应用程序,则应看到一个轻弹标题列表。
创建轻弹卡片
在我们深入研究创建下方的卡片之前,让我们为将在应用程序中使用的背景和文本颜色创建一些类。由于这将在整个应用程序中共享,因此我们将其添加到 app.css
。打开 app.css
,并添加以下内容:
/* app/app.scss */
/* applied when device is in light mode */
.ns-light .bg-primary {
background-color: #fdfdfd;
}
.ns-light .bg-secondary {
background-color: #ffffff;
}
.ns-light.text-primary {
color: #444;
}
.ns-light.text-secondary {
color: #777;
}
/* applied when device is in dark mode */
.ns-dark .bg-primary {
background-color: #212121;
}
.ns-dark .bg-secondary {
background-color: #383838;
}
.ns-dark .text-primary {
color: #eee;
}
.ns-dark .text-secondary {
color: #ccc;
}
正如您在上图中看到的,每张卡片由 3 个组件组成,预览图像、标题和说明。我们将使用 GridLayout
作为容器,并使用 Image
和 Label
组件作为预览图像和文本。打开 Home.svelte
,并添加以下内容:
<!-- app/pages/Home.svelte -->
<page>
<actionBar title="NativeFlix" />
<stackLayout height="100%">
<listView height="100%" separatorColor="transparent" items="{flicks}">
<template let:item>
<!-- Add this 👇 -->
<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>
</template>
</listView>
</stackLayout>
</page>
<script>
import { Template } from 'svelte-native/components'
import { FlickService } from '../services/FlickService'
let flicks = FlickService.getInstance().getFlicks()
</script>
检查点
如果您按照此步骤进行操作,则在任意平台上运行该应用都应会产生类似于这幅屏幕截图中的应用,其中列表可以垂直滚动。
创建详情页面
让我们首先使用下列内容创建我们的详情页面的文件
<!-- app/pages/Details.svelte -->
<page> </page>
<script></script>
设置从主页到详情组件的导航
我们将使用来自 svelte-native 类的 navigate 函数从主页组件导航至详情组件。除了页面名称之外,我们还将传入 flick 的 id 作为 navigate 函数的 props 对象的一部分。我们将在详情组件中使用此 id 访问更多有关 flick 的信息。打开 Home.svelte,并添加以下内容
<!-- app/pages/Home.svelte -->
<page>
<actionBar title="NativeFlix" />
<stackLayout height="100%">
<listView height="100%" separatorColor="transparent" items="{flicks}">
<Template let:item>
<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>
</Template>
</listView>
</stackLayout>
</page>
<script>
// Add this 👇
import { navigate } from 'svelte-native'
import { Template } from 'svelte-native/components'
import { FlickService } from '../services/FlickService'
// Add this 👇
import Details from './Details.svelte'
let flicks = FlickService.getInstance().getFlicks()
// Add this 👇
function onFlickTap(event) {
navigate({
page: Details,
props: { flickId: flicks[event.index].id }
})
}
</script>
接下来,让我们将点击事件添加到 ListView 项目。打开 Home.svelte,并添加以下内容
<!-- app/pages/Home.svelte -->
<page>
<actionBar title="NativeFlix" />
<stackLayout height="100%">
<!-- add itemTap 👇 -->
<listView
height="100%"
separatorColor="transparent"
items="{flicks}"
on:itemTap="{onFlickTap}"
>
<Template let:item>
<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>
</Template>
</listView>
</stackLayout>
</page>
<script>
import { navigate } from 'svelte-native'
import { Template } from 'svelte-native/components'
import { FlickService } from '../services/FlickService'
import Details from './Details.svelte'
let flicks = FlickService.getInstance().getFlicks()
function onFlickTap(event) {
navigate({
page: Details,
props: { flickId: flicks[event.index].id }
})
}
</script>
访问导航道具
在我们导航至详情页面时,已在上一部分中传入用户点按的 flick 卡片 id。我们可以通过在详情组件中声明并导出同名变量来访问传入的 flickId。然后,我们可以使用 id 获得所选的 flick 信息,以便在详情组件的模板中显示。打开 Details.svelte,并添加以下内容
<!-- app/pages/Details.svelte -->
<page> </page>
<script>
// Add this 👇
export let flickId
</script>
接下来,让我们使用 flickId 从我们的 FlickService 获取 flick 信息。打开 Details.svelte,并添加以下内容
<!-- app/pages/Details.svelte -->
<page> </page>
<script>
// Add this 👇
import { FlickService } from '../services/FlickService'
export let flickId
// Add this 👇
let flick = FlickService.getInstance().getFlickById(flickId)
</script>
详情 UI
让我们分解详情页面的布局和 UI 元素。
详情页面可分为三个主要部分,带有 flick 标题的 ActionBar、英雄图像以及带有 flick 详细信息的主内容。我们将使用 flicks 对象中的 details 数组来填充 flick 详细信息部分。details 数组包含具有 title 和 body 的对象,这些对象以统一的方式呈现,每个对象都有自己的样式。我们可以使用 Svelte 的 #each 块来遍历数组,并针对数组中的每个输入创建一个 UI 元素或一系列元素。打开 Details.svelte 并添加以下代码
<!-- app/pages/Details.svelte -->
<page>
<!-- Add this 👇 -->
<actionBar title="{flick.title}" />
<!-- Add this 👇 -->
<scrollView>
<stackLayout>
<image margin="0" stretch="aspectFill" src="{flick.image}" />
<stackLayout padding="10 20">
{#each flick.details as details}
<stackLayout>
<label
marginTop="15"
fontSize="16"
fontWeight="700"
class="text-primary"
textWrap="true"
text="{details.title}"
/>
<label
fontSize="14"
class="text-secondary"
textWrap="true"
text="{details.body}"
/>
</stackLayout>
{/each}
</stackLayout>
</stackLayout>
</scrollView>
</page>
<script>
import { FlickService } from '../services/FlickService'
export let flickId
let flick = FlickService.getInstance().getFlickById(flickId)
</script>
检查点
现在,在任意平台上运行该应用都应会产生类似于这幅屏幕截图中的应用,并且能够在主页和详情页面之间导航。
下一步
恭喜!你构建了你的第一个 NativeScript 应用,它可同时在 iOS 和 Android 上运行。你可以继续添加更多NativeScript UI 组件(或构建你自己的自定义 UI 组件),或者你可以添加一些原生功能。实现的可能性无穷无尽!