vue父组件异步获取动态数据传递给子组件 获取不到值的问题已完美解决 附demo

前几天遇到一个问题,在父组件中使用axios获取异步数据传给子组件,但是发现子组件在渲染的时候并没有数据,在created里面打印也是空的,结果发现一开始子组件绑定的数据是空的,在请求数据没有返回数据时,子组件就已经加载了,并且他绑定的值也是空的,问题找到了,怎么解决那?有两种方法解决,请看下面代码。

方法一、

开始的时候让子组件隐藏,然后等数据返回的时候,让子组件显示。
<template>
  <div class="list">
    <ul v-if="list.length != 0">
      <li v-for="(item,index) in list" :key="index"</li>
    </ul>
  </div>
</template>
<script>
export default {
  props:['getList'], //接收一个数组
    data(){
      return {
        list: []
      }
    },
    watch:{   // 使用监听的方式,监听数据的变化
      getList(val){
        this.list = val;
      }
    }
}
</script>
不过这样方式不太合适,有bug,比如我点击一个按钮去获取数据,然后在弹框里面展示数据,弹框是一个子组件,在获取数据的这段过程有可能几百毫秒,也有可能十秒或者更长时间,难道我要在点击按钮过十秒才让弹框显示吗?这这这绝对不行,推荐使用方法二

方法二、 推荐使用

大概逻辑:使用vuex全局状态管理,其实简单,利用vuex的辅助函数(mapState,mapMutations)mapState是将state里面的数据映射到计算中(computed),mapMutations也是类似,把vuex中mutations的方法映射到组件里面,就可以在组件里面直接使用方法了,在vuex中使用异步(actions)去掉用接口,然后在接口成功的函数里面取触发同步(mutations)里面的方法,把得到数据传给mutations里面的方法里并且给state里面的属性赋值,然后就可以在子组件中使用computed计算中去获取数据并且渲染到页面上,其实说的有点绕( -_-"),但是看代码就明白了 。
vuex / index.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex) 
export default new Vuex.Store({  
    //定义初始数据
    state: {  
        title: '',
        list: [],
        isShow: false
    },
    //同步的方法
    mutations: {
        //向state 里面设置数据
        changeListMutation(state, list) {
            state.list = list
        },
        //在list.vue里面点击下拉选项的时候触发 给state.title赋值
        changeTitleMutation(state, title) {
            state.title = title
        },
        //selectinput.vue里面点击input的时候触发 给state.isShow赋值
        toggleShow(state, isShow) {
            state.isShow = isShow 
        }
    },
    //异步的方法
    actions: {
        //在list.vue里面created生命周期里面触发
        getListAction({ commit }) {
            axios.get('/mock/5afd9dc0c088691e06a6ab45/example/dataList')
                .then((res) => {
                    commit('changeListMutation', res.data) //调用mutations下面的changeListMutation方法并且传值过去
                })
                .catch((error) => {
                    console.log(error)
                })

        }
    }
})
// 触发异步里面的方法是用 this.$store.dispatch('这里是方法名')
// 触发同步里面的方法是用 this.$store.commit('这里是方法名')
父组件 select.vue
这个页面只是引入两个子组件,没有什么好说的
<template>
  <div class="select">
    <div class="wrap">
        <selectInput></selectInput>
        <list></list>
    </div>
  </div>
</template>
<script>
  // 引入子组件 
  import selectInput from '@/components/selectInput'  
  import list from '@/components/list'
  export default {
    components:{   //加载子组件
      selectInput,
      list
    },
  }
</script>
<style>
  .select{
    background:#4a56fe;
    width: 400px;
    margin: 100px auto 0;
    padding: 40px;
    border-radius: 10px;
  }
  .wrap{
    background: #e3e5fe;
    border-radius: 10px;
    padding: 40px;
  }
  ul{
    list-style: none;
  }
</style>
子组件 list.vue
该组件就是展示下拉选项,并且调用数据渲染
<template>
  <div class="list">
    <ul>
      <li v-for="(item,index) in list" :key="index" v-show="initShow" @click="changeTitle(item.title)">{{item.title}}</li>
    </ul>
  </div>
</template>

<script>
    import {mapState,mapMutations} from 'vuex'  // 将vuex中的state数据和mutations中的方法映射到组件中
    export default {
        //vue 生命周期(created)在实例创建之后,在数据初始化之前被调用
        created(){  
            this.$store.dispatch('getListAction')  //调用vuex 中的 getListAction异步方法
        },
        //计算state数据
        computed:{
            ...mapState({
              list:'list',
              initShow:'isShow'
            })
        },
        methods:{
            changeTitle(title){
              this.$store.commit('changeTitleMutation',title)
              this.$store.commit('toggleShow',!this.initShow)
            }
        }
    }
</script>
// 触发异步里面的方法是用 this.$store.dispatch('这里是方法名')
// 触发同步里面的方法是用 this.$store.commit('这里是方法名')

<style>
  .list{
    padding: 10px 0;
    text-align: center;
  }
  li{
    line-height: 30px;
    height: 30px;
    border-radius: 15px;
    cursor: pointer;
    color:#535353;
  }
  li:hover{
    background: #ff705b;
    color: #fff;
  }
</style>
子组件 selectinput.vue
该组件展示选中的数据
<template>
  <div class="inputBox">
    <input type="text" readonly :value="getTitle" @click="toggleShow" placeholder="你喜欢什么">
  </div>
</template>

<script>
export default {
  computed:{
    // 获取vuex中的state数据并赋值绑定到 value上面  computed 里面的方法名其实就是相当于 data里面的数据,可以用this.getTitle 去访问
    getTitle(){ 
      return this.$store.state.title
    },
    // 初始化控制下拉选项显示隐藏的状态,如果isShow是false 则不限是下拉菜单,默认是false
    initShow(){
        return this.$store.state.isShow
    }
  },
  methods:{
    //点击input的时候调用该方法,这个方法去触发mutations下面的toggleShow,去改变isShow的状态,默认是isShow等于false, 然后在点击的时候去改变isShow 等于true ,  !this.initShow就是true,如果是true的话,下拉选项才能出来,并将改变过后的值传给toggleShow方法,去给vuex/store.js 里面的state.isShow赋值。
    toggleShow(){
      this.$store.commit('toggleShow',!this.initShow)
    }
  }
}
</script>

<style>
input{
  outline: none;
  width: 100%;
  height: 40px;
  line-height: 40px;
  border-radius: 10px;
  border: 1px solid #d3d3d3;
  text-indent: 20px;
  color: #535353;
}
</style>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章