vue2.0 插槽不是响应性的

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

请注意插槽不是响应性的。如果你需要一个组件可以在被传入的数据发生变化时重渲染我们建议改变策略依赖诸如 propsdata 等响应性实例选项。-- vm.$slots

问题描述

项目中自定了组件 widget作为容器其中 header 部分做了预置插槽 slot用于信息的展示在实际使用过程中header 内容需要根据不同条件展示不同信息。至此问题出现了~~~

期望效果右侧 title 根据 ajax 返回结果展示其中一种
在这里插入图片描述
实际效果ajax 正常返回但没有渲染成功
在这里插入图片描述
widget 代码

<template>
	<div	class="widget-container">
    <header v-if="titleSlots.length">
      <div v-for="(item, index) in titleSlots" :key="index">
        <slot :name="item"></slot>
      </div>
    </header>
    <main v-if="$slots.default">
      <slot></slot>
    </main>
    <footer v-if="$slots.footer">
      <slot name="footer"></slot>
    </footer>
  </div>
</template>
<script>
const TITLE_SLOTS_TYPES = ['title-left', 'title-center', 'title-right'] // title-slots类型
export default {
  name: 'widget',
	computed: {
    titleSlots () {
      return TITLE_SLOTS_TYPES.filter(name => this.$slots[name])
    }
  }
}
</script>

① slots 是动态渲染的② titleSlots 值是由 vm.$slots 计算computed而来。

业务中使用代码

<template>
	<widget>
    <template slot="title-left">
      <span>任务名称{{taskName}}</span>
		</template>
		<template slot="title-right">
    	<el-button v-if="condition1">开始报送</el-button>
			<el-button v-else-if="condition2">重新报送</el-button>
			<el-button v-else-if="condition3">报送中</el-button>
		</template>
  </widget>
</template>

condition 条件是通过异步 ajax 请求返回初始状态slot为空② 请求成功后其中某一条件生效但不展示

问题拆解

将上述示例进行简化以便剖解问题。

Test.vue

<template>
  <div>
  	<slot name="content" v-if="isShow"></slot>
  </div>
</template>
<script>
export default {
  name: 'test',
  computed: {
    isShow () {
      return !!this.$slots.content
    }
  },
  created () {
    console.log(this.$slots.content)
  }
}
</script>

App.vue

<template>
 <test ref="test">
   <template slot="content">
     <p v-if="count === 1">1</p>
     <p v-else-if="count === 2">2</p>
     <p v-else-if="count >= 3">3</p>
  	</template>
  </test>
	<button @click="getTestSlots">获取test组件slots</button>
</template>
<script>
import Test from './components/Test.vue'
export default {
  name: 'App',
  components: { Test },
  data () {
    return {
      count: 0
    }
  },
  methods: {
    getTestSlots () {
      console.log(this.$refs['test'].$slots)
    }
  },
  created () {
    // 模拟 ajax
    setTimeout(() => {
      this.count = 3
    }, 3000)
  }
}
</script>

【问题1】slot 内容为空组件内部 vm.$slots 无法获取相应内容

如示例在初始状态任何一个 condition 都不成立此时组件内部 vm.$slots 是获取不到相应 slot 的。

console.log(this.$refs['test'].$slots)  // getTestSlots App.vue 

结果{}

console.log(this.$slots.content)	// created Test.vue

结果undefined

【问题2】$slots 不具备响应性

如示例 等待 3000 ms此时 count 值变成了3但 test 组件依然未渲染
在这里插入图片描述

console.log(this.$refs['test'].$slots)  // getTestSlots App.vue 

结果{content: [VNode]}

isShowComputed未生效但 vm. s l o t s 中是有值的应征了官方 a p i 中提到的“ ‘ v m . slots 中是有值的应征了官方 api 中提到的“`vm. slots中是有值的应征了官方api中提到的“‘vm.slots`不具备响应性”

问题解决

问题的核心组件内依赖 $slots 来判断是否渲染相应的 slot 内容而业务端调用时初始时不存在数据变化时$slots 不具备响应性computed也就不会生效从而相应的 slot 也就无法显示。

【方案1】规避默认不存在 slot 的情况

初始时让相应 slot 具备内容组件中也就无需依赖 computed 。

App.vue

<test ref="test">
  <template slot="content">
		<div>
      <p v-if="count === 1">1</p>
      <p v-else-if="count === 2">2</p>
      <p v-else-if="count >= 3">3</p>
    </div>
  </template>
</test>

增加一层元素<div>确保在自定义组件初始化过程中即可获取 $slots

【方案2】规避到 “vm.$slots不具备响应性 “

改变策略依赖 props 等响应性实例

App.vue

<test ref="test" :isShow="count > 0">
...
</test>

Test.vue

props: ['isShow']

【其他】相同父元素的子元素渲染错误

不使用 keyVue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。

<p v-if="count === 1" key="1">1</p>
<p v-else-if="count === 2" key="2">2</p>
<p v-else-if="count >= 3" key="3">3</p>
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: vue