Vue基础3

插槽

1.插槽的作用:父组件向子组件传递内容,为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。
无非就是在 子组件 中挖个坑,坑里面放什么东西由 父组件 决定
2.<slot> 元素是一个插槽出口 (slot outlet),标示了父元素提供的插槽内容 将在哪里被渲染

<FancyButton>
  Click me! <!-- 插槽内容 -->
</FancyButton>

<button class="fancy-btn">
  <slot></slot> <!-- 插槽出口 -->
</button>

`要想放入到指定位置就使用具名插槽`
	父组件中 要配合<template></template> 使用,在template中取名
	子组件中 在slot中命名   
   ` template要与slot中的命名相同`

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单个(匿名)插槽

`基础用法  单个(匿名)插槽`
//父组件
<template>
  <div>
    <!-- 下面这种形式叫插槽(双标签形式) -->
    <ChildA>你好</ChildA>
    <ChildA><input type="text"></ChildA>
    <ChildA></ChildA>
  </div>
</template>

<script>
import ChildA from "@/components/slotsView/ChildA.vue"
export default {
  name: "SlotsView",
  components: {
    ChildA
  }
}
</script>

//子组件
<template>
  <div>
    a
    <slot>默认值</slot>
    b
  </div>
</template>

<script>
export default{
  name:"childA"
}
</script>

案例 使用插槽渲染表格

`插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的,插槽内容无法访问子组件的数据`

`父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域`

//父组件
<template>
  <div>
    <ChildB>
      <!-- 
        list 是一个数组 item是每一项值是对象类型
        index是对应值的下标

        key是item中对应属性名 value是key对应的属性值
      -->
      <tr v-for="(item, index) in list" :key="index">
        <td v-for="(value, key) in item" :key="key">{{ value }}</td>
      </tr>
    </ChildB>

  </div>
</template>

<script>
import ChildB from "@/components/slotsView/ChildB.vue"

export default {
  name: "SlotsView",
  components: {
    ChildB
  },
  data() {
    return {
      list: [
        { name: "张三", age: 30, sex: "男", tel: "18617263823", img: "/img/3.jpg" },
        { name: "李四", age: 21, sex: "女", tel: "18617342823", img: "/img/4.jpg" },
        { name: "王五", age: 23, sex: "男", tel: "18617262345", img: "/img/5.jpg" },
        { name: "赵六", age: 24, sex: "女", tel: "18612452823", img: "/img/6.jpg" },
      ]
    }
  }
}
</script>

//子组件
<template>
  <table>
    <thead>
      <tr>
        <th>name</th>
        <th>sex</th>
        <th>age</th>
        <th>tel</th>
      </tr>
    </thead>
    <tbody>
    //slot 获取到父组件值传递的内容
      <slot></slot>
    </tbody>
  </table>
</template>

<script>
export default {
  name: "childB"
}
</script>

<style>
table {
  width: 1200px;
  border-collapse: collapse;
}

th,
td {
  height: 30px;
  line-height: 30px;
  text-align: center;
  border: 1px solid #000;
}
</style>

具名插槽

//如果当前有一个以上的插槽,我们就需要给每个插槽具名,设置name

//父组件
<template>
  <div>
    <!-- 会自动放入默认的插槽中 -->
    <!-- <ChildC>abcde</ChildC> -->

    <ChildC>
    //通过设置的name 把内容放到对应的slot中
      <template #header>
        <input type="text">
      </template>

      <template #footer>
        <button>按钮</button>
      </template>
    </ChildC>
  </div>
</template>

<script>
import ChildC from "@/components/slotsView/ChildC.vue"
export default {
  name: "SlotsView",
  components: {
    ChildC
  },
  data() {
    return {
    }
  }
}
</script>

//子组件
<template>
  <div>
    a
    <slot name="header"></slot>
    b
  </div>
  <hr />
  <div>
    a
    <slot name="footer"></slot>
    b
  </div>
  <p>
