使用 Vue Composition API 构建电影搜索应用

2025-05-27

使用 Vue Composition API 构建电影搜索应用

Vue 3 的首个Alpha 版本发布了!Vue 3 带来了许多激动人心的功能:Vue 在新的 Composition API 背后公开了其响应式系统。如果您还没有听说过它,我建议您阅读描述它的 RFC。起初我有点怀疑,但看到 React 的 Hooks API 与之类似,我决定尝试一下。

在本文中,我们将使用 Composition API 构建一个电影搜索应用程序。我们不会使用基于对象的组件。我将解释新 API 的工作原理以及如何构建应用程序。

当我们完成后,我们将看到类似这样的内容:

最终应用

该应用程序将能够通过开放电影数据库 API搜索电影并呈现结果。构建此应用程序的原因是,它足够简单,不会分散学习新 API 的注意力,但又足够复杂,能够展示其有效性。

如果您对解释不感兴趣,您可以直接查看源代码最终应用程序

设置项目

对于本教程,我们将使用 Vue CLI,它可以快速生成必要的环境。

npm install -g @vue/cli
vue create movie-search-vue
cd movie-search-vue
npm run serve
Enter fullscreen mode Exit fullscreen mode

我们的应用程序现在在http://localhost:8080上运行,如下所示:

生成的应用程序

您可以在此处看到默认文件夹结构:

默认文件夹结构

如果你不想在本地电脑上安装所有依赖项,也可以在Codesandbox上启动项目。Codesandbox 为大多数主流框架(包括 Vue)提供了完美的启动项目。

启用新 API

生成的源代码使用的是 Vue 2 和旧 API。要使用 Vue 2 的新 API,我们必须安装Composition 插件

npm install @vue/composition-api
Enter fullscreen mode Exit fullscreen mode

安装后,我们必须将其添加为插件:

import Vue from 'vue';
import VueCompositionApi from '@vue/composition-api';

Vue.use(VueCompositionApi);
Enter fullscreen mode Exit fullscreen mode

组合插件是附加的:您仍然可以按照旧的方式创建和使用组件,并开始使用 Composition API 创建新的组件。

我们将有四个部分:

  • App.vue:父组件。它将处理 API 调用并与其他组件通信。
  • Header.vue:接收并显示页面标题的基础组件
  • Movie.vue:渲染每部电影。电影对象作为属性传递。
  • Search.vue:包含一个带有输入元素和搜索按钮的表单。提交表单时,它会将搜索词传递给应用组件。

创建组件

让我们编写第一个组件,即标题:

<template>
  <header class="App-header">
    <h2>{{ title }}</h2>
  </header>
</template>

<script>
  export default {
    name: 'Header',
    props: ['title'],
    setup() {}
  }
</script>
Enter fullscreen mode Exit fullscreen mode

组件的props声明方式相同。您可以将期望从父组件获取的变量命名为数组或对象。这些变量将在 template( {{ title }}) 和setup方法中可用。

这里新增的是setup方法。它在初始props解析后运行。该setup方法可以返回一个对象,该对象的属性将被合并到模板上下文中:这意味着它们将在模板中可用。返回的对象也是放置生命周期回调的地方。我们将在搜索组件中看到相关示例。

让我们看一下搜索组件:

<template>
  <form class="search">
    <input
       type="text"
       :value="movieTitle"
       @keyup="handleChange"
    />
    <input @click="handleSubmit" type="submit" value="SEARCH" />
  </form>
</template>

<script>
  import { ref } from '@vue/composition-api';

  export default {
    name: 'Search',
    props: ['search'],
    setup({ search }, { emit }) {
      const movieTitle = ref(search);

      return {
        movieTitle,
        handleSubmit(event) {
          event.preventDefault();
          emit('search', movieTitle.value);
        },
        handleChange(event) {
          movieTitle.value = event.target.value
        }
      }
    }
  };
</script>
Enter fullscreen mode Exit fullscreen mode

搜索组件会跟踪键盘输入,并将输入值存储在一个变量中。当我们完成输入并点击提交按钮后,它会将当前搜索词向上发送到父组件。

setup方法有两个参数。

第一个参数被解析props为一个命名对象。你可以使用对象解构来访问其属性。该参数是响应式的,这意味着setup当输入属性发生变化时,函数将再次运行。

this第二个参数是上下文对象。您可以在这里找到2.x API 中可用的属性列表( attrsslotsparentrootemit)。

这里的下一个新元素是ref函数。该ref函数暴露了 Vue 的响应式系统。调用时,它会创建一个具有单个属性的响应式可变变量value。该value属性将具有传递给函数的参数的值ref。它是对原始值的响应式包装器。在模板内部,我们无需引用该value属性,Vue 会为我们解开它。如果我们传入一个对象,它将具有高度的响应性。

反应式意味着当我们修改对象的值(在我们的例子中是value属性)时,Vue 会知道该值已经改变,并且它需要重新渲染连接的模板并重新运行监视的函数。

它的作用类似于从data方法返回的对象属性。

data: function() {
  return { movieTitle: 'Joker' };
}
Enter fullscreen mode Exit fullscreen mode

粘合

下一步是引入 Header 和 Search 组件的父组件,即 App 组件。它监听来自 Search 组件的搜索事件,在搜索词发生变化时运行 API,并将找到的电影传递给 Movie 组件列表。

<template>
  <div class="App">
    <Header :title="'Composition API'" />
    <Search :search="state.search" @search="handleSearch" />
    <p class="App-intro">Sharing a few of our favourite movies</p>
    <div class="movies">
      <Movie v-for="movie in state.movies" :movie="movie" :key="movie.imdbID" />
    </div>
  </div>
