Vue进阶
Vue进阶
JinVUE2.X自定义指令
私有自定义指令
在每个 vue 组件中,可以在 directives 节点下声明私有自定义指令
directives: {
color:{
// 为绑定的元素设置红色字体
bind(el){
// el 是绑定此指令的 dom 对象
el.style.color='red'
}
}
}
使用:需要加上 v- 前缀
<!-- 上面声明的自定义指令名为 color -->
<!-- 使用自定义指令名 color,需要加上 v- 前缀 -->
<h1 v-color>app组件</h1>
动态绑值
data:{
return{
color:'red'
}
}
<h1 v-color="color">app组件</h1>
通过 binding 获取指令的参数值
directives: {
color:{
bind(el,binding){
// el 是绑定此指令的 dom 对象
// 通过 binding 对象.value 属性获取动态的参数值
el.style.color=bing.value
}
}
}
update 函数
bind 函数只调用 1 次:当指令第一次绑定到元素时调用,当 DOM 更新时 bind 函数不会被触发。 update 函数会在每次 DOM 更新时被调用
directives: {
color:{
// 指令第一次绑定元素时调用
bind(el,binding){
// el 是绑定此指令的 dom 对象
// 通过 binding 对象.value 属性获取动态的参数值
el.style.color=bing.value
}
// 每次 dom 更新都调用
update(el,binding){
el.style.color=bing.value
}
}
}
简写
insert 和update 函数中的逻辑完全相同,则对象格式的自定义指令可以简写成函数格式
directives: {
color(el,binding){
el.style.color=bing.value
}
}
全局自定义指令
全局共享的自定义指令需要通过 Vue.directive()
进行声明
// 全局写法
Vue.directive('color', (el, binding) => {
el.style.color = binding.value
}))
自定义指令钩子函数
- bind:只会调用一次,指令 第一次 绑定到元素时会调用
- inserted:被绑定元素插入父节点时调用 。
- update:元素第一次绑定不会触发,绑定的值发生更新触发,可能发生在其子节点更新之前。
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- u0nbind:只调用一次,指令与元素解绑时调用。
注意事项
- 自定义指令使用时需要添加
v-
前缀 - 指令名如果是多个单词,要使用
camel-case
短横线命名方式,不要用camelCase
驼峰命名 - 自定义指令三个函数里的
this
指向window
<span v-big-number="n"></span>
data() {
return {
n: 1
}
},
directives: {
// 添加引号才是对象键名完整写法
// 平时不加引号都是简写形式
// 遇到短横线的键名就必须添加引号
'big-number': {
bind(el, binding) {
console.log(this) // Window
el.innerText = binding.value * 10
}
}
}
VUE3.X自定义指令
钩子函数的变化
- created:在绑定元素的 attribute 或事件监听器被应用之前调用;
- beforeMount:当指令第一次绑定到元素并且在挂载父组件之前调用;
- mounted:在绑定元素的父组件被挂载后调用;
- beforeUpdate:在更新包含组件的 VNode 之前调用;
- updated:在包含组件的 VNode 及其子组件的 VNode 更新后调用;
- beforeUnmount:在卸载绑定元素的父组件之前调用;
- unmounted:当指令与元素解除绑定且父组件已卸载时,只调用一次;
在setup语法糖使用
<input v-focus ></input>
<script setup>
// 局部指令, 变量名为驼峰命名(vFocus = v-focus)
const vFocus = {
mounted: (el) => {
el.focus()
console.log(el, '已经自动获得焦点')
}
}
</script>
指令的参数和修饰符
<!--
info是参数的名称
aaa是修饰符的名称
后面是传入的具体的值
-->
<span v-msg:info.aaa="{name:'zs',age:'18'}">11</span>
const vMsg = {
mounted: (el, bindings) => {
// 把标签内容改成传入的值
el.textContent = bindings.value.age
console.log(bindings)
}
}
Teleport
一般情况下组件是挂载在 Vue app template的某个位置,形成一颗DOM树结构
如果要组件不是挂载在这个组件树上的,可能是移动到Vue app之外的其他位置
比如:移动到body元素上,或者我们有其他的div#app之外的元素上
Teleport 就可以实现
Teleport 两个属性:
- to:指定将其中的内容移动到的目标元素,可以使用选择器;
- disabled:是否禁用 teleport 的功能;
<template>
<div class="myapp">
<teleport to="body">
<h2>哈哈哈</h2>
</teleport>
</div>
</template>
同理也可以把组件放进去
多个teleport应用到同一个目标上(to的值相同),那么这些目标会进行合并
异步组件和Suspense
Suspense显示的是一个实验性的特性,API随时可能会修改
Suspense是一个内置的全局组件,该组件有两个插槽:
- default:如果 default 可以显示,那么显示default的内容;
- fallback:如果 default 无法显示,那么会显示fallback插槽的内容;
<suspense>
<template #default>
<async-home/>
</template>
<template #fallback>
<h2>Loading</h2>
</template>
</suspense>
Vue插件
如果要在Vue全局添加一些功能,可以采用插件模式
两种方式:
对象:一个对象,但是必须包含一个 install 的函数,该函数会在安装插件时执行
app.use({ install: function(app,options) { console.log("传入对象的install被执行:", app) } })
函数:这个函数会在安装插件时自动执行
app.use(function(app,options) { console.log("传入函数被执行:", app) })
插件可以完成的功能没有限制,比如下面的几种都是可以的:
- 添加全局方法或者 property,通过把它们添加到 config.globalProperties 上实现;
- 添加全局资源:指令/过滤器/过渡等;
- 通过全局 mixin 来添加一些组件选项;
- 一个库,提供自己的 API,同时提供上面提到的一个或多个功能;
h 函数
在 <template>
内容 Vue在生成真实的DOM之前,会将我们的节点转换成VNode,而VNode组合在一起形成一颗树结构,就是虚拟DOM
编写 createVNode / h 函数,生成对应的VNode
// 完整参数签名
function h(
type: string | Component,
props?: object | null,
children?: Children | Slot | Slots
): VNode
参数:HTML标签名/组件,标签的属性 比如class,内容
import {h} from 'vue'
export default {
render() {
return h('div',{class:"app"},'hello h')
}
}
setup中使用
import {h} from 'vue'
export default {
setup() {
return () => h('div',{class:"app"},'hello h')
}
}
注意事项:
- 如果没有props,那么通常可以将children作为第二个参数传入;
- 如果会产生歧义,可以将null作为第二个参数传入,将children作为第三个参数传入;
动画
vue 中为提供了内置组件和对于的api来完成动画
vue 提供 transition 封装组件,可以给元素和组件添加进入离开动画
例如:
- 条件渲染(v-if / v-show)
- 动态组件
- 组件根节点
代码示例:
<transition name="fade">
<h2 v-if="show">hello world</h2>
</transition>
<style scoped>
.fade-leave-to,
.fade-leave-from{
opacity: 0;
}
.fade-enter-to,
.fade-leave-form{
opacity: 1;
}
.fade-enter-active,
.fade-leave-active{
transition: opacity 1s ease;
}
</style>
上面代码类名说明
v-enter-from
:进入的开始阶段,元素被插入之前的生效,在元素被插入之后的下一帧移除v-enter-active
: 进入的过渡阶段,定义过度时间,延迟和曲线函数v-enter-to
:进入的结束阶段,在过渡动画完成后移除v-enter-from
v-leave-from
:离开的开始阶段v-leave-active
:离开的生效过度阶段v-leave-to
: 离开的结束阶段,同样也移除v-leave-from
class的name命名规则如下:
- 如果我们使用的是一个没有name的transition,那么所有的class是以
v-
作为默认前缀 - 如果我们添加了一个name属性,比如
<transtion name="fade">
,那么所有的class会以fade-
开头
原理:
- 自动嗅探目标元素是否应用了CSS过渡或者动画,如果有,那么在恰当的时机添加/删除 CSS类名
- 如果 transition 组件提供了JavaScript钩子函数,这些钩子函数将在恰当的时机被调用
- 如果没有找到JavaScript钩子并且也没有检测到CSS过渡/动画,DOM插入、删除操作将会立即执行
同理也可以通过animation
实现
.fade-enter-active{
animation: fade-in 0.5s;
}
.fade-leave-active{
animation:fade-in 0.5s reverse;
}
@keyframs: fade-in {
0% { transform: scale(0) }
50% {transform: scale(1.25)}
100% {transform: scale(1)}
}
transtion组件属性:
mode
:解决进入和离开的元素都是在同时开始动画- in-out: 新元素先进行过渡,完成之后当前元素过渡离开
- out-in: 当前元素先进行过渡,完成之后新元素过渡进入
- appear:解决首次渲染没有动画
<transition-group>
官方文档链接
如果我们希望渲染的是一个列表可以使用<transtion-group>
特点:
- 默认情况下,它不会渲染一个元素的包裹器,但是你可以指定一个元素并以 tag attribute 进行渲染
- 过渡模式不可用,因为我们不再相互切换特有的元素
- 内部元素总是需要提供唯一的 key attribute 值
- CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身