九、Vue3 高级特性补充
Lyk 2022/8/28 Vue自定义指令directive内置组件TeleportVue.use插件
# 1、自定义指令directive
# 1.1.认识自定义指令
- 在Vue的模板语法中我们学习过各种各样的指令:v-show、v-for、v-model等等,除了使用这些指令之外,Vue也允许我们来自定义自己的指令。
- 注意:在Vue中,代码的复用和抽象主要还是通过组件;
- 通常在某些情况下,你需要对DOM元素进行底层操作,这个时候就会用到自定义指令;
- 自定义指令分为两种:
- 自定义局部指令:组件中通过 directives 选项,只能在当前组件中使用;
- 自定义全局指令:app的 directive 方法,可以在任意组件中被使用;
- 比如我们来做一个非常简单的案例:当某个元素挂载完成后可以自定获取焦点
- 实现方式一:如果我们使用默认的实现方式;
- 实现方式二:自定义一个 v-focus 的局部指令;
- 实现方式三:自定义一个 v-focus 的全局指令;
- 注意:vue2跟vue3的自定义指令中的钩子即 生命周期是有一定的区别的;
# 1.2.实现方式一:聚焦的默认实现
- vue3语法默认实现
<template>
<div class="test">
<input type="text" ref="inputRef" />
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const inputRef = ref(null)
onMounted(() => {
inputRef.value.focus()
})
</script>
<style lang="less" scoped></style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1.3.实现方式二:局部自定义指令
- 实现方式二:自定义一个 v-focus 的局部指令
- 这个自定义指令实现非常简单,我们只需要在组件选项中使用 directives 即可;
- 它是一个对象,在对象中编写我们自定义指令的名称(注意:这里不需要加v-);
- 自定义指令有一个生命周期,是在组件挂载后调用的 mounted,我们可以在其中完成操作;
- Vue2实现
# 1.4.实现方式三:自定义全局指令
- 自定义一个全局的v-focus指令可以让我们在任何地方直接使用
- vue2实现
- vue3实现
# 1.5.指令的生命周期
一个指令定义对象可以提供如下几个钩子函数 (均为可选):(Vue2)
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind
:只调用一次,指令与元素解绑时调用。- 具体每个钩子函数有哪些参数,具体参考:Vue2钩子函数的参数 (opens new window)
一个指令定义的对象,Vue提供了如下的几个钩子函数:(Vue3)
created
:在绑定元素的 attribute 或事件监听器被应用之前调用;beforeMount
:当指令第一次绑定到元素并且在挂载父组件之前调用;mounted
:在绑定元素的父组件被挂载后调用;beforeUpdate
:在更新包含组件的 VNode 之前调用;updated
:在包含组件的 VNode 及其子组件的 VNode 更新后调用;beforeUnmount
:在卸载绑定元素的父组件之前调用;unmounted
:当指令与元素解除绑定且父组件已卸载时,只调用一次;- 具体每个钩子函数有哪些参数,具体参考:vue3钩子函数的参数 (opens new window)
# 1.6.指令的参数和修饰符
- 如果我们指令需要接受一些参数或者修饰符应该如何操作呢?结合下图进行学习
- info是参数的名称;
- nba,cba是修饰符的名称;
- 后面是传入的具体的值:
'你好'
;
- 在我们的生命周期中,我们可以通过 binding 获取到对应的内容
value
:传递给指令的值。例如在v-my-directive="1 + 1"
中,值是2
。oldValue
:之前的值,仅在beforeUpdate
和updated
中可用。无论值是否更改,它都可用。arg
:传递给指令的参数 (如果有的话)。例如在v-my-directive:foo
中,参数是"foo"
。modifiers
:一个包含修饰符的对象 (如果有的话)。例如在v-my-directive.foo.bar
中,修饰符对象是{ foo: true, bar: true }
。instance
:使用该指令的组件实例。dir
:指令的定义对象。
- 补充:钩子函数的el参数,表示的是使用该指令的元素对象
# 1.7.自定义指令练习之时间格式化指令
- 自定义指令案例:时间戳的显示需求:
- 在开发中,大多数情况下从服务器获取到的都是时间戳;
- 我们需要将时间戳转换成具体格式化的时间来展示;
- 在Vue2中我们可以通过过滤器来完成;
- 在Vue3中我们可以通过 计算属性(computed) 或者 自定义一个方法(methods) 来完成;
- 其实我们还可以通过一个自定义的指令来完成;
- 我们来实现一个可以自动对时间格式化的指令v-format-time:
- 这里我封装了一个函数,在首页中我们只需要调用这个函数并且传入app即可;
- 代码如下图
# 2、内置组件Teleport
# 2.1.认识Teleport
在组件化开发中,我们封装一个组件A,在另外一个组件B中使用:
- 那么组件A中template的元素,会被挂载到组件B中template的某个位置;
- 最终我们的应用程序会形成一颗DOM树结构;
但是某些情况下,我们希望组件不是挂载在这个组件树上的,可能是移动到Vue app之外的其他位置:
- 比如移动到body元素上,或者我们有其他的div#app之外的元素上;
- 这个时候我们就可以通过teleport来完成;
- 注意:目标元素必须在安装组件之前存在,即目标不能由组件本身呈现,理想情况下应该位于整个Vue组件树之外
- 如果A组件中使用内置组件teleport时,目标不能由A组件本身呈现,否则会报错;理想情况下应该位于整个组件树/DOM树之外, 或该DOM树的根(指body的子元素)
Teleport是什么呢?
- 它是一个Vue提供的内置组件,类似于react的Portals;
- teleport翻译过来是心灵传输、远距离运输的意思;
- teleport内置组件它有两个属性:
- ➢ to:指定将其中的内容移动到的目标元素,可以使用选择器;
- ➢ disabled:是否禁用 teleport 的功能;
我们来看下面代码的效果:
# 2.2.和组件结合使用
- 当然,teleport也可以和组件结合一起来使用:
- 我们可以在 teleport 中使用组件,并且也可以给他传入一些数据;
# 2.3.内置组件Teleport(避免错误使用)
- 注意:目标元素必须在安装组件之前存在,即目标不能由组件本身呈现,理想情况下应该位于整个Vue组件树之外
- 补充:如果A组件中使用内置组件teleport时,目标不能由A组件本身呈现,否则会报错;理想情况下 目标元素 应该位于整个组件树之外
- 或者如果沿着DOM树向上找不到的元素(即不是teleport内置组件的祖先元素),也可以指定为目标元素:如下图案例的div.app1元素
- 像div.app元素,我们发现可以找到,即是teleport内置组件的祖先元素;所以不可指定为目标元素;
- 得出结论:teleport内置组件的祖先元素不可指定为teleport的目标元素
- 特殊情况:当然有一种情况,目标元素可以是teleport内置组件的祖先元素,这个目标祖先元素必须是body元素的子元素
# 2.4.多个teleport
- 如果我们将多个teleport应用到同一个目标上(to的值相同),那么这些目标会进行合并:
- 实现效果如下:
# 3、异步组件和Suspense内置组件
- 注意:目前(2022-08-28)Suspense显示的是一个实验性的特性,API随时可能会修改。Suspense内置组件 (opens new window)
- Suspense是一个内置的全局组件,该组件有两个插槽:
- default:如果default可以显示,那么显示default的内容;
- fallback:如果default无法显示,那么会显示fallback插槽的内容;
# 4、认识Vue插件(vue.use(object/function))
# 4.1.认识Vue插件
- 通常我们向Vue全局添加一些功能时,会采用插件的模式,它有两种编写方式:
- 对象类型:一个对象,但是必须包含一个 install 的函数,该函数会在安装插件时执行;
- 函数类型:一个function,这个函数会在安装插件时自动执行;
- 插件可以完成的功能没有限制,比如下面的几种都是可以的:
- 添加全局方法或者 property,通过把它们添加到 config.globalProperties 上实现;
- 添加全局资源:指令/过滤器/过渡等;
- 通过全局 mixin 来添加一些组件选项;
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能;
# 4.2.插件的编写方式
- 对象类型的写法
- 函数类型的写法
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
//1.对象类型的写法
app.use({
name: 'kobe',
install(app) {
console.log(app)
console.log(this.name)//'kobe'
console.log(
'当我使用use安装插件,且传入的是对象时,会调用该对象的install方法;且会将app作为install方法的参数传入'
)
}
})
//2.函数类型的写法
app.use(function (app) {
console.log(app)
console.log(
'当我使用use安装插件,且传入的是函数时,会直接调用该函数;且会将app作为该函数的参数传入'
)
})
app.mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26