</template>

<script>
  import { reactive, watch } from '@vue/composition-api';
  import Header from './Header.vue';
  import Search from './Search.vue';
  import Movie from './Movie.vue';

  const API_KEY = 'a5549d08';

  export default {
    name: 'app',
    components: {
      Header, Search, Movie
    },
    setup() {
      const state = reactive({
        search: 'Joker',
        loading: true,
        movies: [],
        errorMessage: null
      });

      watch(() => {
        const MOVIE_API_URL = `https://www.omdbapi.com/?s=${state.search}&apikey=${API_KEY}`;

        fetch(MOVIE_API_URL)
          .then(response => response.json())
          .then(jsonResponse => {
            state.movies = jsonResponse.Search;
            state.loading = false;
          });
      });

      return {
        state,
        handleSearch(searchTerm) {
          state.loading = true;
          state.search = searchTerm;
        }
      };
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

我们在这里引入两个新元素:reactivewatch

reactive函数相当于 Vue 2 中的Vue.observable()
它使传递的对象具有高度的响应性:获取原始对象并用代理(基于 ES2015 代理的实现)包装它。在从 返回的对象上,我们可以直接访问属性,而不是在需要使用属性时reactive从函数返回的值。如果您想在 Vue 2.x API 中搜索等效方法,该方法将完全匹配。refvaluedata

该对象的一个​​缺点reactive是我们不能将它传播到从setup方法返回的对象中。

watch函数需要一个函数作为参数。它会跟踪内部的反应变量,就像组件为模板所做的那样。当我们修改传入函数内部的反应变量时,给定的函数会再次运行。在我们的示例中,每当搜索词发生变化时,它都会获取与搜索词匹配的电影。

剩下一个组件,用于显示每条电影记录:

<template>
  <div class="movie">
    <h2>{{ movie.Title }}</h2>
    <div>
      <img width="200" :alt="altText" :src="movie.Poster" />
    </div>
    <p>{{ movie.Year }}</p>
  </div>
</template>

<script>
  import { computed } from '@vue/composition-api';

  export default {
    name: "Movie",
    props: ['movie'],
    setup({ movie }) {
      const altText = computed(() => `The movie titled: ${movie.Title}`);

      return { altText };
    }
  };
</script>
Enter fullscreen mode Exit fullscreen mode

Movie 组件接收要显示的电影,并将其名称与图片一起打印出来。令人兴奋的是,对于alt图片字段,我们使用了基于标题计算的文本。

computed函数获取一个 getter 函数,并将返回的变量包装成一个响应式函数。返回的变量与函数返回的变量具有相同的接口ref。区别在于它是只读的。当 getter 函数中的某个响应式变量发生变化时,getter 函数将再次运行。如果computed函数返回的是未包装的原始值,则模板将无法跟踪依赖项的变化。

清理组件

目前,App 组件内部有很多业务逻辑。它主要负责两件事:处理 API 调用及其子组件。我们的目标是让每个对象只负责一项职责:App 组件应该只管理组件本身,而不应该处理 API 调用。为了实现这一点,我们必须将 API 调用提取出来。

import { reactive, watch } from '@vue/composition-api';
const API_KEY = 'a5549d08';

export const useMovieApi = () => {
  const state = reactive({
    search: 'Joker',
    loading: true,
    movies: []
  });

  watch(() => {
    const MOVIE_API_URL = `https://www.omdbapi.com/?s=${state.search}&apikey=${API_KEY}`;

    fetch(MOVIE_API_URL)
      .then(response => response.json())
      .then(jsonResponse => {
        state.movies = jsonResponse.Search;
        state.loading = false;
      });
  });

  return state;
};
Enter fullscreen mode Exit fullscreen mode

现在 App 组件缩小仅用于处理与视图相关的操作:

import Header from './Header.vue';
import Search from './Search.vue';
import Movie from './Movie.vue';
import { useMovieApi } from '../hooks/movie-api';

export default {
  name: 'app',
  components: { Header, Search, Movie },
  setup() {
    const state = useMovieApi();

    return {
      state,
      handleSearch(searchTerm) {
        state.loading = true;
        state.search = searchTerm;
      }
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

就是这样;我们用新的 Composition API 完成了一个小应用程序的实现。

总结

自从使用 Vue CLI 生成项目以来,我们已经取得了很大进展。让我们总结一下我们学到的东西。

我们可以将新的 Composition API 与当前稳定的 Vue 2 版本一起使用。为此,我们必须使用@vue/composition-api插件。该 API 是可扩展的:我们可以使用新 API 创建新组件,同时创建旧组件,而现有组件仍可像以前一样工作。

Vue 3 将引入许多不同的功能:

  • setup:驻留在组件上,并将协调组件的逻辑,在初始props解析后运行,接收props上下文作为参数
  • ref:返回一个反应变量,在改变时触发模板的重新渲染,我们可以通过value属性来操作它的值。
  • reactive:返回一个反应对象(基于代理),在反应变量改变时触发模板的重新渲染,我们可以在不使用value属性的情况下修改其值
  • computed:根据 getter 函数参数返回一个反应变量,跟踪反应变量的变化并在变化时重新评估
  • watch:根据提供的功能处理副作用,跟踪反应变量的变化并在变化时重新运行

我希望这个例子能让您熟悉新的 API,并消除您的怀疑,就像我一样。

文章来源:https://dev.to/sonicoder/build-a-movie-search-app-using-the-vue-composition-api-5218
PREV
让你大吃一惊的在线 Javascript 游乐场
NEXT
使用这个简短的 CSS 技巧为您的网站添加暗模式