持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
自定义指令
在Vue中,代码的复用和抽象主要还是通过组件,但是在需要对DOM元素进行底层操作的时候,使用自定义指令会更为的方便
| 分类 | 说明 |
|---|---|
| 自定义局部指令 | 组件中通过 directives 选项,只能在当前组件中使用 |
| 自定义全局指令 | app的 directive 方法,可以在任意组件中被使用 |
局部指令
options api
<template>
<div>
<input type="text" v-focus>
</div>
</template>
<script>
// 自定义指令实现非常简单,我们只需要在组件选项中使用 directives 即可
// 它是一个对象,在对象中编写我们自定义指令的名称(注意:这里不需要加v-)
export default {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
}
</script>
composition api
<template>
<div>
<input type="text" v-focus>
</div>
</template>
<script setup>
// 当setup中的变量以v开头的小驼峰命名的时候
// vue会将其解析名为v-focus的自定义指令
const vFocus = {
mounted(el) {
el.focus()
}
}
</script>
全局指令
/directives/focus.js
// 导出函数,以便于外部传入对应的app实例
export default function registerFocus(app) {
app.directive('focus', {
mounted(el) {
el.focus()
}
})
}
/directives/index.js
import registerFocus from './focus'
// 如果存在多条指令的时候 统一在这里进行注册
// 避免在main.js中进行过多的指令注册
export default function registerDirectives(app) {
registerFocus(app)
}
/main.js
import { createApp } from 'vue'
import App from './App.vue'
import useDirectives from './directives/index'
const app = createApp(App)
useDirectives(app)
app.mount('#app')
生命周期函数
Tips: 组件中的生命周期钩子的命名方式类似于options api中生命周期钩子的命名方式,是不以on开头的
| 函数 | 说明 |
|---|---|
| created | 在绑定元素的 attribute 或事件监听器被应用之前调用 元素被创建完,但是参数和修饰符没有生效且元素没有被挂载到dom中 |
| beforeMount | 当指令第一次绑定到元素并且在挂载父组件之前调用 |
| mounted | 在绑定元素的父组件被挂载后调用 |
| beforeUpdate | 在更新包含组件的 VNode及其子组件的 VNode 之前调用 |
| updated | 在包含组件的 VNode 及其子组件的 VNode 更新后调用 |
| beforeUnmount | 在卸载绑定元素的父组件之前调用 |
| unmounted | 当指令与元素解除绑定且父组件已卸载时,只调用一次 |
参数和修饰符
# v-model - 指令
# title - 参数
# .lazy, .number - 修饰符 可以链式调用
# 'value' - 值 - 可以是任意数据类型,如字符串,对象等
# - 指令的值必须是js表达式,所以如果直接绑定对应字符串的时候,必须加上引号以便于正常解析
v-model:title.lazy.number="'value'"
export default function registerFocus(app) {
app.directive('focus', {
// 参数一: 指令所在的dom元素
// 参数二: 一个对象,包含了指令其它相关的所有信息,如修饰符,参数等
mounted(el, bindings) {
console.log(bindings.value) // 指令的值
console.log(bindings.arg) // 指令的参数
console.log(bindings.modifiers) // 指令的修饰符
// 如果指令修饰符为.lazy.number
// 对应的modifiers为
/*
{
lazy: boolean,
number: boolean
}
*/
el.focus()
}
})
}
示例 - 添加价格单位
<template>
<div>
<div v-unit>123.22</div>
<!--
参数是字符串的时候 必须加单引号
因为参数值会被作为js表达式进行解析
-->
<div v-unit="'$'">123.22</div>
</div>
</template>
app.directive('unit', {
mounted(el, bindings) {
el.textContent = (bindings.value ?? '¥') + el.textContent
}
})
Teleport
在组件化开发中,我们封装一个组件A,在另外一个组件B中使用
那么组件A中template的元素,会被挂载到组件B中template的某个位置, 最终我们的应用程序会形成一颗DOM树结构
但是某些情况下,我们希望组件不是挂载在这个组件树上的,可能是移动到Vue app之外的其他位置
比如移动到body元素上,或者我们有其他的div#app之外的元素上, 这个时候我们就可以通过teleport来完成
Teleport是一个Vue提供的内置组件,类似于react的Portals,teleport翻译过来是心灵传输、远距离运输的意思
如果我们将多个teleport应用到同一个目标上(to的值相同),那么这些目标会进行合并
| 属性 | 说明 |
|---|---|
| to | 指定将其中的内容移动到的目标元素,可以使用选择器 |
| disabled | 是否禁用 teleport 的功能 |
<Teleport to="body">
<div class="modal">
this is a modal
</div>
</Teleport>
异步组件和Suspense
Suspense是一个内置的全局组件,该组件有两个插槽
| 插槽 | 说明 |
|---|---|
| default | 如果default可以显示,那么显示default的内容 |
| fallback | 如果default无法显示,那么会显示fallback插槽的内容 |
suspense一般和异步组件一起结合使用,当浏览器还在下载和解析异步组件的时候
界面就会像是fallback插槽中的内容,如果对应组件已经被解析完毕,那么此时就会显示default插槽中的内容
<Suspense>
<AsyncComponent />
<template #fallback>
loading...
</template>
</Suspense>
插件
通常我们向Vue全局添加一些功能时,会采用插件的模式,它有两种编写方式
- 对象类型:一个对象,但是必须包含一个 install 的函数,该函数会在安装插件时执行
- 函数类型:一个function,这个函数会在安装插件时自动执行
插件可以完成的功能没有限制,插件的主要功能是对vue的功能进行扩展
- 添加全局方法或者 property,通过把它们添加到 config.globalProperties 上实现
- 添加全局资源:指令/过滤器/过渡等
- 通过全局 mixin 来添加一些组件选项
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能
示例 — 将之前指令注册函数修改为插件
import registerFocus from './focus'
// 当插件导出的是一个函数的时候,在执行use方法的时候 会自动执行对应的函数
// 当插件导出的是一个含有install方法的对象的时候,在执行use方法的时候,会自动调用对象的install方法
// 在调用的时候,会传入两个参数
// 参数一 - 对应的App实例对象
// 参数二 - 通过use的第二个参数传入的一个原生对象 - 可以用来对插件进行配置信息的传入
export default function(app, options) {
console.log(options)
// 我们可以使用插件中对vue进行任何全局性的功能扩展
// 例如 本例中 就是在vue上额外挂载了一些自定义的全局指令
registerFocus(app)
}
import { createApp } from 'vue'
import App from './App.vue'
import driectives from './directives/index'
// 我们可以使用App实例的use方法注册插件
// 参数一 - 插件
// 参数二 - 额外的配置选项
// use方法会返回App实例对象,方便我们进行链式调用
createApp(App).use(driectives, {name: 'Klaus'}).mount('#app')
globalProperties
我们可以通过app.config获取当前App实例对象对应的所有配置信息
其中有一个属性名为globalProperties,这个属性可以用于定义一些全局的状态
该属性的默认值是一个空对象,所有在该对象中定义的属性可以通过组件实例获取
// 在globalProperties中定义的属性可以在组件实例中被获取
// 为了区分全局属性和组件内部状态,推荐在globalProperties中定义的属性都以$开头
app.config.globalProperties.$customRouter = 'my custom router object'
export default {
created() {
console.log(this.$customRouter)
}
}