如何使用 VueJS 正确地将数据传递给同级组件?

clz*_*ola 2 javascript vue.js vuejs2

假设我有CountrySelectCitySelect,其中CountrySelect列出了所有国家/地区,并且CitySelect仅列出了当前所选国家/地区的城市...

\n\n

通过新选择的国家的正确方法是什么?正如我在 vuejs 文档中读到的:

\n\n
\n

在Vue中,父子组件关系可以概括为props down,events up。父级通过 props 将数据传递给子级,子级通过事件向父级发送消息。接下来让\xe2\x80\x99s看看它们是如何工作的。

\n
\n\n

CountrySelect因此,在这种情况下,我将检测触发事件内部选择框的变化changed以及国家/地区对象。然后 的父组件CountrySelect将侦听此事件,然后当事件发生时将更新其属性country。属性country在父组件中被“观察到”,并且更改其值将导致 DOM 更新,因此<city-select>调用的标记上的 HTML 属性country将会更改。但是接下来我将如何检测CitySelect组件的属性更改,因为我需要通过 AJAX 重新加载城市。我考虑将country属性放入watch对象中CitySelect,但这对我来说似乎不是一个优雅的解决方案,感觉这样做不太正确......

\n\n
<template>\n    <parent>\n        <country-select name="country_id" v-model="country"></country-select>\n        <city-select name="city_id" :country="country" v-model="city"></city-select>\n    </parent>\n<template>\n\n<script>\n    export default {\n        data: function() {\n            return {\n                country: null,\n                city: null,\n            };\n        }\n    }\n</script>\n
Run Code Online (Sandbox Code Playgroud)\n\n

我认为的另一种方法是,如果在父级中做这样的事情:

\n\n
this.$refs.citySelect.emit(\'countryChanged\', this.country)\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者:

\n\n
this.$refs.citySelect.countryChanged(this.country)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我不知道这两个是否可能...那么CountrySelect组件告诉其兄弟组件CitySelect更新城市列表的正确方法是什么...

\n

Ber*_*ert 5

country通过作为属性传递给您的CitySelect组件,您已经在做您需要做的事情了。当country更改时,更改的值将传递给CitySelect组件。您只需要确保您的CitySelect组件知道这些更改即可。

这是一个工作示例。

console.clear()

// Simulated service for retrieving cities by country
const CityService = {
  cities: [
    {id: 1, country: "USA", name: "New York"},
    {id: 2, country: "USA", name: "San Francisco"},
    {id: 3, country: "USA", name: "Boston"},
    {id: 4, country: "USA", name: "Chicago"},
    {id: 5, country: "England", name: "London"},
    {id: 6, country: "England", name: "Bristol"},
    {id: 7, country: "England", name: "Manchester"},
    {id: 8, country: "England", name: "Leeds"},
  ],
  getCities(country){
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve(this.cities.filter(city => city.country === country)), 250)
    })
  }
}

Vue.component("CountrySelect",{
  props: ["value"],
  template: `
  <select v-model="selectedCountry">
    <option v-for="country in countries" :value="country">{{country}}</option>
  </select>
  `,
  data(){
    return {
      countries: ["USA", "England"]
    }
  },
  computed:{
    selectedCountry:{
      get(){return this.value},
      set(v){this.$emit('input', v)}
    }
  }
})

Vue.component("CitySelect", {
  props: ["value", "country"],
  template: `
  <select v-model="selectedCity">
    <option v-for="city in cities" :value="city">{{city.name}}</option>
  </select>
  `,
  data(){
    return {
      cities: []
    }
  },
  computed:{
    selectedCity:{
      get(){return this.value},
      set(v){this.$emit('input', v)}
    }
  },
  watch:{
    country(newCountry){
      // simulate an AJAX call to an external service
      CityService.getCities(newCountry).then(cities => this.cities = cities)
    }
  }
})

new Vue({
  el: "#app",
  data:{
    country: null,
    city: null
  }
})
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
  <country-select v-model="country"></country-select>
  <city-select :country="country" v-model="city"></city-select>
  <hr>
  Selected Country: {{country}} <br>
  Selected City: {{city}}
</div>
Run Code Online (Sandbox Code Playgroud)