问题背景:父组件为index.vue, 两个子组件为home-header.vue和home-sidebar.vue。现在需要在父组件中请求完数据后,两个子组件才去发生请求数据。

解决方案一:

在父组件index.vue中定义两个变量,用来确定父组件中的请求是否完毕,然后在子组件中使用v-if

index.vue:

<template>
  <div id="index">
    <home-header v-if="homeHeader" />
    <home-sidebar v-if="homeSider" />
  </div>
</template>

...

  data() {
    return {
      homeHeader: false,
      homeSider: false
    }
  },

axios请求成功后才设置: homeHeader 和 homeSider = true

这时是可以解决问题的,但是由于header和sider的背景是黑色,在该组件未显示的时候,会出现白色的过渡,页面看上去效果不好。所以就有了下面思考:

方案二:(有问题)

在父组件index中将表示axios是否请求完成的homeHeaderhomeSider通过props传递给子组件,然后在子组件中接收到值后再去发生请求。

父组件index

<template>
  <div id="index">
    <home-header :isShow="homeHeader" />
    <home-sidebar :isShow="homeSider" />
  </div>
</template>

...

  data() {
    return {
      homeHeader: false,
      homeSider: false
    }
  },

axios请求成功后才设置: homeHeader 和 homeSider = true

子组件home-header(只拿一个举例)

  props: {
    isShow: {
      type: Boolean,
      default: false
    }
  },
  created() {
    // 请求版本号信息
    if (this.isShow) {
      getVersion().then(res => {
        if (res.data) {
          this.version = res.data
        }
      })
    }
  },

这时出现问题,初次进入页面子组件的请求并不会执行,但是在刷新浏览器后就会执行。


寻找原因:

为了寻找原因,我又创建了一个新的demo,问题的本质一样

父组件index.vue:

<template>
  <div id="index">
    <props-test :isShow="isShow" />
  </div>
</template>

<script>
import PropsTest from '@/components/PropsTest'
export default {
  name: 'Index',
  components: {
    PropsTest
  },
  data() {
    return {
      isShow: false
    }
  },
  created() {
    setTimeout(() => (this.isShow = true), 5000)
  }
}
</script>

<style scoped></style>

子组件Propstest.vue

<template>
  <div>{{ message }}</div>
</template>

<script>
export default {
  name: '',
  props: {
    isShow: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      message: 'isShow为false时显示'
    }
  },
  created() {
    if (this.isShow) {
      this.message = 'isShow为true时显示'
    }
  }
}
</script>

<style scoped></style>

初次进入页面的显示效果:
在这里插入图片描述
可以观察到,子组件中的isShow已经变为了true但是message的值并没有变。

其实,通过vuetools可以看到,isShow的值确实已经发生了改变,但message的数据并没有更新。这是因为在页面生成后,父组件和子组件都已经挂载完毕,但这时父组件中的异步请求还没有完成。所以初次进入页面时,isShow为false,子组件执行created钩子时发现isShow为false,就不会去改变message的值。然后,等待父组件中的异步请求完毕后,isShow变为了true,并且传递到子组件中,这时,可以在tools中观察到isShow发生了变化,但是messaeg并没有改变。这是因为此时子组件的created钩子并不会触发

可以在子组件created中打印一下isShow,发现它只打印了一次,而且是刚进入页面就打印了。这也说明created钩子只触发了一次:

在这里插入图片描述


怎么解决这个问题呢

使用watch监听props的变化
官方文档中有对用watch监听props变化的介绍,点击此处
和监听data中的数据不一样,监听props需要这样:

<template>
  <div>{{ message }}</div>
</template>

<script>
export default {
  name: '',
  props: {
    isShow: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      message: 'isShow为false时显示'
    }
  },
  watch: {
    isShow: {
      handler(newValue) {
        if (newValue) {
          this.message = 'isShow为true时显示'
        }
      },
      immediate: true
    }
  }
  // created() {
  //   console.log('this.isShow', this.isShow)
  //   if (this.isShow) {
  //     this.message = 'isShow为true时显示'
  //   }
  // }
}
</script>

<style scoped></style>

这时打开页面:
在这里插入图片描述
到此,问题终于解决。


方案二(修复)

使用watch来监听 props

  watch: {
    isShow: {
      handler(newValue) {
        if (newValue) {
          this.requestBriefInfo()
        }
      },
      immediate: true
    }
  },

Logo

前往低代码交流专区

更多推荐