Vue作用域插槽的复杂应用

Vue的作用域插槽是融合父组件和子组件的终极方案,父子组件最终渲染出来页面由父组件和子组件两方的数据加工而成,在构建组件的时候可以灵活的根据业务的需要去规划业务数据的归属,是放到父组件去处理,还是封装到子组件里面去渲染,没有定论,依赖需求;

在slot上使用v-bind(或者语法糖“:”)那么传入的是父级组件的数据,使用v-slot 那么传入的子组件的数据;并且申明了v-slot的插槽内依然可以使用父级组件内的所有状态,只是申明的作用域可以越级访问到子组件的数据而已;

这里列举一个典型复杂样例来说明这个事实:

文件结构:

Vue作用域插槽的复杂应用_Vue

首先,我们看父级组件Home.vue的内容:

<template>
 <child>
   
   <template v-slot="user">
     <div
       class="nav-bar"
       v-nav-active="{
         className: 'nav-item',
         activeClass: 'nav-active',
         currentIndex,
       }"
     >
       <div
         v-for="(item, index) in items"
         :key="index"
         @click="changeIndex(index)"
         class="nav-item"
       >
         {{ item }}
       </div>
     </div>

     <ul>
       <li v-for="(item, index) in user.data" :key="index">{{ item }}</li>
     </ul>
   </template>
 </child>
</template>

<script>
import NavActive from "@/directives/NavActive";
import child from "@/components/test";

export default {
 name: "Home",
 directives: {
   NavActive,
},
 components: { child },

 data() {
   return {
     items: ["项目一", "项目二", "项目三"],
     currentIndex: 0,
  };
},

 methods: {
   changeIndex(index) {
     console.log(index);
     this.currentIndex = index;
  },
},
};
</script>

<style scoped>
.nav-active {
 background: black;
 color: white;
}
.nav-item {
 float: left;
 width: 33%;
 height: 100%;
 line-height: 50px;
 text-align: center;
}
.nav-active {
 background: black;
 color: white;
}
</style>

子组件Test.vue的内容:

<template>
 <div class="child">

   <h3>这里是子组件</h3>
  // 作用域插槽
   <slot  :data="data"></slot>
 </div>
</template>

<script>
export default {
   data: function(){
     return {
       data: ['zhangsan','lisi','wanwu','zhaoliu','tianqi','xiaoba']
    }
  }
}
</script>

自定义指令文件NavActie.js内容:用来动态显示nav-bar的选择高亮状态:

export default {
   bind(el, binding){
       const {className,activeClass,currentIndex} = binding.value
       const childen = el.getElementsByClassName(className);
       childen[currentIndex].className += ` ${activeClass}`;
  }
  ,update(el, binding){
       // console.log(binding)        

       const {className,activeClass,currentIndex} = binding.value
       const childen = el.getElementsByClassName(className);

       childen[binding.oldValue.currentIndex].className = className; //将原来的元素的样式撤销,注意oldValue的大小写

       childen[currentIndex].className += ` ${activeClass}`;


  }
}

截图来加以说明:

Vue作用域插槽的复杂应用_Vue_02

上图表明,我们依然可以在template里面访问到父级组件内所有定义的样式和状态,并不是说slot通过v-slot声明了作用域就只能使用子组件的状态,父级的所有内容依然可以全部取得,父级的直接取用,子组件的通过v-slot去获取,注意变量名避免冲突就可以了;

这种直接在作用域插槽里面直接插入父组件模板,好处是不用去子组件内部再定义一个普通插槽并使用prop去绑定,父级组件调用子组件的时候非常灵活,因为子组件不太能预知父级组件需要安插啥内容并不可预知插槽的数量,另外,如果在子组件内去做封装,那么想自定义子组件内部DOM节点的样式,需要使用样式穿透,而使用以上方法就不需要。

最终效果:

Vue作用域插槽的复杂应用_Vue_03

我们可以看到所有样式,自定义指令,还有父子级的数据全部渲染成功!