Vue框架 ---- 组件高级:ref、动态组件,插槽slot和自定义命令

Vue框架

内容管理

    • ref引用
      • 使用ref引用DOM元素
      • 使用ref引用组件
      • 实例----控制文本框和按钮的按需转换
      • this.$nextTick(cb)将cb延迟到DOM更新完
    • 动态组件
      • keep-alive保持组件状态
    • 插槽slot
      • slot的基本使用
        • 没有预留插槽,自定义内容丢弃
        • 后备内容【默认内容】--- slot标签域内容
      • 具名插槽
        • 具名插槽插入内容 v-slot:名称 【slot已经弃用】
        • 具名插槽简写 : #插槽名
      • 作用域插槽
        • 解构作用域插槽
        • 作用域插槽应用
    • 自定义指令
      • 私有自定义指令 directives结点下声明
      • 全局自定义指令
      • updated函数保持v-focus持续触发
      • 指令的参数值
    • Table案例


Vue3组件基础:ref引用、动态组件、插槽、自定义指令


前面简单介绍了组件的相关,比如监听,组件关系和组件的数据共享,还有自定义事件,这样一个界面就可以为不同的组件相互作用结合形成,数据传递则依靠指令和之间【后代可以使用provide和inject便捷传输】 复杂的数据传递一般使用vuex

其实很多得概念之前就提到了,比如这里得ref引用;【这个在Spring的IOC的属性注入的位置,对象属性就是ref,普通的属性就是value】,之前的响应式数据传递,就配置全局数据项,就可以不写.value

ref引用

ref引用就是对象的引用,ref是帮助programmer在不依赖JQuery的情况下,获取DOM元素或者组件的引用【之前Jq最方便的就是简化了对象的操作,特别是$(选择器:过滤器)】

Vue里面是不建议使用Jq的 ,因为容易造成引用的混乱,所以就要使用ref来操作DOM或者组件。每一个vue的组件实例上,都包含一个$refs对象,里面存储着对应的DOM元素或者对于组件的引用,默认情况下,其默认指向一个空对象

之前分享组件的基础的时候,就提过this代表的就是当前组件的实例,这里可以看一下其内容

[[Target]]: Object
num: (...)
username: (...)
$: (...)
$attrs: (...)
$data: (...)
$el: (...)
$emit: (...)
$forceUpdate: (...)
$nextTick: (...)
$options: (...)
$parent: (...)
$props: (...)
$refs: Object
$root: (...)
$slots: (...)
$watch: (...)
_: Object

可以看到有很多的内置的对象,这里就有一个$refs

使用ref引用DOM元素

如果想要对页面上的某个DOM元素进行操作,就给需要操作的DOM结点添加ref属性

  • 为对应的DOM添加ref属性,指明引用的名称
<h1 ref="myh1">LifeCircle子组件h1>
  • 通过this.$refs.引用名称,获取到dom元素的引用

  • 通过该引用操作DOM元素



methods:{
			getDom() {
				console.log(this.$refs.myh1) //因为会将带有ref属性的结点注册到$refs中,所以这里想要使用myh1,就直接.引用即可
			}
		}

这里打印的结果就是: < h1>LifeCircle子组件< /h1>;成功获取到了该组件对应页面上的DOM结点,比如这里想要修改文本的颜色: this.$refs.myh1.style.color = 'red’就可以设置红色

使用ref引用组件

如果想要引用页面上的组件【组件对于父组件来说也是一个标签,和上面的DOM一样是一个结点】

  • 为要引用的组件加上ref引用名称
  • 使用this.$refs.引用名称就可以获取到组件的实例对象,通过.methods就可以直接使用组件的methods结点中定义的方法

这里比如在consum-kid组件中定义了方法setColor,可以获取组件页面中的dom元素修改其背景颜色,在App中点击按钮达到效果

//consum-kid.vue
X * 2 + 1的结果为 :{{count * 2 + 1}}

methods:{
			setColor() { //在子组件中定义了一个set方法,在另一个组件中点击按钮就可以执行该方法,那么就要使用组件的引用
				this.$refs.myspan.style.backgroundColor = 'pink'
			}
		}

//父组件App.vue




methods:{
	  changeColor(){
		  this.$refs.conKid.setColor()        //获取子组件调用其set方法修改颜色
	  }
  }

这样通过ref引用就可以方便操作组件及上面的DOM结点

实例----控制文本框和按钮的按需转换

通过布尔值inputVisible来控制组件中的文本框于按钮的按需切换

当为true的时候就可以展示文本输入框,要想展示的一瞬间获得焦点,就使用元素js的方法.focus()即可;所以通过ref引用获取文本框即可



methods:{ showInput() { this.inputVisible = true this.$refs.ipt.focus() } }

这样点击按钮就可显示文本框,但是控制台报错:Uncaught TypeError: Cannot read properties of undefined (reading ‘focus’) — 也就是浏览器并没有检测到文本框undefined

DOM元素的更新是异步的,也就是异步的任务【因为要重新渲染结点,属于宏任务】,在EventLoop中,这是在一个消息队列中,主任务就是优先执行,所以会立即去执行this.$refs,这个时候并没有获取到结点,报错

