九、Vue3 高级特性补充

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

# 1.3.实现方式二:局部自定义指令

  • 实现方式二:自定义一个 v-focus 的局部指令
    • 这个自定义指令实现非常简单,我们只需要在组件选项中使用 directives 即可
    • 它是一个对象,在对象中编写我们自定义指令的名称(注意:这里不需要加v-);
    • 自定义指令有一个生命周期,是在组件挂载后调用的 mounted,我们可以在其中完成操作;
  • Vue2实现

11afcd3cd78708e55.png

2019a97be7632492f.png

# 1.4.实现方式三:自定义全局指令

  • 自定义一个全局的v-focus指令可以让我们在任何地方直接使用
  • vue2实现

vue2.png

  • vue3实现

47ee1cf9667ac9f94.png

# 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:之前的值,仅在 beforeUpdateupdated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。

5f2abfe485741be3c.png

  • 补充:钩子函数的el参数,表示的是使用该指令的元素对象

# 1.7.自定义指令练习之时间格式化指令

  • 自定义指令案例:时间戳的显示需求:
    • 在开发中,大多数情况下从服务器获取到的都是时间戳;
    • 我们需要将时间戳转换成具体格式化的时间来展示;
    • 在Vue2中我们可以通过过滤器来完成;
    • 在Vue3中我们可以通过 计算属性(computed) 或者 自定义一个方法(methods) 来完成;
    • 其实我们还可以通过一个自定义的指令来完成;
  • 我们来实现一个可以自动对时间格式化的指令v-format-time:
    • 这里我封装了一个函数,在首页中我们只需要调用这个函数并且传入app即可;
  • 代码如下图

6e88c1bc0fcbc1f1b.png

# 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 的功能;
  • 我们来看下面代码的效果:

75ccbe728f442905a.png

# 2.2.和组件结合使用

  • 当然,teleport也可以和组件结合一起来使用:
    • 我们可以在 teleport 中使用组件,并且也可以给他传入一些数据;

8753f2b441ac37a42.png

# 2.3.内置组件Teleport(避免错误使用)

  • 注意:目标元素必须在安装组件之前存在,即目标不能由组件本身呈现,理想情况下应该位于整个Vue组件树之外
    • 补充:如果A组件中使用内置组件teleport时,目标不能由A组件本身呈现,否则会报错;理想情况下 目标元素 应该位于整个组件树之外
    • 或者如果沿着DOM树向上找不到的元素(即不是teleport内置组件的祖先元素),也可以指定为目标元素:如下图案例的div.app1元素
    • 像div.app元素,我们发现可以找到,即是teleport内置组件的祖先元素;所以不可指定为目标元素;
      • 得出结论:teleport内置组件的祖先元素不可指定为teleport的目标元素
    • 特殊情况:当然有一种情况,目标元素可以是teleport内置组件的祖先元素,这个目标祖先元素必须是body元素的子元素

9ecb09c9017a24889.png

# 2.4.多个teleport

  • 如果我们将多个teleport应用到同一个目标上(to的值相同),那么这些目标会进行合并
  • 实现效果如下:

1062d294108238dd21.png

# 3、异步组件和Suspense内置组件

  • 注意:目前(2022-08-28)Suspense显示的是一个实验性的特性,API随时可能会修改。Suspense内置组件 (opens new window)
  • Suspense是一个内置的全局组件,该组件有两个插槽:
    • default:如果default可以显示,那么显示default的内容;
    • fallback:如果default无法显示,那么会显示fallback插槽的内容;

119afa2130eab58af3.png

# 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

12585728a97016504a.png

# 5、

# 6、

# 7、

# 8、

# 9、

# 10、

# 11、

# 12、

最后更新时间: 2022/09/13, 00:20:26
彩虹
周杰伦