//我们如果希望其中有一个是默认插槽,正常给组件标签填入的内容,就会自动放入这个默认的插槽中		默认插槽名必须起名为defalut
    <slot name="default"></slot>
  </p>
</template>

<script>
export default {
  name: "ChildC"
}
</script>

条件插槽

//根据插槽是否存在来渲染某些内容,结合使用 $slots 属性与 v-if 来实现
<template>
  <Card>
    <template #header>
      <h1>This is the header</h1>
    </template>

    <template #default>
      <p>This is the content</p>
    </template>

    <template #footer>
      <em>This is the footer</em>
    </template>
  </Card>
</template>


<template>
  <div class="card">
      // 若$slots.header 为真就把内容放到下面的插槽中
    <div v-if="$slots.header" class="card-header">
      <slot name="header" />
    </div>
    
    <div v-if="$slots.default" class="card-content">
      <slot />
    </div>
    
    <div v-if="$slots.footer" class="card-footer">
      <slot name="footer" />
    </div>
  </div>
</template>

动态插槽名

#[变量名] 表示动态插槽名 可以根据插槽变量的变化将内容放在不同的插槽中
<template #[变量名]>
     <input type="text">
</template>

//父组件
<template>
  <div>
    <ChildD>
      <template #[slotName]>
        <input type="text">
      </template>
    </ChildD>
// 点击设置不同的name 改变input 放入的位置
    <button @click="slotName = 'content'">按钮</button>
  </div>
</template>

<script>
import ChildD from "@/components/slotsView/ChildD.vue"
export default {
  name: "SlotsView",
  components: {
    ChildD
  },
  data() {
    return {
      slotName:"header"
    }
  }
}
</script>

//子组件
<template>
  <div>
    <h3>
      h3
      <slot name="header"></slot>
    </h3>
    <div>
      div
      <slot name="content"></slot>
    </div>
  </div>

</template>

<script>
</script>

作用域插槽

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

`父组件可以通过插槽给子组件传递一些值
子组件也可以通过插槽返回一些值`

//父组件
  <div>
    <!-- 向子组件传入数据 -->
    <ChildE :data="data">
//<!-- 获取到子组件返回的key key是对象中的每一个 键名 -->
      <template #header="{ key }">
//<!-- 给 name是header的slot传入内容 -->
//<!-- 判断 若对应key是checked 则在其中加入一个checkbox控件 -->
//<!-- v-model="allChecekd 给checkbox控件设置初始值 为不被选中 添加一个点击事件 -->
	   <span v-if="key == 'checked'"><input type="checkbox" id="all" v-model="allChecekd"
            @click="checkedHandler"></span>
//<!-- 此处是表头内容 应该为每一个键名 -->
        <span>{{ key }}</span>
      </template>
//<!-- 获取到子组件返回的value, key, index -->
      <template #content="{ value, key, index }">
//<!-- 给 name是content的slot传入内容 -->
//<!-- 判断 若对应key是checked 则在其中加入一个checkbox控件 -->
//<!-- :checked="value" 因为判断是key是checked 所以此处value是 对象中checked对应的值 -->
        <span v-if="key == 'checked'"><input type="checkbox" :checked="value"
            @click="e => checkedHandler(e, index)"></span>
//<!-- 其余的放入对应的值 -->
        <span v-else>{{ value }}</span>
      </template>
    </ChildE>
  </div>


<script>
import ChildE from "@/components/slotsView/ChildE.vue"

