Vue 3 组合 API 对象数据在应用过滤器后未更新
Vue 3 composition API object data not updating after apply the filter
我有一个产品列表的反应对象,我在我的模板中显示产品。现在我制作了一个过滤器,它通过以下方式过滤产品对象
对象的产品类型键。问题是当我应用过滤器时,它在控制台中显示正确的值,但在模板中,它不反映任何更改。在 vue 2 中,我使用了 this.$forceUpdate();
,它似乎对我有用。在 vue3 组合中做这件事的正确方法是什么 API?
我做错了什么,但不知道该怎么做。谢谢你在 advacne.
这里附上我的代码沙盒link
Code sandbox
<template>
<div>
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in productData"
:key="index"
>
{{ pata["name"] }}
</div>
<button @click="filterProductData()">FILTER</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
setup() {
const productTypeVal = 1;
let productData = reactive({
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
});
let productInitData = {
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
};
let filteredProducts = reactive({});
const filterProductData = () => {
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
filteredProducts[key] = productInitData[key];
}
});
productData = filteredProducts;
console.log(productData);
};
return {
productData,
productTypeVal,
filterProductData,
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
请看Vue.js Documentation: Limitations of reactive()
:
The reactive() API has two limitations:
- It only works for object types (objects, arrays, and collection types such as Map and Set). It cannot hold primitive types such as string, number or boolean.
- Since Vue's reactivity tracking works over property access, we must always keep the same reference to the reactive object. This means we can't easily "replace" a reactive object because the reactivity connection to the first reference is lost.
后一点是你的代码的问题:你没有改变这个对象的属性的状态,这是允许的,而是改变了变量引用的引用,我认为这个引用的重新分配是是什么导致反应性丧失。
因此,我认为您可以使用几种可能的解决方案,包括:
- 正在更改
productData
的实际对象属性。为此,您可能需要删除所有属性并从 productInitData
中删除它们 re-add 如果它们符合条件:
function myFilter() {
// remove all properties from productData
Object.keys(productData).forEach((key) => delete productData[key]);
// re-add properties that we want to display
Object.keys(productInitData).forEach((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
productData[key] = productInitData[key];
}
});
}
- 使用并显示计算的 属性,其中 v-for 在
computedProducts
字段上。这可能需要其他字段,在我的示例中,我使用布尔字段 isFiltered
:
let isFiltered = reactive({ value: false });
function toggleFiltered() {
isFiltered.value = !isFiltered.value;
}
const computedProducts = computed(() => {
if (isFiltered.value) {
let myFilteredProducts = {};
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
myFilteredProducts[key] = productInitData[key];
}
});
return myFilteredProducts;
} else {
return productInitData;
}
});
并在模板中:
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in computedProducts"
:key="index"
>
{{ pata["name"] }}
</div>
<button @click="toggleFiltered()">FILTER 1</button>
- 将反应对象包装在另一个对象中,以便可以更改内部对象的引用并保持反应。但是,如果我们要走这么远,不妨使用 Pinia 或 Vuex 等商店。
例如:
<template>
<div>
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in computedProducts"
:key="index"
>
{{ pata["name"] }}
</div>
<button @click="toggleFiltered()">FILTER 1</button>
<br />
<br />
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in productData"
:key="index"
>
{{ pata["name"] }}
</div>
<button @click="myFilter">FILTER 2</button>
</div>
</template>
<script>
import { computed, reactive } from "vue";
export default {
name: "App",
setup() {
const productTypeVal = 1;
let productData = reactive({
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
});
let productInitData = {
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
};
let isFiltered = reactive({ value: false });
function toggleFiltered() {
isFiltered.value = !isFiltered.value;
}
const computedProducts = computed(() => {
if (isFiltered.value) {
let myFilteredProducts = {};
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
myFilteredProducts[key] = productInitData[key];
}
});
return myFilteredProducts;
} else {
return productInitData;
}
});
function myFilter() {
// remove all properties from productData
Object.keys(productData).forEach((key) => delete productData[key]);
// re-add properties that we want to display
Object.keys(productInitData).forEach((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
productData[key] = productInitData[key];
}
});
}
return {
productData,
productTypeVal,
computedProducts,
toggleFiltered,
myFilter,
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
我有一个产品列表的反应对象,我在我的模板中显示产品。现在我制作了一个过滤器,它通过以下方式过滤产品对象
对象的产品类型键。问题是当我应用过滤器时,它在控制台中显示正确的值,但在模板中,它不反映任何更改。在 vue 2 中,我使用了 this.$forceUpdate();
,它似乎对我有用。在 vue3 组合中做这件事的正确方法是什么 API?
我做错了什么,但不知道该怎么做。谢谢你在 advacne.
这里附上我的代码沙盒link Code sandbox
<template>
<div>
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in productData"
:key="index"
>
{{ pata["name"] }}
</div>
<button @click="filterProductData()">FILTER</button>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "App",
setup() {
const productTypeVal = 1;
let productData = reactive({
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
});
let productInitData = {
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
};
let filteredProducts = reactive({});
const filterProductData = () => {
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
filteredProducts[key] = productInitData[key];
}
});
productData = filteredProducts;
console.log(productData);
};
return {
productData,
productTypeVal,
filterProductData,
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
请看Vue.js Documentation: Limitations of reactive()
:
The reactive() API has two limitations:
- It only works for object types (objects, arrays, and collection types such as Map and Set). It cannot hold primitive types such as string, number or boolean.
- Since Vue's reactivity tracking works over property access, we must always keep the same reference to the reactive object. This means we can't easily "replace" a reactive object because the reactivity connection to the first reference is lost.
后一点是你的代码的问题:你没有改变这个对象的属性的状态,这是允许的,而是改变了变量引用的引用,我认为这个引用的重新分配是是什么导致反应性丧失。
因此,我认为您可以使用几种可能的解决方案,包括:
- 正在更改
productData
的实际对象属性。为此,您可能需要删除所有属性并从productInitData
中删除它们 re-add 如果它们符合条件:
function myFilter() {
// remove all properties from productData
Object.keys(productData).forEach((key) => delete productData[key]);
// re-add properties that we want to display
Object.keys(productInitData).forEach((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
productData[key] = productInitData[key];
}
});
}
- 使用并显示计算的 属性,其中 v-for 在
computedProducts
字段上。这可能需要其他字段,在我的示例中,我使用布尔字段isFiltered
:
let isFiltered = reactive({ value: false });
function toggleFiltered() {
isFiltered.value = !isFiltered.value;
}
const computedProducts = computed(() => {
if (isFiltered.value) {
let myFilteredProducts = {};
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
myFilteredProducts[key] = productInitData[key];
}
});
return myFilteredProducts;
} else {
return productInitData;
}
});
并在模板中:
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in computedProducts"
:key="index"
>
{{ pata["name"] }}
</div>
<button @click="toggleFiltered()">FILTER 1</button>
- 将反应对象包装在另一个对象中,以便可以更改内部对象的引用并保持反应。但是,如果我们要走这么远,不妨使用 Pinia 或 Vuex 等商店。
例如:
<template>
<div>
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in computedProducts"
:key="index"
>
{{ pata["name"] }}
</div>
<button @click="toggleFiltered()">FILTER 1</button>
<br />
<br />
<div
class="assets-dropdwon accordian"
v-for="(pata, index) in productData"
:key="index"
>
{{ pata["name"] }}
</div>
<button @click="myFilter">FILTER 2</button>
</div>
</template>
<script>
import { computed, reactive } from "vue";
export default {
name: "App",
setup() {
const productTypeVal = 1;
let productData = reactive({
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
});
let productInitData = {
1: {
sort: 1,
name: "Product 1",
product_type: 1,
},
2: {
sort: -1,
name: "Product 2",
product_type: 2,
},
3: {
sort: 0,
name: "Product 3",
product_type: 1,
},
4: {
sort: 5,
name: "Product 4",
product_type: 3,
},
};
let isFiltered = reactive({ value: false });
function toggleFiltered() {
isFiltered.value = !isFiltered.value;
}
const computedProducts = computed(() => {
if (isFiltered.value) {
let myFilteredProducts = {};
Object.keys(productInitData).map((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
myFilteredProducts[key] = productInitData[key];
}
});
return myFilteredProducts;
} else {
return productInitData;
}
});
function myFilter() {
// remove all properties from productData
Object.keys(productData).forEach((key) => delete productData[key]);
// re-add properties that we want to display
Object.keys(productInitData).forEach((key) => {
if (productInitData[key]["product_type"] === productTypeVal) {
productData[key] = productInitData[key];
}
});
}
return {
productData,
productTypeVal,
computedProducts,
toggleFiltered,
myFilter,
};
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>