我有一个简单的购物车演示。
它有3个Vue应用程序,一个作为共享上下文的全局对象,以及一个在Vanilla JS中的按钮。
如果您通过Vue应用程序将产品添加到购物车中,那么一切都正常。
但如果你用香草JS按钮,什么都不能用。
基本上,我希望在Vue应用程序之外更改全局对象,然后以某种方式强制视图捕获更改。
我怎么能这么做?
我之所以这么做是因为我在一个新的应用程序中逐步使用Vue.js。是is jQuery的一部分,也是Vue的一部分。它们都将全局对象作为共享对象进行操作。
这是我的JS代码:
let cart = { orderLines: [], itemsCount: 0 }
let addViaJs = document.querySelector('#addViaJs');
addViaJs.addEventListener('click', () => {
cart.orderLines[0].quantity += 1; // this line does not cause update in UI
});
let miniCart = {
data() {
return {
cart,
}
},
computed: {
itemsCount() {
let itemsCount = 0;
for (let i = 0; i < this.cart.orderLines.length; i++) {
itemsCount += this.cart.orderLines[i].quantity || 0;
}
return itemsCount;
}
},
};
Vue.createApp(miniCart).mount('#miniCart');
let bigCart = {
data() {
return {
cart,
}
},
computed: {
itemsCount() {
let itemsCount = 0;
for (let i = 0; i < this.cart.orderLines.length; i++) {
itemsCount += this.cart.orderLines[i].quantity || 0;
}
return itemsCount;
}
},
};
Vue.createApp(bigCart).mount('#bigCart');
let productList = {
data() {
return {
cart,
products: [
{ id: 1, label: 'Product A' },
{ id: 2, label: 'Product B' },
{ id: 3, label: 'Product C' },
]
}
},
methods: {
addToCart(product) {
const line = this.cart.orderLines.find(line => line.id === product.id);
if (line) {
line.quantity++;
} else {
this.cart.orderLines.push({ ...product, quantity: 1 });
}
},
}
};
Vue.createApp(productList).mount('#productList');发布于 2022-03-13 14:51:04
在所有应用程序的外部使用一个反应性存储,并在需要的地方注入它。
您可以实现pinia或vuex。最简单的解决方案是一个reactive()对象。
下面是一个基本的例子,有3个单独的应用程序,演示了原理:
const { createApp, reactive, toRefs } = Vue;
const store = reactive({
count: 0
})
createApp({
setup: () => ({
...toRefs(store),
increaseCount: () => store.count += 1
})
}).mount('#app-1');
createApp({
setup: () => ({
...toRefs(store),
decreaseCount: () => store.count -= 1
})
}).mount('#app-2');
createApp({
setup: () => ({
...toRefs(store)
})
}).mount('#app-3');.container {
display: flex;
justify-content: space-evenly;
}<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div class="container">
<div id="app-1">
<h3> App 1</h3>
{{ count }}
<button @click="increaseCount">Increase</button>
</div>
<div id="app-2">
<h3> App 2</h3>
{{ count }}
<button @click="decreaseCount">Decrease</button>
</div>
<div id="app-3">
<h3> App 3</h3>
{{ count }}
<input v-model.number="count" type="number">
</div>
</div>
同样,使用pinia并在商店中放置increase和decrease方法:
const { createApp, reactive, toRefs } = Vue;
const { createPinia, defineStore } = Pinia;
const pinia = createPinia();
const useCounter = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increase() {
this.count += 1;
},
decrease() {
this.count -= 1;
}
}
});
[1, 2, 3].forEach(key => createApp({
setup: () => ({
counter: useCounter(pinia)
})
}).mount(`#app-${key}`)).container {
display: flex;
justify-content: space-evenly;
}<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/vue-demi"></script>
<script src="https://unpkg.com/pinia@2.0.11/dist/pinia.iife.prod.js"></script>
<div class="container">
<div id="app-1">
<h3> App 1</h3>
{{ counter.count }}
<button @click="counter.increase">Increase</button>
</div>
<div id="app-2">
<h3> App 2</h3>
{{ counter.count }}
<button @click="counter.decrease">Decrease</button>
</div>
<div id="app-3">
<h3> App 3</h3>
{{ counter.count }}
<input v-model.number="counter.count" type="number"/>
</div>
</div>
Pinia和Vuex具有与vue devtools集成的优势,这意味着您可以检查更改(即.a)。突变),撤销或重播它们。基本上,它使调试变得简单多了。
发布于 2022-03-13 16:49:44
我分析了您在codepen中添加的代码。First of all,如果你是"vue.js“新手,我建议不要在应用程序的某些部分使用jQuery和纯js和vue-js来构建这样的应用程序。因为这可能会引起一些你无法理解的冲突。
无论如何,这里的问题是:而不是考虑Vue reactivity。简单地说,当您使用Vue.createApp(...).mount("some id")时,only -- html代码中作为id子级的部分--由Vue访问和控制。因此,当您在该部分外有一个按钮时,您不能期望Vue识别并正确处理它。由于这个事实,如果您想用自己的itemsCount javascript代码更改一个数字(这里是数据)并立即在html部分(即Vue的反应性行为)中看到结果,那么您必须自己编写所有代码。例如,我在这里放了一些代码来说明我的意思:
let cart = { orderLines: [
/* change the structure of defined array */
{
quantity: 0
}
], itemsCount: 0 }
let addViaJs = document.querySelector('#addViaJs');
addViaJs.addEventListener('click', () => {
/* manually change the "html" content with js codes */
let currentValue = parseInt(document.getElementById("itemsSpan").innerText)
cart.orderLines[0].quantity = currentValue + 1;
cart.itemsCount = currentValue + 1;
document.getElementById("itemsSpan").innerText = cart.itemsCount;
});
let miniCart = {
data() {
return {
cart,
}
},
computed: {
/* this "itemsCount is different from "cart.itemsCount" */
itemsCount() {
let itemsCount = 0;
for (let i = 0; i < this.cart.orderLines.length; i++) {
itemsCount += this.cart.orderLines[i].quantity || 0;
}
return itemsCount;
}
},
};
Vue.createApp(miniCart).mount('#miniCart');
let bigCart = {
data() {
return {
cart,
}
},
computed: {
itemsCount() {
let itemsCount = 0;
for (let i = 0; i < this.cart.orderLines.length; i++) {
itemsCount += this.cart.orderLines[i].quantity || 0;
}
return itemsCount;
}
},
};
Vue.createApp(bigCart).mount('#bigCart');
let productList = {
data() {
return {
cart,
products: [
{ id: 1, label: 'Product A' },
{ id: 2, label: 'Product B' },
{ id: 3, label: 'Product C' },
]
}
},
methods: {
addToCart(product) {
/* change the related values of "count" object */
this.cart.orderLines[0].quantity++
this.cart.itemsCount++
// const line = this.cart.orderLines.find(line => line.id === product.id);
// if (line) {
// line.quantity++;
// } else {
// this.cart.orderLines.push({ ...product, quantity: 1 });
// }
},
}
};
Vue.createApp(productList).mount('#productList');<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="productList" v-cloak>
<h2>Products</h2>
<div v-for="product in products">
<span>{{ product.label }}</span>
<button @click="addToCart(product)">Add</button>
</div>
</div>
<div id="miniCart" v-cloak>
<h2>Mini cart</h2>
<div>itemsCount:
<span id="itemsSpan">
{{ itemsCount }}
</span>
</div>
</div>
<div id="bigCart" v-cloak>
<!-- <h2>Cart ({{ itemsCount }})</h2>-->
<!-- <ul>-->
<!-- <li v-for="line in cart.orderLines">{{ line.label}} ({{ line.quantity }})</li>-->
<!-- </ul>-->
</div>
<button id='addViaJs'>
Add via JS (outside Vue)
</button>
<script src="https://unpkg.com/vue@3.2.31/dist/vue.global.js"></script>
</body>
</html>
这与您的代码并不完全类似。我评论了一些部分,并使数据更简单,以更好地显示结果。首先,我将cart数据的结构设置为必须在添加元素之后的结构(如果不向orderLines数组添加任何object,则javascript在添加例如orderLines.quantity的值时会给出错误)。之后,在addEventListener部件中,我从html部件获取内容,向它添加值,并将新值返回到html。如果您的按钮是vue应用程序的子程序,而不是外部的,那么所有这些进程都可以通过Vue处理。同样,在您的Vue部分中,您必须在需要的时候和地点更改cart.itemsCount和其他属性。
您还必须注意,您定义的itemsCount计算属性与cart.itemsCount完全不同,这不应导致代码中的错误。以上所有的描述,我上面说过,再次我不建议以这种方式构建您的应用程序。在这种情况下,您可能会在代码中遇到其他错误。您可以使用jQuery或pure js更改数据(例如,cart.quantity),但是要在由 Vue 处理的html部件中显示数据,可以使用按钮和其他方法,这些方法本身并不在Vue之外,还可以监视Vue Devtools中数据的更改,以正确调试代码。
https://stackoverflow.com/questions/71456423
复制相似问题