更改Vue.js外部的Vue.jss数据



我有一个简单的购物车演示。

它有3个Vue应用程序,一个作为共享上下文的全局对象,以及一个VanillaJS中的按钮。

如果你通过Vue应用程序将产品添加到购物车中,一切都会正常工作。

但是如果你使用VanillaJS按钮,什么都不起作用。

基本上,我想在Vue应用程序之外更改全局对象,然后以某种方式强制视图捕获更改。

我该怎么做?

附言:我之所以这么做,是因为我正在一个新的应用程序中逐步使用Vue.js。的一部分是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');

使用所有应用程序外部的反应式存储,并根据需要注入。

您可以实现piniavuex。最简单的解决方案是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并在商店中放置increasedecrease方法:

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开发工具集成的优势,这意味着您可以检查更改(也称为突变(、撤消或回放它们。基本上,它使调试变得简单多了。

我分析了您放入codepen中的代码首先如果您是";vue.js";我建议不要构建这样一个在应用程序的某些部分使用jQuerypure-js以及vue-js的应用程序。因为这可能会引起一些你无法清楚理解的冲突。

无论如何,您在这里遇到的问题是:而不是考虑Vue reactivity。简单地说,当您使用Vue.createApp(...).mount("some id")时,只有作为该id的子级的html代码部分由Vue访问和控制。因此,当您在该部分之外有一个按钮时,您不能指望Vue能够正确识别和处理它。正因为如此,如果您想用自己的javascript代码更改一个数字(此处为itemsCount数据(并立即在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数据的结构设置为添加元素后必须使用的结构(如果不将任何object添加到orderLines数组中,则当您想要添加值(例如orderLines.quantity(时,javascript会出错(。之后,在addEventListener部分中,我从html部分获得内容,向其添加值,并将新值返回到html。如果你的按钮是vue应用程序的子级,而不是在vue之外,那么所有这些过程都可以由vue处理。此外,在Vue部件中,您必须在需要的时间和地点更改cart.itemsCount和其他属性。

您还必须注意,您定义的itemsCount计算属性与cart.itemsCount完全不同,这不会在代码中导致错误。根据我上面所说的所有描述,我再次不建议以这种方式构建您的应用程序。在这种情况下,您可能会在代码中遇到其他错误。您可以使用jQuerypure js来更改数据(例如cart.quantity(,但要在Vue处理的html部分中显示这些数据,请使用按钮和其他方法,这些方法是Vue本身的一部分,不在Vue之外,还可以监视Vue Devtools中数据的更改,以正确调试代码。

最新更新