You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Vue组件mounted周期获取Watcher更新的prev与curr值?

问题说明

我需要在Vue组件的mounted生命周期钩子中使用经Watcher更新后的this.prev和this.curr值,但目前在mounted里无法获取到这些最新值。尝试将mounted中的逻辑移至Watcher时,动画仅能显示一秒,因此必须在mounted周期内处理该逻辑。

组件代码如下:

<template>
  <component
    :is="'nuxt-link'"
    :to="{ path: slug, query: { scroll: 'false' } }"
    :class="linkClasses"
    v-on:click.native="getSelectedFacet($event.target.textContent)"
  >
    <slot></slot>
  </component>
</template>

<script>
export default {
  inject: ["filterVariantState"],

  props: {
    facetKey: {
      type: String,
      required: true,
    },

    facet: {
      type: String | undefined,
      required: true,
    },

    selectedFacets: {
      type: Object,
      required: true,
    },

    selectedFacet: {
      type: String,
      required: false,
    },

    results: {
      type: Array,
      default() {
        return [];
      },
    },
  },

  data() {
    return {
      trackFacets: [],
      showAnimation: false,
      updatedFacet: null,
      updatedFacetKey: null,
      prev: {
        attributes: [],
        name: "",
      },
      curr: {
        attributes: [],
        name: "",
      },
    };
  },

  computed: {
    // Get data object from store
    dataObj() {
      return this.$store.getters["page/data"];
    },

    // Get Previous and Current Facets from store
    storedFacets() {
      return this.$store.getters["page/facets"];
    },

    // Get Selected Facets
    storedSelectedFacet() {
      return this.$store.getters["page/selectedFacet"];
    },

    facets() {
      let currentFacet = {};
      currentFacet[this.facetKey] = this.facet;

      return Object.assign({}, this.selectedFacets, currentFacet);
    },

    // Check if product is availble
    productIsAvailable() {
      return this.productAvailability ? true : false;
    },

    // Computes the slug of each facet
    slug() {
      let result;

      if (!this.filterVariantState.clearAll && this.results.length > 2) {
        result = this.results.find((x) => {
          return Object.keys(this.facets).every((facet) => {
            return (x[facet] ? x[facet].raw : null) === this.facets[facet];
          });
        });
      }

      let productSlug = result ? result.slug.raw : "";

      if (productSlug === "") {
        result = this.results.find(
          (x) =>
            x[this.facetKey] &&
            x[this.facetKey].raw === this.facets[this.facetKey]
        );
      }

      return result ? result.slug.raw : "";
    },

    // Computes product availability
    productAvailability() {
      let result =
        !this.filterVariantState.clearAll && this.results.length > 2
          ? this.results.find((x) => {
              return Object.keys(this.facets).every((facet) => {
                return (x[facet] ? x[facet].raw : null) === this.facets[facet];
              });
            })
          : this.results.find(
              (x) =>
                x[this.facetKey] &&
                x[this.facetKey].raw === this.facets[this.facetKey]
            );

 
      return result ? result.slug.raw : "";
    },

    // Current selected variant
    currentVariant() {
      return this.selectedFacet === this.facet;
    },

    // Link Classes
    linkClasses() {
      return {
        "bg-gray-100 text-gray-400": !this.productIsAvailable,
        "text-gray-900": this.productIsAvailable,
        pulse: this.showAnimation && this.currentVariant,
      };
    },

    animationIsTrue() {
      return showAnimation ? true : false;
    },
  },

  methods: {
    // Get facetKey from value // Adjust styling for styling detector for storage as it entered into the backend off
    getKeyByValue(object, value) {
      return Object.keys(object).find((key) =>
        key === "storage"
          ? object[key].replaceAll(" ", "") === value
          : object[key] === value
      );
    },

    // This method gets you the facetKey that is NOT changed when you click on a faded option and your selection has been changed
    getUnchangedKey() {
      // Get two arrays of facetKeys
      let result = this.storedFacets.map((value) =>
        value.attributes.map((attribute) =>
          this.getKeyByValue(this.dataObj.attributes, attribute)
        )
      );

      // Need the last two results to continue
      if (result.length === 2) {
        // Remove sim and network from the arrays
        result[0] = result[0].filter(
          (str) => str !== "sim" && str !== "network"
        );
        result[1] = result[1].filter(
          (str) => str !== "sim" && str !== "network"
        );

        let unchangedKey = [];

        // Compare the two arrays, if there is a matching facetKey, it means it has not changed and so we will not fire the text for that facetKey
        for (let i = 0; i < result[0].length; i++) {
          for (let j = 0; j < result[1].length; j++) {
            if (result[0][i] === result[1][j]) {
              unchangedKey.push(result[0][i]);
            }
          }
        }

        return unchangedKey ? unchangedKey : "";
      }
    },

    // Get selected facet value on click
    getSelectedFacet(value) {
      // Set the value found in the $event click
      this.updatedFacet = value.trim().toLowerCase();

      // Dispatch the getSelectedFacets action and pass through the facet value that has just been selected/clicked
      this.$store.dispatch("page/getSelectedFacet", this.updatedFacet);
    },
  },

  watch: {
    dataObj(newVal, oldVal) {
      // this.fetchFacets();
      this.prev = {
        attributes: Object.values(oldVal.attributes),
        name: this.dataObj.name,
      };
      this.curr = {
        attributes: Object.values(newVal.attributes),
        name: this.dataObj.name,
      };
    },
  },

  mounted() {
    // Conditionally show pulse animation
    let n = 1;
    let queue = [];
    // let namesQueue = [];

    // Loop through both arrays and join them create one array
    for (let i = 1; i <= n; i++) {
      let prevFacets = this.prev.attributes;
      let currFacets = this.curr.attributes;

      // let prevProductName = this.prev.name;
      // let currProductName = this.curr.name;

      console.log("prev", this.prev.attributes);
      console.log("curr", this.curr.attributes);

      // namesQueue.push(prevProductName, currProductName);

      // Loop through prev facets
      for (let j = 0; j < prevFacets.length; j++) {
        queue.push(prevFacets[j]);
      }

      // Loop through current facets
      for (let k = 0; k < currFacets.length; k++) {
        queue.push(currFacets[k]);
      }
    }

    // Loop through joint array and check for no duplicates
    for (let i = 0; i < queue.length; i++) {
      for (let j = i + 1; j < queue.length; j++) {
        if (queue[i] === queue[j]) {
          queue.splice(i, 1); // Remove the element at index i
          queue.splice(j - 1, 1); // Remove the element at index j (adjusted after removing i)
          i--; // Adjust i since the array has been modified
          break; // Restart checking from the new i position
        }
      }
    }

    // The queue removes any duplicates
    // So, if there is only pair of facets left (length === 2), then turn off the animation as only the selected facet has changed
    if (queue.length === 2) {
      this.showAnimation = false;

      // Else if, they are multiple pairs of changes and the product is still the same, then turn on the animation as the facet options have changed, and also, alert the changing text
    } else if (queue.length > 2) {
      this.showAnimation = true;

      // Conditionally show updated label

      // Find key based on the value of the selected facet found in storedSelectedFacets
      this.updatedFacetKey = this.getKeyByValue(
        this.dataObj.attributes,
        this.storedSelectedFacet
      );

      // Pass the facet key to ProductVariantFilter to trigger the "update" label
      this.$emit("updatedFacetOptions", {
        facetKey: this.updatedFacetKey,
        // Get unchanged facetKey, if there is one
        unchangedFacetKey: this.getUnchangedKey(),
      });
    }
  },
};
</script>
解决方案

