用Vue.js来开发一个电影App的前端部分

我们要构建一个什么样的App?

我们大多数人使用在线流媒体服务(如Netflix)观看我们最喜欢的电影或者节目。这篇文章将重点介绍如何通过使用vue.js 2建立一个类似风格的电影流媒体WEB交互界面(见上图)。

最终的产品可以去这里找:https://codepen.io/itslit/full/mvvjzr。

尽管Bulma将作为应用的CSS框架,但是本文将主要集中在Vue.js的使用和浏览CSS式样,如果你想跟着学,我设置了一个可以作为开始学习的地方,所有自定义组合,初始数据对象和必要的需要通过CDN引用的外部库(如Vue-router等),都可以从https://codepen.io/itslit/pen/qmzrev获得。

App的基本需求

让我们记下这些基本需求:

  1. 介绍(登录)屏幕
  2. 页脚要允许用户可以选择自己想要的电影
  3. 一个电影屏幕,显示电影的标题/描述和并且有“立即播放”的提示。
  4. 一个电影预告片屏幕,在电影播放时显示电影的预告片。
  5. 可以将电影添加到收藏夹中

我们将创建应用程序,让页脚随时出现,而首页、电影和电影预告片将共享相同的屏幕。

数据

为了简单起见,我们将从一个简单/可靠的数据对象(对象)开始,它将作为我们所有组件的主存储器。这个存储对象将拥有我们所需要的所有电影信息,并将集中在克里斯托弗·诺兰的令人敬畏的电影。下面是数据对象的一部分:

const movies = {

"dunkirk": {

"id": ‘dunkirk‘

"title": ‘Dunkirk‘,

"subtitle": ‘Dunkirk‘,

"description": ‘Miraculous evacuation of Allied soldiers from Belgium, Britain,
Canada, and France, who were cut off and surrounded by the German army
from the beaches and harbor of Dunkirk, France, during the Battle of France in
World War II.‘,

"largeImgSrc": `url(‘https://image.tmdb.org/t/p/w780/fudEG1VUWuOqleXv6NwCExK0VLy.jpg‘)`,

"smallImgSrc‘: ‘https://image.tmdb.org/t/p/w185/fudEG1VUWuOqleXv6NwCExK0VLy.jpg‘,

"releaseDate": ‘July 21 2017‘,

"duration": ‘1hr 46min‘,

"genre": ‘Action, Drama, History‘,

"trailerPath": ‘https://www.youtube.com/embed/F-eMt3SrfFU‘,

"favorite": false

},

"interstellar": {

...

},

"the-dark-knight-rises": {

...

},

"inception": {

...

},

"the-prestige": {

...

}

}

让我们开始构建App

既然我们已经创建了主要的存储对象并理解了我们的所有组件是如何布局的,我们就可以开始构建接口了。

让我们首先创建Vue实例。我们将把实例挂载到DOM元素app,并返回全局存储的movies,作为我们的HTML中访问的实例数据对象的一部分。

const rootApp = new Vue({

el: ‘#app‘,

data() {

return {

movieChoices: movies

}

}

})

我们现在可以开始处理每个独立的组件了。

页脚部分

让我们从列出数据存储中所有电影的固定页脚部分开始。

<div id="app">

<section class="hero is-primary is-medium">

<div class="hero-foot">

<div class="columns is-mobile">

<div v-for="movieChoice in movieChoices" class="column">

<li class="movie-choice">

<img :src="`${movieChoice.smallImgSrc}`" class="desktop"/>

<p class="mobile">{{ movieChoice.subtitle }}</p>

</li>

</div>

</div>

</div>

</section>

</div>

对已在上述粗体代码片段的部分:

  1. 我们用id="app"创建的DOM元素,在那里我们的Vue实例将被挂载。
  2. 我们使用的是原生的v-for指令从数据源movieChoices渲染列表。
  3. 在每一个movieChoice:
  • 我们将绑定一个图像的src到描述我们电影的smallImgSrc对象内的url(“desktop”)。
  • 我们用Mustache语法,数据绑定到movieChoice.subtitle作为手机屏幕文本显示内容。

随着所有CSS样式的渲染,我们的应用程序目前应该像这样:

桌面页脚

手机端页脚

