gpt4 book ai didi

javascript - Vue:从 parent 那里收到 Prop 后, child 的组件不会改变

转载 作者:行者123 更新时间:2023-12-01 00:32:01 25 4
gpt4 key购买 nike

所以我对与vue的父子组件通信有问题。关键是,在我导航到组件之后,它应该调用ajax从服务器获取数据。接收到数据后,父组件应该通过props将其发送给所有子组件,但是props数据没有显示。仅在我在编辑器上更改代码之后,子组件才开始显示道具数据。
所以,这是我父组件的代码

<template>
<div id="single-product-container">
<product-header :name="singleProductName" :details="singleProductDetail" />
<product-spec :spec="singleProductSpec" />
</div>
</template>

<script>
import SingleProductHeader from '@/pages/SingleProductPage/single-product-header'
import SingleProductSpec from '@/pages/SingleProductPage/single-product-spec'
import singleProductApi from '@/api/product.api'

export default {
data () {
return {
singleProductData: null,
singleProductDetail: [],
singleProductName: '',
singleProductSpec: null
}
},
methods: {
getAllSingleProductDetail () {
const productName = this.$route.params.product
const location = this.location || 'jakarta'
let vehicleType = null
const path = this.$route.fullPath
let self = this
if (path.includes('motorcycle')) {
vehicleType = 'motorcycle'
} else if (path.includes('car')) {
vehicleType = 'car'
}
singleProductApi.getSingleProductRequest(location, productName, vehicleType)
.then(singleProductResponse => {
console.log(singleProductResponse)
let specObj = singleProductResponse.specification
self.singleProductDetail = singleProductResponse.detail
self.singleProductName = singleProductResponse.product_name
self.singleProductSpec = specObj
self.singleProductData = singleProductResponse
})
.catch(error => {
throw error
})
}
},
mounted () {
document.title = this.$route.params.product
},
created () {
this.getAllSingleProductDetail()
},
components: {
'product-header': SingleProductHeader,
'product-spec': SingleProductSpec
}
}
</script>


这是我的单一产品规格组件,不会加载道具数据:

<template>
<div id="product-spec">
<div class="product-spec-title">
Spesifikasi
</div>
<div class="produk-laris-wrapper">
<div class="tab-navigation-wrapper tab-navigation-default">
<div class="tab-navigation tab-default" v-bind:class="{ 'active-default': mesinActive}" v-on:click="openSpaceTab(event, 'mesin')">
<p class="tab-text tab-text-default">Mesin</p>
</div>
<div class="tab-navigation tab-default" v-bind:class="{ 'active-default': rangkaActive}" v-on:click="openSpaceTab(event, 'rangka')">
<p class="tab-text tab-text-default">Rangka & Kaki</p>
</div>
<div class="tab-navigation tab-default" v-bind:class="{ 'active-default': dimensiActive}" v-on:click="openSpaceTab(event, 'dimensi')">
<p class="tab-text tab-text-default">Dimensi & Berat</p>
</div>
<div class="tab-navigation tab-default" v-bind:class="{ 'active-default': kapasitasActive}" v-on:click="openSpaceTab(event, 'kapasitas')">
<p class="tab-text tab-text-default">Kapasitas</p>
</div>
<div class="tab-navigation tab-default" v-bind:class="{ 'active-default': kelistrikanActive}" v-on:click="openSpaceTab(event, 'kelistrikan')">
<p class="tab-text tab-text-default">Kelistrikan</p>
</div>
</div>
<div id="tab-1" class="spec-tab-panel" v-bind:style="{ display: mesinTab }">
<table class="spec-table">
<tbody>
<tr class="spec-row" v-for="(value, name) in mesinData" :key="name">
<td> {{ name }} </td>
<td> {{ value }} </td>
</tr>
</tbody>
</table>
</div>
<div id="tab-2" class="spec-tab-panel" v-bind:style="{ display: rangkaTab }">
<table class="spec-table">
<tbody>
<tr class="spec-row" v-for="(value, name) in rangkaData" :key="name">
<td> {{ name }} </td>
<td> {{ value }} </td>
</tr>
</tbody>
</table>
</div>
<div id="tab-3" class="spec-tab-panel" v-bind:style="{ display: dimensiTab }">
<table class="spec-table">
<tbody>
<tr class="spec-row" v-for="(value, name) in dimensiData" :key="name">
<td> {{ name }} </td>
<td> {{ value }} </td>
</tr>
</tbody>
</table>
</div>
<div id="tab-4" class="spec-tab-panel" v-bind:style="{ display: kapasitasTab }">
<table class="spec-table">
<tbody>
<tr class="spec-row" v-for="(value, name) in kapasitasData" :key="name">
<td> {{ name }} </td>
<td> {{ value }} </td>
</tr>
</tbody>
</table>
</div>
<div id="tab-5" class="spec-tab-panel" v-bind:style="{ display: kelistrikanTab }">
<table class="spec-table">
<tbody>
<tr class="spec-row" v-for="(value, name) in kelistrikanData" :key="name">
<td> {{ name }} </td>
<td> {{ value }} </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>

