变量保留旧值

问题描述 投票:0回答:1

我的 Vue 应用程序遇到问题:当我单击某个产品时,会触发 navigationAndChange 函数,该函数会获取该产品的 ID 并将我带到包含该产品的页面。此页面上的某些元素包含该产品的图片,最后一个元素应该有一个“active”类,允许它有一个边框,这意味着最后一个图像显示在屏幕上。问题是,在实现 NavigationAndChange 函数时,imgBlocks 变量存储一个数组,其中的元素包含旧产品图像的值,当转换到具有另一个产品的页面时,我需要它更改此值,所以新产品图像的最后一个元素已采用“活跃”类别。怎么做?我提供下面的代码。

<script setup>
import { computed, ref, onMounted, nextTick } from "vue";
import { useProductStore } from "../stores/store.js";
import { useRouter, RouterLink } from "vue-router";
import btn from "./btn.vue";
import orderBtn from "./orderBtn.vue";

const router = useRouter();
const productStore = useProductStore();
let product = computed(() => productStore.getProduct);
const extraProducts = computed(() => productStore.getExtraProducts);


// change image

let activeImg = ref('');
activeImg.value = product?.value?.thumbnail;
let imgBlocks = ref([]);

onMounted(() => {
 imgBlocks.value = document.querySelectorAll('.img-good');
 let lastNum = imgBlocks.value.length - 1;
 let lastBlock = imgBlocks.value[lastNum];
 lastBlock.classList.add('active')
})


function changeImg(event, img) {
 activeImg.value = img;
 const imgBlock = event.target.closest('.img-good');

 if (imgBlock.classList.contains('active')) {
   imgBlock.classList.remove('active');
 } else {
   imgBlock.classList.add('active');
 }
 for (let i = 0; i < imgBlocks.value.length; i++) {
   if (imgBlocks.value[i] != imgBlock) {
     imgBlocks.value[i].classList.remove('active')
   }
 }
}


// change product 

function changeProduct(extraProduct) {
 productStore.id = extraProduct.id;
 localStorage.setItem("productStore", JSON.stringify(productStore));
 activeImg.value = extraProduct.thumbnail;
  // location.reload()
  //  console.log(imgBlocks.value);
 let images = document.querySelectorAll('.img-good');
 imgBlocks.value = images;
 console.log(imgBlocks.value);
}

function navigateAndChange(extraProduct) {
 // Execute the router navigation first
 router.push(`/productCard/${extraProduct.id}`);

 // Execute the changeProduct function after a delay
 setTimeout(() => {
  changeProduct(extraProduct);
 }, 100);
}

</script>

<template>
 <div>
  <section class="container products">
  
   <div class="products__info">
    <div class="products__info-img">
      <div class="img-gallery" ref="imgGallery">
        <div class="img-good" v-for="image in product.images" @click="changeImg($event, 
         image)" ref="imgBlocks">
          <img :src="image" alt="">
        </div>
      </div>
      <img :src="activeImg" alt="" class="main-img" />
    </div>
    <div class="products__info-description">
      <h3>{{ product.title }}</h3>
      <p>$ {{ product.price }}</p>
      <h4>Product description</h4>
      <p class="description">{{ product.description }}</p>
      <div class="products-order">
        <div class="order-btns">
          <orderBtn text="-" @click="decrease(product)"></orderBtn>
          <div class="ordered-quantity">
            {{ product.amount > 0 ? product.amount : 0 }}
          </div>
          <orderBtn text="+" @click="increase(product)"></orderBtn>
        </div>
        <RouterLink :to="`/shoppingCart`">
          <btn text="Buy NOW"></btn>
        </RouterLink>
        <div class="icon-like" @click="likeProduct($event, product)" :class="{ active: product.liked == true }"></div>
      </div>
    </div>
  </div>
  <div class="products__extra">
    <h2>You may be interested in</h2>
    <div class="products__extra-content">
      <div class="extra__item" v-for="extraProduct in extraProducts" :key="extraProduct.id">
        <div class="extra__item-img">
          <RouterLink :to="`/productCard/${extraProduct.id}`" @click="
            navigateAndChange(extraProduct)"></RouterLink>
          <img :src="extraProduct.thumbnail" alt="" />
          <div class="icons">
            <div class="icon-cart" @click="addOrDeleteProduct(extraProduct)" :class="{ active: extraProduct.amount }">
            </div>
            <div class="icon-like" @click="likeProduct($event, extraProduct)"
              :class="{ active: extraProduct.liked == true }"></div>
          </div>
          <h5>{{ Math.ceil(extraProduct.discountPercentage) }} % OFF</h5>
        </div>
        <div class="extra__item-description">
          <h4>{{ extraProduct.title }}</h4>
          <p>$ {{ extraProduct.price }}</p>
        </div>
      </div>
    </div>
  </div>
