vue3 知识点补充

159 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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全局添加一些功能时,会采用插件的模式,它有两种编写方式

  1. 对象类型:一个对象,但是必须包含一个 install 的函数,该函数会在安装插件时执行
  2. 函数类型:一个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)
  }
}