核心问题分析

mounted执行时,Watcher监听的dataObj可能还未完成更新(比如从store异步获取数据),导致this.prev和this.curr还是初始空值;而将逻辑移到Watcher后,每次dataObj更新都会重新执行逻辑,可能导致动画状态被频繁重置,只显示一秒。

方法1:用$nextTick延迟mounted逻辑执行

通过$nextTick等待Vue完成数据更新和DOM渲染后,再执行mounted内的逻辑:

mounted() {
  this.$nextTick(() => {
    // 原mounted内的所有逻辑移到这里
    let n = 1;
    let queue = [];

    // Loop through both arrays and join them create one array
    for (let i = 1; i <= n; i++) {
      let prevFacets = this.prev.attributes;
      let currFacets = this.curr.attributes;

      console.log("prev", this.prev.attributes);
      console.log("curr", this.curr.attributes);

      // Loop through prev facets
      for (let j = 0; j < prevFacets.length; j++) {
        queue.push(prevFacets[j]);
      }

      // Loop through current facets
      for (let k = 0; k < currFacets.length; k++) {
        queue.push(currFacets[k]);
      }
    }

    // 剩余逻辑不变...
    if (queue.length === 2) {
      this.showAnimation = false;
    } else if (queue.length > 2) {
      this.showAnimation = true;
      // ...其他逻辑
    }
  })
}

方法2:给Watcher添加immediate: true,拆分动画控制逻辑

让Watcher在组件挂载时立即执行一次,同时把动画逻辑抽成独立方法,避免重复代码,并手动控制动画显示时长:

watch: {
  dataObj: {
    handler(newVal, oldVal) {
      this.prev = {
        attributes: Object.values(oldVal.attributes),
        name: this.dataObj.name,
      };
      this.curr = {
        attributes: Object.values(newVal.attributes),
        name: this.dataObj.name,
      };
      // 调用动画处理方法
      this.handleAnimation();
    },
    immediate: true // 组件挂载时立即执行
  }
},
methods: {
  // ...原有方法不变
  handleAnimation() {
    let n = 1;
    let queue = [];
    // 原mounted内的逻辑移到这里
    // ...

    if (queue.length === 2) {
      this.showAnimation = false;
    } else if (queue.length > 2) {
      this.showAnimation = true;
      // 设置动画显示时长,比如3秒后自动关闭
      setTimeout(() => {
        this.showAnimation = false;
      }, 3000);
      // ...其他逻辑
    }
  }
},
mounted() {
  // 确保挂载后再次执行一次(可选,视情况而定)
  this.$nextTick(() => {
    this.handleAnimation();
  })
}

方法3:监听prev和curr的变化

直接监听prev和curr的深层变化,当两者都更新后再执行动画逻辑:

watch: {
  dataObj(newVal, oldVal) {
    this.prev = {
      attributes: Object.values(oldVal.attributes),
      name: this.dataObj.name,
    };
    this.curr = {
      attributes: Object.values(newVal.attributes),
      name: this.dataObj.name,
    };
  },
  // 监听prev的深层变化
  prev: {
    handler() {
      this.checkAndRunAnimation();
    },
    deep: true
  },
  // 监听curr的深层变化
  curr: {
    handler() {
      this.checkAndRunAnimation();
    },
    deep: true
  }
},
methods: {
  // ...原有方法不变
  checkAndRunAnimation() {
    // 确保prev和curr都有数据
    if (!this.prev.attributes.length || !this.curr.attributes.length) return;
    
    let n = 1;
    let queue = [];
    // 原mounted内的逻辑移到这里
    // ...

    if (queue.length === 2) {
      this.showAnimation = false;
    } else if (queue.length > 2) {
      this.showAnimation = true;
      // ...其他逻辑
    }
  }
},
mounted() {
  // 初始化检查
  this.checkAndRunAnimation();
}

内容的提问来源于stack exchange,提问作者user26893186

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 12:10:57