</section>
</div>
</template>
vuejs3 vue-router vue-composition-api
1个回答
0
投票

这里有很多“不太好”的代码。抱歉发了这么长的帖子。不过,我想解决您的代码的两个主要方面。更新产品信息,然后更新图像上的 active 类。公平警告,并非所有下面的代码对于您的实际应用程序来说实际上都是“正确的”,并且可能需要一定量的重构才能 100% 工作。请更多地将其视为示例代码。

更改产品页面

您可以使用更好的策略来改变产品。路由器推送后跟 setTimeout 延迟

changeProduct

函数调用并不是一个好习惯。这非常hacky。

鉴于这就是你推动的方式

router.push(`/productCard/${extraProduct.id}`);

我假设你有一个像这样定义的路线

{ path: '/productCard/:id', component: productComponent, props: true }

如果您没有
props: true

,您应该添加它。这样,

:id
参数将自动分配为组件中的 prop。

https://router.vuejs.org/guide/essentials/passing-props.html

props

设置为

true
时,
route.params
将被设置为组件道具。

现在,如果您在组件上定义一个 prop
id

,它将从路由 URL 接收

/:id
参数
const props = defineProps({
  id: {
    type: String, // URL param is always type String. You can cast to Number later if needed
    default: '1'
  }
})

现在
观看

此道具并调用更新商店和页面上的产品信息所需的所有其他功能。 watch( () => props.id, newId => { setNewProduct(newId) // updates store, product object, images array, etc. } )

更新图像
active

我可以看出对于如何使用你的 

imgBlocks

变量存在误解。在这里查看相关的div:

<div class="img-good" v-for="image in product.images" @click="changeImg($event, 
         image)" ref="imgBlocks">

添加 
ref="imgBlocks"

很好,但您没有在脚本代码中正确使用它。这称为“模板引用”。安装时,脚本代码中的

imgBlocks
将自动分配该 div 的物理 DOM 元素。这意味着像这样的代码imgBlocks.value = document.querySelectorAll('.img-good');是不必要的,可以删除(
document
接口几乎应该
从不
与Vue一起使用)。
实际上,我什至根本不会使用imgBlocks之类的东西。使用

类绑定

我们可以避免手动添加/删除类的麻烦,而是根据图像本身设置条件。

获取 product 信息后,将 Product.images 数组映射为图像 

objects

数组,其中一个属性称为

isActive
最初设置为 false,(如果您希望它已经处于活动状态,则第一个属性除外默认情况下)。那么也许你最终会得到一个看起来像这样的图像数组: product.images = [ { id: 0, src: '/foo.jpg', isActive: true }, { id: 1, src: '/bar.jpg', isActive: false }, { id: 2, src: '/baz.jpg', isActive: false } ]
现在更新模板代码以包含以下类绑定...

<!-- always include a unique key with v-for -->
<div
  v-for="image in product.images"
  :key="image.id"
  @click="changeImg(image)"
>
  <img :src="image.src" alt="" :class="{ active: image.isActive }">
</div>

仅当

active
 为 true 时,才会分配类别 
image.isActive

。因此,当单击图像时,

changeImg
函数应为刚刚单击的图像设置
isActive = true
,而对其他图像设置为 false。
function changeImg(image) {
  product.value.images.forEach(
    i => (i.isActive = i.id === image.id ? true : false)
  )
}
现在无论单击哪个图像都将处于活动状态。更改产品时,请始终将 

products.images
 重新映射为对象数组,并使用 
isActive

属性设置您想要的初始渲染方式。

codesandbox.io 示例
如果有帮助的话,我制作了一个 

codesandbox

,它以非常简化的方式实现了上述所有建议。就像我一开始所说的那样,我的代码示例更多的是为了说明更好的策略,而实际应用程序中的实现将需要重构以适应您的应用程序的设计和结构。

© www.soinside.com 2019 - 2024. All rights reserved.