“在这个Vue 3和TypeScript应用中,是什么导致了切片不是一个函数的错误?”

huangapple go评论99阅读模式
英文:

What causes the slice is not a function error in this Vue 3 and TypeScript app?

问题

Here's the translated code part:

  1. 我一直在使用**Vue 3**、**TypeScript** **The Movie Database (TMDB) API** 来开发单页面应用。
  2. `src\components\MovieDetails.vue` 中有以下代码:
  3. <template>
  4. <div class="row">
  5. <div class="col-sm-4 col-md-3">
  6. <div class="poster-container text-center text-sm-start my-3">
  7. <img v-if="movie"
  8. :src="genericPoster"
  9. :alt="movie?.title"
  10. class="img-fluid shadow-sm"
  11. />
  12. </div>
  13. </div>
  14. <div class="col-sm-8 col-md-9">
  15. <h1 class="movie-title mt-4">{{ movie?.original_title }}</h1>
  16. <p v-if="movie?.genres.length">
  17. <strong class="pe-1">Genres:</strong>
  18. <span
  19. class="badge bg-secondary"
  20. v-for="genre in movie?.genres"
  21. :key="genre.id"
  22. >{{ genre.name }}</span
  23. >
  24. </p>
  25. <p v-if="movie?.vote_average" class="user-score">
  26. <strong>User Score:</strong>
  27. <span class="score" :class="movieQuality">{{ Number(movie?.vote_average).toFixed(2) }}</span>
  28. </p>
  29. <div v-if="movie?.overview">
  30. <h2 class="section-title">Overview</h2>
  31. <p>{{ movie.overview }}</p>
  32. </div>
  33. <div v-if="movieTrailers.length" class="mb-3">
  34. <h2 class="section-title">Trailers</h2>
  35. <TrailerCarousel :movieTrailers="movieTrailers" />
  36. </div>
  37. <div v-if="movieCast.length" class="cast-container">
  38. <h2 class="section-title">Cast</h2>
  39. <div class="row">
  40. <div
  41. v-for="actor in movieCast.slice(0, 12)"
  42. :key="actor['id']"
  43. class="col-sm-3"
  44. >
  45. <ActorCard :actor="actor" />
  46. </div>
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. </template>
  52. <script lang="ts">
  53. import { defineComponent } from "vue";
  54. import { useRoute } from "vue-router";
  55. import env from "../env";
  56. import { Movie } from "@/models/Movie";
  57. import { Trailer } from "@/models/Trailer";
  58. import { MovieCast } from "@/models/MovieCast";
  59. import TrailerCarousel from "./TrailerCarousel.vue";
  60. import ActorCard from "./ActorCard.vue";
  61. export default defineComponent({
  62. name: "MovieDetails",
  63. components: {
  64. TrailerCarousel,
  65. ActorCard,
  66. },
  67. data: () => ({
  68. route: useRoute(),
  69. genericPosterBig: require('../assets/generic-poster.png'),
  70. movie: null as Movie | null,
  71. movieQuality: String as unknown | 'bad',
  72. movieCast: Array as unknown | MovieCast[],
  73. movieTrailers: Array as unknown | Trailer[],
  74. }),
  75. mounted() {
  76. this.getMovieDetails();
  77. this.getMovieTrailers();
  78. this.getMovieCast();
  79. },
  80. methods: {
  81. getMovieDetails() {
  82. this.$axios
  83. .get(
  84. `${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`
  85. )
  86. .then((response) => {
  87. this.movie = response.data;
  88. // 获取电影后,设置其质量
  89. this.setMovieQuality();
  90. })
  91. .catch((err) => console.log(err));
  92. },
  93. setMovieQuality() {
  94. if (Number(this.movie?.vote_average) >= 7) {
  95. this.movieQuality = 'good';
  96. } else if(Number(this.movie?.vote_average) < 7 && Number(this.movie?.vote_average) > 5.5) {
  97. this.movieQuality = 'average';
  98. } else {
  99. this.movieQuality = 'bad';
  100. }
  101. },
  102. getMovieTrailers() {
  103. this.$axios
  104. .get(
  105. `${env.api_url}/movie/${this.route.params.id}/videos?api_key=${env.api_key}`
  106. )
  107. .then((response) => {
  108. this.movieTrailers = response.data.results;
  109. })
  110. .catch((err) => console.log(err));
  111. },
  112. getMovieCast() {
  113. this.$axios
  114. .get(
  115. `${env.api_url}/movie/${this.route.params.id}/credits?api_key=${env.api_key}`
  116. )
  117. .then((response) => {
  118. this.movieCast = response.data.cast;
  119. })
  120. .catch((err) => console.log(err));
  121. },
  122. },
  123. computed: {
  124. genericPoster() {
  125. return !this.movie?.poster_path
  126. ? this.genericPosterBig
  127. : `https://image.tmdb.org/t/p/w500/${this.movie?.poster_path}`
  128. },
  129. },
  130. watch: {
  131. "$route.params.id"() {
  132. this.getMovieDetails();
  133. this.getMovieTrailers();
  134. this.getMovieCast();
  135. },
  136. },
  137. });
  138. </script>
  139. `src\models\MovieCast.ts` 中:
  140. export interface MovieCast {
  141. adult?: boolean;
  142. character?: string;
  143. name?: string;
  144. profile_path?: string;
  145. }
  146. 而不是这两行代码:
  147. movieCast: Array as unknown | MovieCast[]
  148. movieTrailers: Array as unknown | Trailer[]
  149. 我*曾经*这样写过:
  150. movieTrailers: [],
  151. movieCast: [],
  152. 我做出了这个改变,以便我有一个更加**严格/强类型化**的应用程序。
  153. ### 问题
  154. 自从做出更改以来,控制台显示错误:
  155. &gt; TypeError: _ctx.movieCast.slice is not a function
  156. <div
  157. v-for="actor in movieCast.slice(0, 12)"
  158. :key="actor['id']"
  159. class="col-sm-3"
  160. >
  161. <ActorCard :actor="actor" />
  162. </div>
  163. 有一个**[Stackblitz](https://stackblitz.com/edit/vue3-typescript-vue-cli-starter-7y9c7v)**,其中包含工作中的应用程序。
  164. ### 问题
  165. 1. 我做错了什么?
  166. 2. 最可靠的解决方法是什么?

Please let me know if you need further assistance or have any questions.

英文:

I have been working on an SPA with Vue 3, TypeScript and The Movie Database (TMDB) API.

In src\components\MovieDetails.vue I have:

  1. &lt;template&gt;
  2. &lt;div class=&quot;row&quot;&gt;
  3. &lt;div class=&quot;col-sm-4 col-md-3&quot;&gt;
  4. &lt;div class=&quot;poster-container text-center text-sm-start my-3&quot;&gt;
  5. &lt;img v-if=&quot;movie&quot;
  6. :src=&quot;genericPoster&quot;
  7. :alt=&quot;movie?.title&quot;
  8. class=&quot;img-fluid shadow-sm&quot;
  9. /&gt;
  10. &lt;/div&gt;
  11. &lt;/div&gt;
  12. &lt;div class=&quot;col-sm-8 col-md-9&quot;&gt;
  13. &lt;h1 class=&quot;movie-title mt-4&quot;&gt;{{ movie?.original_title }}&lt;/h1&gt;
  14. &lt;p v-if=&quot;movie?.genres.length&quot;&gt;
  15. &lt;strong class=&quot;pe-1&quot;&gt;Genres:&lt;/strong&gt;
  16. &lt;span
  17. class=&quot;badge bg-secondary&quot;
  18. v-for=&quot;genre in movie?.genres&quot;
  19. :key=&quot;genre.id&quot;
  20. &gt;{{ genre.name }}&lt;/span
  21. &gt;
  22. &lt;/p&gt;
  23. &lt;p v-if=&quot;movie?.vote_average&quot; class=&quot;user-score&quot;&gt;
  24. &lt;strong&gt;User Score:&lt;/strong&gt;
  25. &lt;span class=&quot;score&quot; :class=&quot;movieQuality&quot;&gt;{{
  26. Number(movie?.vote_average).toFixed(2)
  27. }}&lt;/span&gt;
  28. &lt;/p&gt;
  29. &lt;div v-if=&quot;movie?.overview&quot;&gt;
  30. &lt;h2 class=&quot;section-title&quot;&gt;Overview&lt;/h2&gt;
  31. &lt;p&gt;{{ movie.overview }}&lt;/p&gt;
  32. &lt;/div&gt;
  33. &lt;div v-if=&quot;movieTrailers.length&quot; class=&quot;mb-3&quot;&gt;
  34. &lt;h2 class=&quot;section-title&quot;&gt;Trailers&lt;/h2&gt;
  35. &lt;TrailerCarousel :movieTrailers=&quot;movieTrailers&quot; /&gt;
  36. &lt;/div&gt;
  37. &lt;div v-if=&quot;movieCast.length&quot; class=&quot;cast-container&quot;&gt;
  38. &lt;h2 class=&quot;section-title&quot;&gt;Cast&lt;/h2&gt;
  39. &lt;div class=&quot;row&quot;&gt;
  40. &lt;div
  41. v-for=&quot;actor in movieCast.slice(0, 12)&quot;
  42. :key=&quot;actor[&#39;id&#39;]&quot;
  43. class=&quot;col-sm-3&quot;
  44. &gt;
  45. &lt;ActorCard :actor=&quot;actor&quot; /&gt;
  46. &lt;/div&gt;
  47. &lt;/div&gt;
  48. &lt;/div&gt;
  49. &lt;/div&gt;
  50. &lt;/div&gt;
  51. &lt;/template&gt;
  52. &lt;script lang=&quot;ts&quot;&gt;
  53. import { defineComponent } from &quot;vue&quot;;
  54. import { useRoute } from &quot;vue-router&quot;;
  55. import env from &quot;../env&quot;;
  56. import { Movie } from &quot;@/models/Movie&quot;;
  57. import { Trailer } from &quot;@/models/Trailer&quot;;
  58. import { MovieCast } from &quot;@/models/MovieCast&quot;;
  59. import TrailerCarousel from &quot;./TrailerCarousel.vue&quot;;
  60. import ActorCard from &quot;./ActorCard.vue&quot;;
  61. export default defineComponent({
  62. name: &quot;MovieDetails&quot;,
  63. components: {
  64. TrailerCarousel,
  65. ActorCard,
  66. },
  67. data: () =&gt; ({
  68. route: useRoute(),
  69. genericPosterBig: require(&#39;../assets/generic-poster.png&#39;),
  70. movie: null as Movie | null,
  71. movieQuality: String as unknown | &#39;bad&#39;,
  72. movieCast: Array as unknown | MovieCast[]
  73. movieTrailers: Array as unknown | Trailer[]
  74. }),
  75. mounted() {
  76. this.getMovieDetails();
  77. this.getMovieTrailers();
  78. this.getMovieCast();
  79. },
  80. methods: {
  81. getMovieDetails() {
  82. this.$axios
  83. .get(
  84. `${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`
  85. )
  86. .then((response) =&gt; {
  87. this.movie = response.data;
  88. // Once you get the movie, set its quality
  89. this.setMovieQuality();
  90. })
  91. .catch((err) =&gt; console.log(err));
  92. },
  93. setMovieQuality() {
  94. if (Number(this.movie?.vote_average) &gt;= 7) {
  95. this.movieQuality = &#39;good&#39;;
  96. } else if(Number(this.movie?.vote_average) &lt; 7 &amp;&amp; Number(this.movie?.vote_average) &gt; 5.5) {
  97. this.movieQuality = &#39;average&#39;;
  98. } else {
  99. this.movieQuality = &#39;bad&#39;;
  100. }
  101. },
  102. getMovieTrailers() {
  103. this.$axios
  104. .get(
  105. `${env.api_url}/movie/${this.route.params.id}/videos?api_key=${env.api_key}`
  106. )
  107. .then((response) =&gt; {
  108. this.movieTrailers = response.data.results;
  109. })
  110. .catch((err) =&gt; console.log(err));
  111. },
  112. getMovieCast() {
  113. this.$axios
  114. .get(
  115. `${env.api_url}/movie/${this.route.params.id}/credits?api_key=${env.api_key}`
  116. )
  117. .then((response) =&gt; {
  118. this.movieCast = response.data.cast;
  119. })
  120. .catch((err) =&gt; console.log(err));
  121. },
  122. },
  123. computed: {
  124. genericPoster() {
  125. return !this.movie?.poster_path
  126. ? this.genericPosterBig
  127. : `https://image.tmdb.org/t/p/w500/${this.movie?.poster_path}`
  128. },
  129. },
  130. watch: {
  131. &quot;$route.params.id&quot;() {
  132. this.getMovieDetails();
  133. this.getMovieTrailers();
  134. this.getMovieCast();
  135. },
  136. },
  137. });
  138. &lt;/script&gt;

In src\models\MovieCast.ts:

  1. export interface MovieCast {
  2. adult?: boolean;
  3. character?: string;
  4. name?: string;
  5. profile_path?: string;
  6. }

In stead of these 2 lines in the components

  1. movieCast: Array as unknown | MovieCast[]
  2. movieTrailers: Array as unknown | Trailer[]

I used to have:

  1. movieTrailers: [],
  2. movieCast: [],

I have made the change so that I would have a more strictly/strongly typed app.

The problem

Since the change, the console shows the error:

> TypeError: _ctx.movieCast.slice is not a function

  1. &lt;div
  2. v-for=&quot;actor in movieCast.slice(0, 12)&quot;
  3. :key=&quot;actor[&#39;id&#39;]&quot;
  4. class=&quot;col-sm-3&quot;
  5. &gt;
  6. &lt;ActorCard :actor=&quot;actor&quot; /&gt;
  7. &lt;/div&gt;

There is a Stackblitz too, with the working app.

Questions

  1. What am I doing wrong?
  2. What is the most reliable way to fix this issue?

答案1

得分: 1

If you are assigning a non-array, you will also get an error. Try logging the type, and see what is actually returned.

  1. console.log('movieCast', response.data.cast)
  2. this.movieCast = response.data.cast;

Use a conditional operator to prevent an error while your async value is undefined and calculating. You could also wrap the v-for in a v-if and check for undefined.

  1. v-for="actor in movieCast?.slice(0, 12)"
英文:

If you are assigning a non-array, you will also get an error. Try logging the type, and see what is actually returned.

  1. console.log(&#39;movieCast&#39;, response.data.cast)
  2. this.movieCast = response.data.cast;

Use a conditional operator to prevent an error while your async value is undefined and calculating. You could also wrap the v-for in a v-if and check for undefined.

  1. v-for=&quot;actor in movieCast?.slice(0, 12)&quot;

huangapple
  • 本文由 发表于 2023年5月25日 22:06:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76333211.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定