this.$nextTick(cb)将cb延迟到DOM更新完

上面就发现因为组件的更新是异步的,而js是将栈中的所有的主任务执行完毕才会执行异步任务,所以调用不应该在主任务,vue提供了内部函数$nextTick(cb),会将回调函数推迟到下一个DOM更新周期之后执行,也就是登组件重新渲染更新完毕之后,才会执行cb函数,保证cb操作的最新的回调函数

methods:{
	showInput() {
		this.inputVisible = true
		//把对dom元素的操作推迟到页面的DOM更新完毕之后执行
		this.$nextTick(() => {
			this.$refs.ipt.focus()
		})
	}
}

这里将执行语句放到匿名回调函数中,等待更新完毕之后执行,相当于也是异步的任务

动态组件

动态组件指的是动态切换组件的显示和隐藏,vue提供了一个内置的< compnent>组件,用来实现组件的动态渲染 【之前的普通的DOM结点的显示可以使用v-if或者v-show】

  • component是组件的占位符
  • 通过js 属性动态指定要渲染的组件的名称
  • < component is=“要渲染的组件的名称”>< /component>

也就是使用component标签占据位置,通过这个标签的is属性就可以动态绑定组件




 data() {
	  return {
		  conName:'',

showComp() {
		  this.conName = 'LifeCircle'
	  }

这样只要点击按钮,就可以显示LifeCircle组件

keep-alive保持组件状态

这里的component占位的效果,其实也是动态创建或者销毁组件实例,当切换其他的组件的时候,之前的组件实例就被销毁,第二次切换回来的时候就是重新创建组件实例,所以之前的状态就会恢复到最初的状态, 如果想要不销毁,那么就要使用keep-alive

默认情况下,切换动态组件时 无法保持组件的状态,这个时候可以使用vue内置的< keep- alive>组件包裹component标签保持动态组件的状态


	

这样组件实例切换的时候就不会被销毁,组件实例就会被缓存到浏览器的内存中,一般都会使用keep-alive来保持之前页面的状态

插槽slot

插槽是vue为组件的封装er提供的能力,允许programmer在封装组件的时候,把不确定的、希望由用户指定的部分定义为插槽

Vue框架 ---- 组件高级:ref、动态组件,插槽slot和自定义命令_第1张图片

这样就可以通过插槽将html小的页面插入到封装好的组件中,插槽就是在组件的封装期间,为用户预留的占位符;上面的动态组件的component也是占位符,但是占的是整个组件的位,这里占位站的就只是DOM的位置

slot的基本使用

在封装组件期间,可以通过< slot>元素定义插槽,从而为用户预留的内容进行占位


使用者,也就是父组件调用的时候,使用双目的子组件的标签,标签之间内容都会被填充到插槽

这里可以演示一下









-------------------父组件App.vue进行调用--------------
  
  
	  
我是App根组件自定义的内容

没有预留插槽,自定义内容丢弃

就是如果在组件中没有定义slot插槽,那么就算在父组件使用时,在标签间定义了内容,也不会填充,因为没有插槽,那么效果就是会被自动舍弃

后备内容【默认内容】— slot标签域内容

封装组件时,可以为预留的slot 插槽提供后备内容【默认内容】,如果使用者没有为插槽提供内容,那么就会使用后备内容,提供了就会覆盖


具名插槽

上面的只是定义了一个插槽,插槽标签slot中的内容就是后备内容;如果需要在封装组件的时候预留多个插槽结点,则需要为每一个插槽指定具体的name名称,这种含有名称的插槽为具名插槽






如果没有指定name名称的插槽,那么这个插槽的名称为default — 所以只能由一个default

具名插槽插入内容 v-slot:名称 【slot已经弃用】

比如这里定义了3个插槽


在App根组件中要使用这个组件,传入了需要插入的自定义的内容


	  

春晓

孟浩然

春眠不觉晓,处处闻啼鸟

夜来风雨声,花落知多少

最终渲染的结果就是: 所有的内容都进入了默认插槽;也就是说,在不特殊说明的情况下,插入的内容都会进入默认插槽

要让内容进入具名插槽,需要使用template标签包裹内容,并且使用v-slot指定剧名插槽的名称

 
  
		  
		  
		  
  

对要插入的内容使用template标签包裹,并且使用v-slot指明具名插槽的名称,使用: ,不是=号,并且直接写名称,不用加引号,不然会报错

Error: Codegen node is missing for element/if/for node. Apply appropriate transforms first.

这里的问题就是因为:template标签放的位置有问题,template必须是最外层的标签,外面不能包裹div了,报错就是因为组件标签里的内容包裹在div中:happy:,template应该是最外面 ----- div也是属于DOM结点,template才能将其渲染到界面上 ---- 所以template必须包裹所有的DOM

  
	  
	  作者:孟浩然
	  
  

默认插槽可以省略template,具名插槽不能省略

具名插槽简写 : #插槽名

和v-on和v-bind一样,v-slot也可以简写,把参数之前的内容v-slot:替换为字符#, 比如