<script>
export default {
props: {
location: String,
spec: Object
},
data () {
return {
mesinActive: true,
rangkaActive: false,
dimensiActive: false,
kapasitasActive: false,
kelistrikanActive: false,
mesinTab: 'block',
rangkaTab: 'none',
dimensiTab: 'none',
kapasitasTab: 'none',
kelistrikanTab: 'none',
mesinData: {},
rangkaData: {},
dimensiData: {},
kapasitasData: {},
kelistrikanData: {}
}
},
methods: {
openSpaceTab (evt, tab) {
if (tab === 'mesin') {
this.mesinActive = true
this.rangkaActive = false
this.dimensiActive = false
this.kapasitasActive = false
this.kelistrikanActive = false
this.mesinTab = 'block'
this.rangkaTab = 'none'
this.dimensiTab = 'none'
this.kapasitasTab = 'none'
this.kelistrikanTab = 'none'
} else if (tab === 'rangka') {
this.mesinActive = false
this.rangkaActive = true
this.dimensiActive = false
this.kapasitasActive = false
this.kelistrikanActive = false
this.mesinTab = 'none'
this.rangkaTab = 'block'
this.dimensiTab = 'none'
this.kapasitasTab = 'none'
this.kelistrikanTab = 'none'
} else if (tab === 'dimensi') {
this.mesinActive = false
this.rangkaActive = false
this.dimensiActive = true
this.kapasitasActive = false
this.kelistrikanActive = false
this.mesinTab = 'none'
this.rangkaTab = 'none'
this.dimensiTab = 'block'
this.kapasitasTab = 'none'
this.kelistrikanTab = 'none'
} else if (tab === 'kapasitas') {
this.mesinActive = false
this.rangkaActive = false
this.dimensiActive = false
this.kapasitasActive = true
this.kelistrikanActive = false
this.mesinTab = 'none'
this.rangkaTab = 'none'
this.dimensiTab = 'none'
this.kapasitasTab = 'block'
this.kelistrikanTab = 'none'
} else if (tab === 'kelistrikan') {
this.mesinActive = false
this.rangkaActive = false
this.dimensiActive = false
this.kapasitasActive = false
this.kelistrikanActive = true
this.mesinTab = 'none'
this.rangkaTab = 'none'
this.dimensiTab = 'none'
this.kapasitasTab = 'none'
this.kelistrikanTab = 'block'
}
}
},
created () {
this.mesinData = this.spec.mesin
this.rangkaData = this.spec.rangka
this.dimensiData = this.spec.dimensi
this.kapasitasData = this.spec.kapasitas
this.kelistrikanData = this.spec.kelistrikan
}
}
</script>


正如我所说,我的单一产品规格组件的唯一问题不是它不会加载道具数据。问题是,当我在文本编辑器中更改代码时,它仅加载props数据(这很奇怪,我知道)。当我开始调试时,我开始意识到这一点,当我在单一产品规格组件中更改代码时,props数据便开始加载。而且,如果我不更改我的单一产品规格组件代码,则无论等待多长时间,道具数据都不会加载。

最佳答案

好的,让我们逐步介绍一下发生的情况:


创建父组件,触发created钩子并从服务器启动数据加载。
父组件进行渲染,创建子组件。由于尚未加载数据,并且spec仍为null,因此singleProductSpec的prop值将为null
createdsingle-product-spec挂钩运行。由于this.specnull,我想这会引发错误,尽管问题中未提及任何错误。
在将来的某个时刻,数据加载完成,从而更新singleProductSpec的值。它是父组件的渲染依赖项,因此该组件将被添加到渲染队列中。
父组件将重新渲染。 singleProductSpec的新值将作为spec属性传递给single-product-specsingle-product-spec的新实例将不会被创建,它将重新使用它最初创建的实例。


到那时,其他任何事情都不会发生。 createdsingle-product-spec钩子不会重新运行,因为它尚未被创建。

当您编辑子组件的源代码时,它将触发该组件的热重装。此类更改的确切效果会有所不同,但通常会导致重新创建子级而不重新创建父级。由于父服务器已经从服务器加载了数据,因此将向新创建的子服务器传递完全填充的spec值。这样就可以在created挂钩中读取它。

有很多方法可以解决此问题。

首先,我们可以避免在数据准备就绪之前创建single-product-spec

<product-spec v-if="singleProductSpec" :spec="singleProductSpec" />


这将完全避免在初始渲染期间创建组件,以便在运行子级 created挂钩时,它可以访问所需的数据。这可能是您应该使用的方法。