电影介绍组件(和Vue-router

我们创建了页脚,现在我们的目标是创建一个具有我们的App标题和描述的电影介绍组件。

我们已经提到了组件将共享相同的屏幕为即将上映的电影预告片和组件(即用户将能够在我们的APP中直接通过点击Intro -> Movie -> MovieComponent到达相应的链接)。

这是一个完美的用例添加vue-router库。vue-router是vue.js官方路由器,是允许组件深入的集成的可配置的路由器,还可以嵌套/视图映射等等。

我们将在js文件中进行基本路由设置:

const Intro = {

template:

`<div class="hero-body" style="background: #1e1d1d">

<div class="container has-text-centered">

<div class="columns">

<div class="column is-half is-offset-one-quarter vertical-align">

<h1 class="home-intro">

VueFlix

</h1>

<p class="home-subintro">Select a movie below from the list of critically acclaimed Christopher Nolan films.</p>

</div>

</div>

</div>

</div>`

}

const routes = [

{ path: ‘/‘, component: Intro },

]

const router = new VueRouter({

routes

})

上面你可以看到我们定义的第一个路由组件Intro,我们路由这个组件{ path: ‘/‘, component: Intro }和实例化我们的路由new VueRouter({ routes })。

注:通过Vue有多种方式定义组件模板。电影介绍和电影详细页的组件使用ES6的模板文本,定义模板的多个路径。Anthony Gore有一篇文章: 7 Ways To Define A Component Template in Vue.js。

我们现在需要注入我们的router到Vue实例,这使整个App的路由和渲染到我们的DOM<router-view></router-view>。

注入我们的router到Vue实例:

const rootApp = new Vue({

el: ‘#app‘,

router: router,

data() {

return {

movieChoices: movies

}

}

})

在DOM中渲染我们的路由组件:

<div id="app">

<section class="hero is-primary is-medium">

<router-view></router-view>

<div class="hero-foot">

<div class="columns">

<div v-for="movieChoice in movieChoices" class="column">

<li class="movie-choice">

<img :src="`${movieChoice.smallImgSrc}`" class="desktop"/>

<p class="mobile">{{ movieChoice.subtitle }}</p>

</li>

</div>

</div>

</div>

</section>

</div>

我们已经成功地创建了我们的第一个根路径:path: ‘/‘来显示我们的IntroComponent。随着我们添加的所有样式,我们的应用程序应该如下所示:

电影组件(多路由)

我们现在App已经完成我们指定的主要路径和我们的页脚部分布局。让我们将路径扩展到显示特定电影所有信息的电影组件。

首先,让我们正确地设置导航。如前所述,我们设置页脚的目的是允许用户在电影之间导航。我们将使用Vue的vue-router的router-link组件去实现导航并提供相应的目标地址。

回到HTML并对页脚部分进行小的编辑:

<div id="app">

<section class="hero is-primary is-medium">

<router-view></router-view>

<div class="hero-foot">

<div class="columns">

<div v-for="movieChoice in movieChoices" class="column">

<router-link :to="`/${movieChoice.id}`"

tag="li"

class="movie-choice">

<img :src="`${movieChoice.smallImgSrc}`" class="desktop"/>

<p class="mobile">{{ movieChoice.subtitle }}</p>

</router-link>

</div>

</div>

</div>

</section>

</div>

我们已经建立了一个`/${movieChoice.id}`使用ES6模板文本标签的目标url作为ID的独立的电影(例如黑暗骑士崛起的路径/the-dark-knight-rises)。tag参数表明,我们希望我们的router-link作为一个li渲染,而仍然侦听点击事件。

为了补充我们的新导航路径,我们需要为我们的电影组件设置一个动态路由。回到我们设置路径的地方:

const routes = [

{ path: ‘/‘, component: Intro },

{ path: ‘/:id‘, component: Movie }

]

我们已经用一个动态段表示:id指向同一组件Movie每一条路由。我们现在能够通过读取$route.params.id获得不同的动态段内组件。

现在我们已经为电影组件设置了路由,让我们快速草拟组件并确保我们的路由正常工作。

电影组件的初稿:

const Movie = {

template:

`<div>

<div class="hero-body">

<div class="container has-text-centered">

<div class="columns">

<div class="column is-half is-offset-one-quarter vertical-align">

<h1 class="home-intro">

{{ selectedMovie.title }}

</h1>

</div>

</div>

</div>

</div>

</div>`,

data () {

return {

selectedMovie: movies[this.$route.params.id]

}

},

watch: {

$route () {

this.selectMovie()

}

},

methods: {

selectMovie () {

this.selectedMovie = movies[this.$route.params.id]

}

}

}

这里有几点需要注意。

data () {

return {

selectedMovie: movies[this.$route.params.id]

}

}

data函数基于$route.params.id设置组件内的selectedMovie属性,可以从全局的movies保存对象。

所以假设电影选择的是黑暗骑士崛起,这selectedMovie将是movies[the-dark-knight-rises]。

watch: {

$route () {

this.selectMovie()

}

},

methods: {

selectMovie () {

this.selectedMovie = movies[this.$route.params.id]

}

}

我们用watch看路径然后有任何变化调用组件的selectMovie方法。该方法selectMovie简单更新selectedMovie参数用新电影的选择。当用户从一个电影组件切换到另一个电影组件(即开关电影)时,这是必须处理的。

测试所有的东西,我们应该能够看到路由已经工作:

现在我们知道我们的路由工作得很好,我们将更新我们的电影组件中的模板来显示所有关于电影的期望信息。

const Movie = {

template:

`<div class="hero-body"

:style="{ ‘background-image‘: selectedMovie.largeImgSrc }">

<header class="nav">

<div class="container">

<div class="nav-left">

<a class="nav-item">

<i class="fa fa-bars" aria-hidden="true"></i>

</a>

<router-link to="/" class="nav-item is-active">

Home

</router-link>

<a class="nav-item is-active">

<span class="tag is-rounded">Films</span>

</a>

<a class="nav-item is-active">

Shows

</a>

<a class="nav-item is-active">

Music

</a>

</div>

<div class="nav-right desktop">

<span class="nav-item">

<a class="title">

VueFlix

</a>

</span>

</div>

</div>

</header>

<div class="container description-container">

<div class="columns">

<div class="column is-three-quarters">

<h1 class="title">{{ selectedMovie.title }}</h1>

<h4 class="subtitle">

<p class="subtitle-tag">{{ selectedMovie.duration }}</p>

<p class="subtitle-tag">{{ selectedMovie.genre }}</p>

<p class="subtitle-tag">{{ selectedMovie.releaseDate }}</p>

</h4>

<p class="description">{{ selectedMovie.description }}</p>

<div class="links">

<router-link

:to="{path: ‘/‘ + $route.params.id + ‘/trailer‘}"

class="button play-button">

Play <i class="fa fa-play"></i>

</router-link>

</div>

</div>

</div>

</div>

</div>`,

}

我们已经建立了一个router-link组件在首页链接在导航栏用户直接回根路径(介绍电影的部分)。

我们引入了另一个router-link,在影片的播放按钮,创建一个目标位置的‘/‘ + $route.params.id + ‘/trailer‘。这基本上用/trailer扩展了电影ID的当前路径和预告片,是对我们最后的电影预告片组件的导航。

到目前为止,我们应用程序中的电影组件应该如下所示:

令人惊叹的。由于我们已经建立了一个合适的router-link来引导用户从电影到电影预告片,现在我们需要创建电影预告片组件和相应的动态路由。

电影预告片:

const MovieTrailer = {

template: `

<div class="trailer-body" style="background: #1e1d1d">

<div class="has-text-centered">

<div class="columns">

<div class="column vertical-align">

<iframe

allowFullScreen

frameborder="0"

height="376"

:src="trailerUrlPath"

style="width: 100%; min-width: 536px"

/>

</div>

</div>

</div>

</div>`,

data () {

return {

trailerUrlPath: movies[this.$route.params.id].trailerPath

}

}

}

我们用一个简单的iframe显示来自YouTube上的预告片。我们绑定iframe的src到组件的属性trailerUrlPath设置在data函数。简单的trailerUrlPath访问全局的movies,得到基于$route.params.id合适的电影信息的url。

伴随着动态路由:

const routes = [

{ path: ‘/‘, component: Intro },

{ path: ‘/:id‘, component: Movie },

{ path: ‘/:id/trailer‘, component: MovieTrailer }

]

我们的应用程序在这个时刻:

我们差不多完成了!我们只需要再解决一个简单的添加电影收藏和VueFlix那便是完整的。

添加到收藏夹

movies中的每一个电影对象都有一个favorite的布尔值。我们将使用这个触发器来表示一个电影是否被添加到收藏夹中。

关于视觉显示,我们将有两个视觉提示:

  • 电影组件周围的黄色框阴影
  • 通过一个黄色标记列表项在页脚部分

我们通过在我们已经预留的favorite-shadow和favourite-check类的建立来帮助我们做到这些。

.favorite-shadow {

box-shadow: 0 0 50px 15px rgba(251, 255, 15, 0.25);

}

.favorite-check {

position: absolute;

right: 5px;

top: 5px;

z-index: 1;

color: #fcff4c;

@media(max-width: $medium) {

position: initial;

display: block;

}

}

现在,我们需要在Movie组件模板和页脚部分中应用条件类绑定。我们还需要为Movie组件中的“添加到收藏夹”按钮创建事件处理程序。

我们的电影组件:

const Movie = {

template:

`<div :class="[{ ‘favorite-shadow‘: selectedMovie.favorite }, ‘hero-body‘]"

:style="{ ‘background-image‘: selectedMovie.largeImgSrc }">

<header class="nav">

...

</header>

<div class="container description-container">

...

...

...

<div class="links">

<router-link

:to="{path: ‘/‘ + $route.params.id + ‘/trailer‘}"

class="button play-button">

Play <i class="fa fa-play"></i>

</router-link>

<a

class="button is-link favorites-button"

@click="addToFavorites">

<span

:class="[{ ‘hide‘: selectedMovie.favorite }]">

Add to

</span>

<span

:class="[{ ‘hide‘: !selectedMovie.favorite }]">

Remove from

</span>

&nbsp;favorites

<i class="fa fa-plus-square-o"></i>

</a>

</div>

</div>

</div>`,

data() {

...

},

watch: {

},

methods: {

selectMovie() {

...

},

addToFavorites() {

movies[this.$route.params.id].favorite =

!movies[this.$route.params.id].favorite

}

}

结合上述的类favorite-shadow是由selectedMovie.favorite布尔值来确定而hero-body应该永远存在。

我们还推出了‘添加/删除‘从收藏夹按钮后,原来的播放按钮。“添加到收藏夹”按钮从addToFavorites()方法处理简单的切换即当单击某一部电影的favorite时,文本之间切换“添加”和“删除”基于电影是否已添加或删除收藏夹(hide类是创建类设置display:none),没有就添加有就删除。

类似地,我们还需要在页脚中引入检查标记的条件类绑定:

<div id="app">

<section class="hero is-primary is-medium">

<router-view></router-view>

<div class="hero-foot">

<div class="columns is-mobile">

<div v-for="movieChoice in movieChoices" class="column">

<router-link :to="`/${movieChoice.id}`"

tag="li"

class="movie-choice">

<i :class="[{ ‘fa fa-check-circle favorite-check‘: movieChoice.favorite }]"></i>

<img :src="`${movieChoice.smallImgSrc}`" class="desktop"/>

<p class="mobile movie-title">{{ movieChoice.subtitle }}</p>

</router-link>

</div>

</div>

</div>

</section>

</div>

现在我们应该能够把电影添加到我们的收藏夹列表中!

干杯!

谢谢你花时间来看这篇文章。这是我我们一起打破瓶颈和进一步了解vue.js概念的共同的一种锻炼,所以我希望这是有益的,你学到的东西了吗?

如果你有任何问题/意见,我会很高兴听到的!

本文原作者Hassan Djirdeh,由本博主翻译,本博主的很多vue.js教程也可以到这里

时间: 2024-10-14 00:56:01

用Vue.js来开发一个电影App的前端部分的相关文章

vue.js应用开发笔记

看vue.js有几天了,之前也零零散散的瞅过,不过一直没有动手去写过demo,这几天后台事比较少,一直在讨论各种需求(其实公司对需求还是比较重视与严谨的,一个项目需求讨论就差不多一周了,这要搁之前,天哪...),于是就琢磨着把vue简单的过下,如下所讲只是个人一些理解,不到的地方还望园友指正,涉及到的东西有vue.vue-router.vuex.axios以及nodejs一些后台东西,废话不说了直接上菜吧. 一.vue.js 1.项目搭建使用vue-cli脚手架,首先必须安装vue.vue-cl

用 Vue.js 实现了一个 V2EX 克隆项目

用 Vue.js 实现了一个 V2EX 克隆项目 Demo 项目 Demo 请访问: http://v2ex.liuzhen.me/ 项目代码: https://github.com/liuzhenangel/v2ex_frontend 项目介绍 v2ex_frontend 项目是一个利用 vue.js 和 v2ex api 实现的 V2EX 社区克隆项目, 主要目的是为了学习 vue.js, 很适合学习 vue.js 的朋友参考. 这是一个前后端分离项目, 前端主要是 vue.js 和 vue

关于Vue.js和Vue.js的优缺点以及和与其他前端框架的区别

首先,我们先了解什么是MVX框架模式? MVX框架模式:MVC+MVP+MVVM 1.MVC:Model(模型)+View(视图)+controller(控制器),主要是基于分层的目的,让彼此的职责分开. View通过Controller来和Model联系,Controller是View和Model的协调者,View和Model不直接联系,基本联系都是单向的. 用户User通过控制器Controller来操作模板Model从而达到视图View的变化. 2.MVP:是从MVC模式演变而来的,都是通

基于webpack和vue.js搭建开发环境

前言 在对着产品高举中指怒发心中之愤后,真正能够解决问题的是自身上的改变,有句话说的好:你虽然改变不了全世界,但是你有机会改变你自己.秉承着“不听老人言,吃亏在眼前”的优良作风,我还是决定玩火自焚. 问题所在 之前的项目总结为以下内容: 1.AMD模块规范开发,使用requirejs实现,使用rjs打包,最终导致的结果是,输出的项目臃肿,肿的就像一坨狗不理……不忍直视2.使用gulp进行打包,这一点貌似没有可吐槽的地方,毕竟都是被grunt折磨过来的……3.数据的渲染使用模板引擎,这就意味着你要

Vue.js实践:一个Node.js+mongoDB+Vue.js的博客内容管理系统

项目来源 以前曾用过WordPress搭建自己的博客网站,但感觉WordPress很是臃肿.所以一直想自己写一个博客内容管理器. 正好近日看完了Vue各个插件的文档,就用着Vue尝试写了这个简约的博客内容管理器(CMS). 嗯,我想完成的功能: 一个基本的博客内容管理器功能,如后台登陆,发布并管理文章等 支持markdown语法实时编辑 支持代码高亮 管理博客页面的链接 博客页面对移动端适配优化 账户管理(修改密码) Demo 登陆后台按钮在页面最下方"站长登陆",可以以游客身份登入后

django2.0 + python37 + vue 实战 网站开发,安卓app生成

最近接到一个需求,要做一个安卓app,这个app就一个功能,查看说说 要求是查询的信息要记录下来,还要有账号权限控制 理了下需求: 1 安卓app提供查询功能 2 记录查询的信息(用户,查询qq,查询结果) 3 权限控制(控制用户是否可以查询,有效期等) 看了下需求后,发现使用django来做网站后台非常方便快速,还提供了账号权限系统,另外安卓app开发并非我本行,正好之前一阵子研究过node 发现node可以直接将网页打包成app,反正这个app功能也就那么几个,用web来做app就行了,这个

beego+vue.js分离开发,结合发布,简单部署

大家知道,golang开发的东西部署简单是它很大的卖点,一般的应用,生成的可执行文件直接放服务器上运行即可,不需要任何环境.当然,大型的应用才需要比如mysql,nginx等.但是当vue.js出现后,前端几乎都是它(vue.js)的天下了,因为用了vue.js就回不去了,无法再回到beego的view里写tpl或html页面了,很受伤,没办法,vue.js实在太方便了,所谓双向绑定,这是神马概念,百度吧,我也讲不清楚-- 之前很懵逼,将vue.js开发的前端,完完全全独立地部署在云上(用ngi

开发一个直播APP多少钱?

近几年,国内直播行业发展火爆,从个人秀场到游戏直播,从UGC走向PUGC,愈来愈多的企业开始涉足直播APP,包括当下最火的短视频.小视频APP,也纷纷加入直播功能,以增加用户粘性及变现能力.那么一直备受关注的直播类APP的背后开发逻辑如何呢?做一个直播APP多少钱呢?下面,就让我们从几个方面一一进行了面解. 我们当下常用的视频直播APP软件项目为例,除了APP直播后台开发人员要把产品需求变成一个实际操作的直播APP.另一方面直播app运维人员也会根据企业开发应用支持的手机设备而有所调整,因此直播

Vue.js组件开发:从一个简单的例子说起

<ol> <!-- <li v-for="thing in someThings" > {{ thing.name }} </li> --> <thing-liv-for="thing in someThings" v-bind:something="thing"></thing-li> </ol> li列表的功能是显示一件事情的名字,我们以它作为例子来介绍最简单