export default {
  name: "SlotsView",
  components: {
    ChildE
  },
  data() {
    return {
      list: [
        { name: "张三", age: 30, sex: "男", tel: "18617263823", img: "/img/3.jpg" },
        { name: "李四", age: 21, sex: "女", tel: "18617342823", img: "/img/4.jpg" },
        { name: "王五", age: 23, sex: "男", tel: "18617262345", img: "/img/5.jpg" },
        { name: "赵六", age: 24, sex: "女", tel: "18612452823", img: "/img/6.jpg" },
      ],
      data: [
        { checked: false, id: 1001, name: "商品1", price: 1023, num: 1, total: 1023 },
        { checked: true, id: 1002, name: "商品2", price: 1123, num: 1, total: 1123 },
        { checked: false, id: 1003, name: "商品3", price: 1223, num: 1, total: 1223 },
        { checked: false, id: 1004, name: "商品4", price: 1323, num: 1, total: 1323 },
      ],
      allChecekd: false
    }
  },
  methods: {
    checkedHandler(e, index) {
      // e.target 是当前点击的checkbox控件
      // 判断 是否是全选的那个控件
      if (e.target.id === "all") {
        // 把全选控件的checked值赋给 下面所有的checkbox控件
        // 即全选控件的checked的值为 true则全选,反之不全选
        this.data.forEach(item => item.checked = e.target.checked);
      } else {
        // 对下面的每个checkbox控件 进行设置
        this.data[index].checked = e.target.checked;
        // 当每项都为true时 通过every()方法会返回一个true 赋值给allChecekd 使全选控件为被选中状态
        this.allChecekd = this.data.every(item => item.checked)
      }
    }
  }
}
</script>
<style>
.img {
  width: 80px;
}
</style>

//子组件
<template>
  <div>
    <table>
      <thead>
        <tr>
// <!-- 根据数据的数量设置 -->
//<!-- data是从父组件获取的数组 -->
//<!-- data[0]是数组中的第一项 是一个对象 -->
// <!-- 下面的操作遍历data[0] 这个对象 获取到key和value -->
          <th v-for="(value, key) in data[0]" :key="key">
//<!-- 给slot设置name->header 把获取到的key返回给父组件 -->
            <slot name="header" :key="key"></slot>
          </th>
        </tr>
      </thead>
      <tbody>
//<!-- item是数组中的每一项  index是每一项对应的索引 -->
        <tr v-for="(item, index) in data" :key="index">
//<!-- key是对应item中的键值   value是键值对应的属性名 -->
          <td v-for="(value, key) in item" :key="key">
//<!-- 把获取到的value key index 返回给父组件-->
            <slot name="content" :value="value" :key="key" :index="index"></slot>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  name: "ChildE",
  props: ["data"],

}
</script>

<style>
table {
  width: 1200px;
  border-collapse: collapse;
}
th,td {
  height: 30px;
  line-height: 30px;
  text-align: center;
  border: 1px solid #000;
}
</style>

依赖注入

`使用的原因:`有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据

1.provide 放在上层组件中,直接为组件后代提供数据,不需要再使用组件传值
2.inject 放在下层组件中,直接获取上层组件提供的数据,注入在下层组件中

provide的两种写法 	1.对象形式provide:{
				  // 生命周期在data之前,不能调用到data中数据
					}	
	`建议使用这种方式` 2.函数形式provide(){
				  // 生命周期在data之后,created之前,能调用到data中数据
				   }
                   
inject两种写法  1.数组形式   2.对象形式                  
1.通过对象形式	给注入的变量重新命名
2.default设置初始默认值(到上次没有注入时,就会使用)
3.default的匿名函数形式 有this

default:function(){
	//默认的生命周期时间早于data的时间,所以无法拿到data的值
	//可以调用props、$attrs中数据
}
`使用`
//根组件
<template>
  <div>
    <ChildA :count="count" />
        // 这里的点击事件不能让下层的sum改变
    <button @click="sum++">加加</button>
  </div>
</template>

<script>
import ChildA from '@/components/injectView/ChildA.vue'

export default {
  name: 'InjectView',
  components: {
    ChildA
  },
  data() {
    return {
      count: 1,
      a: 500
    }
  },

  // provide 写法1 对象形式
  provide: {
    sum:100,
  }

  // provide 写法2 函数形式
  provide() {
    return {
      sum: 10,
      // sum:this.a, 可以获取到data中的值
    }
  }
}
</script>

//父组件
<template>
  <div>
   //<!-- 向下层元素 传入count lastNum的值-->
    <ChildB :count="count" :lastNum="lastNum" />
  </div>