第二种方法是使用 key。密钥用于在重新渲染时配对组件,以便Vue知道哪个旧组件与哪个新组件匹配。如果 key更改,则Vue将丢弃旧的子组件,而是创建一个新的子组件。创建新组件后,它将运行 created挂钩。这可能不是针对您的方案的最佳方法,因为不清楚在传递 specnull时子组件应该做什么。

第三种方法是在子组件中使用 watch。这将监视 spec的值何时更改,并将相关值复制到组件的本地数据属性。在某些情况下,使用这样的 watch是适当的,它通常表示组件设计中的潜在弱点。

但是,您的代码中还有其他问题...


目前尚不清楚为什么要首先将prop中的值复制到本地数据中。您可以直接使用道具。如果您只是在给它们起一个简短的名字,那么就使用计算属性代替。像这样复制它们的唯一合法理由是,如果可以在子代中更改属性值,并且prop仅用于传递初始值。即使在这种情况下,您也不会使用 created钩子,而只是在 data函数中执行它。请参见 https://vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
您将5个标签的所有内容复制了5次。这应该使用对象数组来实现,每个对象都包含选项卡的所有相关详细信息。
属性 mesinActivemesinTab都表示相同的基础数据。 data中不应同时包含两者。至少应该是一个计算属性,尽管就我个人而言,我可能会完全放弃 mesinTab。而是使用CSS类来应用相关样式,而只需使用 mesinActive来决定要应用哪些类(就像在其他地方一样)。显然,其他 xActive / xTab属性也是如此。
您的标签是一种单选形式。使用5个布尔值表示一个选择是不合适的数据结构。正确的方法是拥有一个用于标识当前选项卡的属性。具体内容可能有所不同,可能包含标签索引,代表标签数据的对象或代表标签的ID。
您无需将 let self = this与箭头功能一起使用。 this值从周围的范围保留。


正确实现的 single-product-spec代码应该可以折叠成几乎没有内容。您应该能够摆脱大约80%的代码。如果您只是使用适当的数据结构来保存所有数据,我希望方法 openSpaceTab是单行的。

更新:

根据要求,这里是您组件的重写,其中考虑了我答案的“其他问题”部分中的1-4点。



const ProductSpecTitle = {
template: `
<div>
<div class="product-spec-title">
Spesifikasi
</div>
<div class="produk-laris-wrapper">
<div class="tab-navigation-wrapper tab-navigation-default">
<div
v-for="tab of tabs"
:key="tab.id"
class="tab-navigation tab-default"
:class="{ 'active-default': tab.active }"
@click="openSpaceTab(tab.id)"
>
<p class="tab-text tab-text-default">{{ tab.text }}</p>
</div>
</div>
<div
v-for="tab in tabs"
class="spec-tab-panel"
:class="{ 'spec-tab-panel-active': tab.active }"
>
<table class="spec-table">
<tbody>
<tr
v-for="(value, name) in tab.data"
:key="name"
class="spec-row"
>
<td> {{ name }} </td>
<td> {{ value }} </td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
`,

props: {
spec: Object
},

data () {
return {
selectedTab: 'mesin'
}
},

computed: {
tabs () {
const tabs = [
{ id: 'mesin', text: 'Mesin' },
{ id: 'rangka', text: 'Rangka & Kaki' },
{ id: 'dimensi', text: 'Dimensi & Berat' },
{ id: 'kapasitas', text: 'Kapasitas' },
{ id: 'kelistrikan', text: 'Kelistrikan' }
]

for (const tab of tabs) {
tab.active = tab.id === this.selectedTab
tab.data = this.spec[tab.id]
}

return tabs
}
},

methods: {
openSpaceTab (tab) {
this.selectedTab = tab
}
}
}

new Vue({
el: '#app',

components: {
ProductSpecTitle
},

data () {
return {
spec: {
mesin: { a: 1, b: 2 },
rangka: { c: 3, d: 4 },
dimensi: { e: 5, f: 6 },
kapasitas: { g: 7, h: 8 },
kelistrikan: { i: 9, j: 10 }
}
}
}
})

.tab-navigation-wrapper {
display: flex;
margin-top: 10px;
}

.tab-navigation {
border: 1px solid #000;
cursor: pointer;
}

.tab-text {
margin: 10px;
}

.active-default {
background: #ccf;
}

.spec-tab-panel {
display: none;
}

.spec-tab-panel-active {
display: block;
margin-top: 10px;
}

.spec-table {
border-collapse: collapse;
}

.spec-table td {
border: 1px solid #000;
padding: 5px;
}

<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>

<div id="app">
<product-spec-title :spec="spec"></product-spec-title>
</div>

关于javascript - Vue:从 parent 那里收到 Prop 后, child 的组件不会改变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58454247/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com