插槽,自定义指令及组件进阶Api

519 阅读3分钟

插槽

当组件的部分内容不确定,可以使用插槽作为占位,而让组件的使用者调用插槽

基本插槽

声明插槽

当组件的标签也不确定的时候,可以使用 slot 作为插槽占位

<slot> 这就是一个简单的插槽 </slot>

使用插槽

直接在使用的组件标签内部写入内容

<panel>
    插槽内容填在这里   
    <p>
    我是一个最简单的插槽 
    </p>
</panel>

填入的内容会将 slot 标签覆盖掉

插槽默认内容

如果 slot 默认为空的时候,页面上不会有任何关于插槽内容的显示,所以这个时候就需要使用插槽的——默认内容

<slot> 默认的内容写在这里 </slot>

如果其他的组件调用了组件,那么将会用新的内容去对 slot 标签进行覆盖

具名插槽

如果一个组件内存在有多个插槽,为了区分插槽的调用,这时候可以通过给插槽命名来区分插槽

<slot name="插槽名"></slot>

调用

具名插槽的调用只能写道 template 标签上

<tempalte v-slot:插槽名>
	插槽内容
</tempalte>

插槽名的调用和声明必须一致,否则会因为插槽名不一致而不显示

作用域插槽

插槽无法通过 slot 标签正常访问到组件中的变量,需要插槽组件主动向插槽内传值

<slot :row="需要传入的值"></slot>

插槽需要主动接取数据

<tempalte v-slot="接取变量">
	插槽内容
</tempalte>

此时就已经完成了插槽间的传值,传入的值是一个对象,且值为: { row:需要传入的值。。。 }

传入的值是以传值的自定义属性名作为 传入的值对象 的属性名,值就是自定义属性名的值

具名作用域插槽

同上面的,插槽需要通过 slot 标签进行传值

<slot :row="data"></slot>

而插槽也需要主动接取数据

<tempalte v-slot="scope"></tempalte>

但是具名插槽需要在 v-slot 添加名字

<template v-slot:插槽名="scope">
	插槽内容
</template>

即可

循环具名插槽的传参

当循环创建组件的过程中,也需要将插槽抽离出来,而配合 v-for 来使用

<slot v-for="item in arr" :key="item.id" :row="data"></slot>

使用

<template #body="{ row: scope }">
    <td>{{ scope.id }}</td>
    <td>{{ scope.goods_name }}</td>
    <td>{{ scope.goods_price }}</td>
    <td>
        <button
                class="btn btn-primary btn-sm"
                v-if="!scope.inputVisible"
                @click="changeIsShow(scope.id)"
                >
            +Tag
        </button>
        <input
               type="text"
               v-if="scope.inputVisible"
               @blur="scope.inputVisible = false"
               ref="inp"
               style="width: 100px"
               v-model="scope.inputValue"
               @keydown.enter="add(scope.id)"
               />
        <br />
        <span
              style="margin-right: 5px"
              class="badge bg-secondary"
              v-for="(item, index) in scope.tags"
              :key="index"
              >
            {{ item }}</span
            >
    </td>
    <td>
        <button class="btn btn-danger btn-sm" @click="remove(scope.id)">
            删除
        </button>
    </td>
</template>

自定义指令

概念

当 Vue 内置的指令无法满足需求的时候,就需要使用自定义指定来操作

注册指令

局部注册

App.vue
export default{
    //注册指令成员
    directive:{
      //声明指令
        指令名:{
            inserted(el) {
                inserted 是一个钩子函数,当被添加指令的元素挂载到 DOM 树上调用
                el 是 inserted 钩子函数的唯一形参,返回的就是绑定指令的元素的 DOM 对象
            }
        }
    }
}

全局注册

main.js

Vue.directive("指令名",{
    inserted(el) {
        inserted 是一个钩子函数,当被添加指令的元素挂载到 DOM 树上的时候调用
        el 是 inserted 钩子函数的唯一形参,返回的是被绑定元素的 DOM 对象
    }
})

使用指令

直接添加前缀 v- 然后绑定到标签元素即可

<template v-自定义指令名></template>

组件进阶 Api

$refs

在 Vue 中是可以直接获取到 原生的 DOM 对象,需要配合 $refs 来使用

绑定 $refs

需要在被获取的 DOM元素 的标签上绑定 ref 属性

<div ref="绑定的属性名">
    标签内容
</div>

调用绑定获取DOM

被绑定ref 的标签会被添加进 $refs 对象里面

this.$refs.被绑定的属性名

即可获取到

调用组件方法和属性

因为组件在是使用的时候也是一个标签,所以也可以通过 $refs 来获取组件对象

<panel ref="components"></panel>

获取并调用其组件的方法

this.$refs.components.fn()

console.log(this.$refs.components.data)

$nextTick

因为 在data 更新后,更新 DOM 的操作为异步操作,无法直接获取到修改后的 DOM ,所以需要使用 $nextTick 来获取 DOM 元素

$nextTick 在下一轮DOM 更新后会自动调用

this.$nextTick( ()=>{ 回调函数体 } )

为什么DOM的更新会是异步操作

因为如果 DOM 的更新是同步的操作,那么每当 data 发生更细的时候都会去更新 DOM ,为了避免频繁操作 DOM ,所以 Vue 的设计者 犹大 将DOM 的更新设计成异步操作,为的就是所有同步的data修改完毕后集中操作 DOM ,以提高性能

v-model原理

v-model 实际上就只做了两件事情

1.给标签动态绑定了一个 value 属性

2.给标签绑定了 input 事件

只要 input 事件被触发,那么就会 对 value 的值修改,然后又会把 value 的值返回给自身

这样就形成了双向的数据绑定,单向的动态属性绑定(数据驱动视图),和单向的事件绑定组合在一起形成了双工工作线