</template>

<script>
import ChildB from "@/components/injectView/ChildB.vue";

export default {
  name: "ChildA",
  components: {
    ChildB
  },
  // 从根组件获取count值
  props: ["count"],
  data() {
    return {
      lastNum: 99
    }
  }
}
</script>

//子组件
<template>
  <div>
   // <!-- 从根组件中 通过ChildA获取到的count -->
    {{ count }}
    //<!-- 从上一组件ChildA中获得的lastNum -->
    {{ lastNum }}
    //<!-- 直接在根组件中获取的值 -->
    {{ sumA }}
  </div>
</template>

<script>
export default {
  name: "ChildB",
  props: ["count", "lastNum"],
  data() {
    return {
      b:200
    }
  },
  
  // inject 写法1 数组形式
  inject: ["sum"],

  // inject 写法2 对象形式
  inject: {
    sumA:{
      from:"sum",

      // default写法1
      default:0

       // default写法2
      default:function(){
         //默认的生命周期时间早于data的时间,所以无法拿到data的值
         // 可以调用props、$attrs中数据
         return this.lastNum+10
      }
    }
  }
}
</script>
`下下层组件给根组件传值`
// 父组件
<template>
  <div>
    <ChildC/>
    <button @click="count++">按钮</button>
  </div>
</template>

<script>
import ChildC from '@/components/injectView/ChildC.vue'
export default{
  name:"InjectView",
  components:{
    ChildC
  },
  data(){
    return{
      count:1
    }
  },
  provide(){
    return{
        // 定义了一个函数 穿给下层组件
      handler:this.callbackHandler,
      count:this.count
    }
  },
  methods:{
      // 其中的num 是从下层组件中获取的
    callbackHandler(num){
      console.log(num+this.count)
    }
  }
}
</script>

//中介组件
<template>
  <div>
    <ChildD/>
  </div>
</template>

<script>
// ChildC 充当中介
import ChildD from "@/components/injectView/ChildD.vue";
export default{
  name:"ChildC",
  components:{
    ChildD
  }
}
</script>

//下层组件
<template>
    // 使用根组件 传来的函数 写入一个参数 返回给根组件
    // 实现 下下层组件给根组件传值
  <button @click="handler(10)">传出值</button>
  {{ count }}
</template>

<script>
export default {
  name: "ChildD",
      //获取到 根组件 传来的数据
  inject: ["handler", "count"]
}
</script>

配合 computed()

`要使用set,get才能实现数据改变`
`否则computed 只是一个只读属性不能变化`
1.computed() 放在provide中

2.使用computed计算属性后,当数据发生改变时,就会触发重新注入数据到子元素
(所以在使用inject注入时要使用computed() 让上层组件的值变化时,下层组件的值也变化)

3.使用computed()setget可以实现双向绑定,但是setget必须写成箭头函数


// 父组件
<template>
  <div>
    <ChildC />
    // 点击 count++ 下层中的 count也会变化
    <button @click="count++">按钮</button>
  </div>
</template>

<script>
import ChildC from '@/components/injectView/ChildC.vue'
	`注:要导入computed`
import { computed } from 'vue';
export default {
  name: "InjectView",
  components: {
    ChildC
  },
  data() {
    return {
      count: 1
    }
  },
  provide() {
    return {
      handler: this.callbackHandler,
      count: computed({
        set: (val) => {
          this.count = val;
        },
        get: () => {
          return this.count
        }
      })
    }
  },
  methods: {
    callbackHandler(num) {
      console.log(num + this.count)
    }
  }
}
</script>

// 中间组件
<template>
  <div>
    <ChildD/>
  </div>
</template>

<script>
// ChildC 充当中介
import ChildD from "@/components/injectView/ChildD.vue";

export default{
  name:"ChildC",
  components:{
    ChildD
  }
}
</script>

//子组件
<template>
  {{ count }}
  <button @click="count++">修改</button>
</template>

<script>
export default {
  name: "ChildD",
  inject: ["count"]
}
</script>
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值