一、目标
- 能够知道如何使用ref引用DOM和组件实例
- 能够知道$nextTick的调用时机
- 能够说出keep-alive元素的作用
- 能够掌握插槽的基本用法
- 能够知道如何自定义指令
二、目录
- ref引用
- 动态组件
- 插槽
- 自定义指令
- Table案例
ref引用
1.什么是ref引用
ref用来辅助开发者在不依赖于jQuery的情况下,获取DOM元素或组件的引用。
每个vue的组件实例上,都包含一个$refs对象,里面存储着对应的DOM元素或组件的引用。默认情况下,组件的$refs指向一个空对象。
2.使用ref引用DOM元素
如果想要使用ref引用页面上的DOM元素,则可以按照如下的方式进行操作:
3.使用ref引用组件实例
如果想要使用ref引用页面上的组件实例,则可以按照如下的方式进行操作:
<template>
<div>
<h1>这是App.vue根组件</h1>
<hr />
<button type="button" class="btn btn-primary" @click="reset">重置</button>
<ref ref="conterRef"></ref>
</div>
</template>
<script>
import ref from "./components/ref/ref.vue";
export default {
components: {},
name: "MyApp",
data() {
return {};
},
methods: {
reset() {
this.$refs.conterRef.rset();
},
},
components: {
ref,
},
};
</script>
<template>
<div>
<h1>数字:{{ data }}</h1>
<button type="button" @click="add">+1</button>
</div>
</template>
<script>
export default {
data() {
return {
data: 0,
};
},
methods: {
add() {
this.data++;
},
rset() {
this.data = 0;
},
},
};
</script>
4.控制文本框和按钮的按需切换
通过布尔值inputVisible来控制组件中的文本框与按钮的按需切换。示例代码如下:
5.让文本框自动获得焦点
当文本框展示出来之后,如果希望它立即获得焦点,则可以为其添加ref引用,并调用原生DOM对象的.focus()方法即可。示例代码如下:
在这里会输出undefined,原因是 DOM元素的更新是异步的,当执行到这行代码的时候,Dom还没有进行更新。
6.this.$nextTick(cd)方法
组件的$nextTick(cb)方法,会把cd回调推迟到下一个DOM更新周期之后执行。通俗的理解是:等b组件的DOM异步地重新渲染完成后,再执行cd回调函数。从而能保证cd回调函数可以操作到最新的DOM元素。
动态组件
1.什么是动态组件
动态组件指的是动态切换组件的显示与隐藏。vue提供了一个内置的<component>组件,专门用来实现组件的动态渲染。
①<component>是组件的占位符
②通过is属性动态指定要渲染的组件名称
③<component is="要渲染的组件的名称"></component>
2.如何实现动态组件渲染
<template>
<div>
<h1>这是App.vue根组件</h1>
<hr />
<button type="button" class="btn btn-primary" @click="comName = 'ref'">
组件一
</button>
<button type="button" class="btn btn-primary" @click="comName = 'reff'">
组件二
</button>
<component :is="comName"></component>
</div>
</template>
<script>
import ref from "./components/ref/ref.vue";
import reff from "./components/ref/reff.vue";
export default {
components: {},
name: "MyApp",
data() {
return {
comName: "ref",
};
},
methods: {
change() {},
},
components: {
ref,
reff,
},
};
</script>
3.使用keep-alive保持状态
默认情况下,切换动态组件时无法保持组件的状态。此时可以使用vue内置的<keep-alive>组件保持动态组件的状态。示例代码如下:
在切换组件的时,切换组件的时候组件的数据会重置。将component组件用keep-alive包括,可以缓存组件中的数据,切换组件时可以保存数据,不会丢失。
插槽
1.什么是插槽
插槽(Slot)是vue为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。
可以把插槽认为是组件封装期间,为用户预留的内容的占位符。
2.体验插槽的基础用法
如果子组件中有内容不确定可以用slot占位,在父组件中使用子组件是插入即可,会自动渲染到子组件预留的位置。
2.1没有预留插槽的内容会被丢弃
如果在封装组件时没有预留任何<slot>插槽,则用户提供的任何自定义内容都会被丢弃。示例代码如下:
没用slot的,不会被渲染出来
2.2后备内容(默认内容)
封装组件时,可以为预留的<slot>插槽提供后备内容(默认内容)。如果组件的使用者没有为插槽提供任何内容,则后备内容会生效。示例代码如下:
3.具名插槽
如果在封装组件时需要预留多个插槽节点,则需要为每个<slot>插槽指定具体的name名称。这种带有具体名称的插槽叫做“具名插槽”。示例代码如下:
注意:没有指定name名称的插槽,会有隐含的名称叫做"default"。
3.1为具名插槽提供内容
在向具名插槽提供内容的时候,我们可以在一个<template>元素上使用v-slot指令,并以v-slot的参数的形式提供其名称。示例代码如下:
必须使用template包裹,才能用v-slot指定具体的插槽
3.2具名插槽的简写形式
跟v-on和v-bind一样,v-slot也有缩写,即把参数之前的所有内容(v-slot:)替换为字符 #。例如v-slot:header可以被重写为#header
4.作用域插槽
通过插槽由子组件向父组件传递数据,子组件用:info=‘数据’,父组件v-slot:插槽名字=“接收数据”
在封装组件的过程中,可以为预留的<slot>插槽绑定props数据,这种带有props数据的<slot>叫做“作用域插槽”。示例代码如下:
无论子组件传递几个数据,父组件只需要用一个数据接收即可,这个数据是以对象的形式包裹。
4.1解构作用域插槽的Prop
案例一:
案例二:
将prop向父组件传递的数据结构出来{数据1,数据2}
自定义指令
1.什么是自定义指令
vue官方提供了V-for、v-model、v-if等常用的内置指令。除此之外vue还允许开发者自定义指令。
vue中的自定义指令分为两类,分别是:
- 私有自定义指令
- 全局自定义指令
2.声明私有自定义指令的语法
在每个vue组件中,可以在directives节点下声明私有自定义指令。示例代码如下:
4.声明全局自定义指令的语法
全局共享的自定义指令需要通过“单页面应用程序的实例对象”进行声明,示例代码如下:
5.updated函数
mounted函数只在元素第一次插入DOM时被调用,当DOM更新时mounted函数不会被触发。updated函数会在每次DOM更新完成后被调用。示例代码如下:
注意:在vue2的项目中使用自定义指令时,【mounted->bind】【updated->update】
6.函数简写
如果mounted和updated函数中的逻辑完全相同,则可以简写成如下格式:
7.指令的参数值
在绑定指令时,可以通过“等号”的形式为指令绑定具体的参数值,示例代码如下:
Table案例
1.案例效果
2.用到的知识点
- 组件封装
- 具名插槽
- 作用域插槽
- 自定义指令
3.实现步骤
①搭建项目的基本结构
②请求商品列表的数据
③封装MyTable组件
④实现删除功能
⑤实现添加标签的功能
main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import './assets/css/bootstrap.css'
import axios from 'axios'
const app = createApp(App)
axios.defaults.baseURL = 'https://applet-base-api-t.itheima.net';
app.config.globalProperties.$http = axios
app.mount('#app')
App.vue
<template>
<div>
<h1>这是App根组件</h1>
<MyTable :data="goodslist">
<template #header>
<th>#</th>
<th>商品名称</th>
<th>价格</th>
<th>标签</th>
<th>操作</th>
</template>
<template v-slot:body="{ row, index }">
<td>{{ index + 1 }}</td>
<td>{{ row.goods_name }}</td>
<td>¥{{ row.goods_price }}</td>
<td>
<input
type="text"
class="form-control form-control-sm form-ipt"
v-if="row.inputVisible"
v-focus
v-model.trim="row.inputValue"
@blur="onInputConfirm(row)"
@keyup.enter="onInputConfirm(row)"
@keyup.esc="row.inputValue = ''"
/>
<button
type="button"
class="btn btn-primary btn-sm"
v-else
@click="row.inputVisible = true"
>
+Tag
</button>
<!-- 循环渲染标签信息 -->
<span
class="badge badge-warning ml-2"
v-for="item in row.tags"
:key="item"
>{{ item }}</span
>
</td>
<td>
<button
type="button"
class="btn btn-danger"
@click="deleteGoods(row.id)"
>
删除
</button>
</td>
</template>
</MyTable>
</div>
</template>
<script>
import MyTable from "./components/my-table/MyTable.vue";
export default {
name: "MyApp",
data() {
return {
//商品列表的数据
goodslist: [],
};
},
created() {
//发起请求
this.getGoodsList();
},
components: { MyTable },
methods: {
async getGoodsList() {
const { data: res } = await this.$http.get("/api/goods");
if (res.status !== 0) {
return console.log("获取商品列表数据失败!");
}
this.goodslist = res.data;
},
//根据id删除商品
deleteGoods(id) {
this.goodslist = this.goodslist.filter((x) => x.id !== id);
},
onInputConfirm(row) {
const val = row.inputValue;
row.inputValue = "";
row.inputVisible = false;
if (!val || row.tags.indexOf(val) !== -1) {
return;
}
row.tags.push(val);
},
},
directives: {
focus(el) {
el.focus();
},
},
};
</script>
<style lang="less" scoped>
.form-ipt {
width: 80px;
display: inline;
}
</style>
MyTable.vue
<template>
<table class="table table-bordered table-striped">
<!-- 标题区域 -->
<thead>
<tr>
<slot name="header"></slot>
</tr>
</thead>
<!-- 内容主题区域 -->
<tbody>
<tr v-for="(item, index) in data" :key="item.id">
<slot name="body" :row="item" :index="index"></slot>
</tr>
</tbody>
</table>
</template>
<script>
export default {
name: "MyTable",
props: {
data: {
type: Array,
require: true,
default: [],
},
},
Date() {
return {};
},
};
</script>
<style lang="less" scoped>
</style>
总结
①能够知道如何使用ref引用DOM和组件实例
- 通过ref属性指令引用的名称、使用this.$refs访问引用实例
②能够知道$nextTick的调用时机
- 组件的DOM更新之后,才执行$nextTick中的回调
③能够说出keep-alive元素的作用
- 保持动态组件的状态
④能够掌握插槽的基本用法
- <slot>标签、具名插槽、作用域插槽、v-slot:简写为#
⑤能够知道如何自定义指令
- 私有自定义指令、全局自定义指令