摘要:在本文中我们会谈及ref引用DOM和组件实例、并且了解$nextTick的调用时机,进而谈论keep-alive元素的作用、插槽的基本用法和自定义指令。
声明:为了文章的清爽性,在文章内部的代码演示中只会附上部分演示代码,main.js文件的代码通常不贴出,如果感兴趣可以前往代码仓库获取
作者:来自ArimaMisaki创作
说明:ref用来辅助开发者在不依赖原生的js和JQuery的情况下,获取DOM元素或组件的引用。
提示:在每个vue的组件实例中,都包含一个$refs对象,里面存储这对应的DOM元素或组件的引用。默认情况下,组件的$refs指向一个空对象。
检验:我们可以通过组件的this来调用当前组件的实例对象,从而查看里面是否存在$refs。
App根组件

说明:在第二章我们曾经介绍过,使用this.$refs.ref属性以及在DOM中定义ref属性即可获取DOM元素。
App根组件
说明:通过在组件标签上设置ref属性可以让我们获得操纵组件的能力。
{{ count }}
说明:ref常用语文本框获取焦点。具体实现步骤是通过ref获取DOM元素,并调用DOM元素原生的focus方法。
提示:在下面给出的代码中,你会发现实际上做不到想要的效果。打印iptRef居然无法获得DOM,这是因为对于组件来说DOM的更新是异步执行的。
App根组件
响应式状态:通过data改变DOM中数据,或者是DOM更新。
tick更新:我们前面谈论过组件更新,当时说DOM的更新是异步执行的,这是因为Vue将更新的DOM元素缓存在一个队列中,直到下一个tick才一起执行,这样可以防止修改一个DOM元素立马响应,从而提高更新速度。
说明:如果我们想要在状态改变后使用更新后的DOM,则使用nextTick()方法可以让我们执行的动作延迟到DOM更新结束,即下一个tick。nextTick()接收一个回调函数作为参数,或者await返回的Promise。
App根组件
说明:动态组件指的是动态切换组件的显示和隐藏。Vue提供了一个内置的
元素:在下面的诸多术语中,我们会把标签说成元素。
实现步骤:
元素对原来的动态组件标签进行替换,相当于一个占位标签:is的方式绑定data中的组件名
Home组件
Movie组件
App根组件
引入:如果组件中包含有数据的变动,那么在切换组件时,被切换的组件中数据会被恢复为最原始的部分。我们来描述这么一个场景,A1组件有count值,默认值为1,但被我通过方法增加到了5,如果此时切换为A2组件,切回A1时我们会发现count又变回1了。
说明:组件状态不能在组件切换中保持,本质原因是因为组件在切换的时候从内存中卸载,这个事实我们可以使用生命周期钩子验证。如果想要保持其存活性,我们可以在component标签的外围用keep-alive标签包裹,使得被卸载的组件仍然存活。
App根组件
Home组件---{{count}}
说明:插槽(Slot)是vue为组件的封装者提供的能力,它允许组件的开发人员预留一片空白的区域,这片区域啥也没有,里面可以有什么样的东西由组件的使用者自行决定。
说明:在封装组件时,通过slot元素可以定义插槽,从而为组件的使用者预留占位。
提示:如果在没有使用slot元素定义插槽的情况下直接在组件标签中添加自定义内容,那添加的内容会全部被丢弃。
后备内容:在封装组件时,可以为预留的slot插槽提供默认内容,一旦组件使用者没有为插槽插入内容,则启用默认的内容。具体做法是在slot元素中写入默认的内容。
插槽在下面
默认内容
插槽在上面
App根组件
插槽在这里!
说明:如果在封装组件时需要拥有多个插槽,则需要为每个slot指定具体的name名称。我们把带有具体名称的插槽叫做具名插槽。
提示:如果一个slot没有指定name名称,则其默认名称为default。
使用步骤:
v-slot:插槽名v-slot:插槽名可以简写为#插槽名
App根组件
滕王阁序
豫章故郡,洪都新府
星分翼轸,地接衡庐
落款:王勃
说明:插槽内容可以访问到组件使用者的数据作用域,因为插槽内容本身是在组件使用者的模板中定义的。换而言之,假设有一个组件使用者的data选项中有一个数据count,那么插槽可以获取到这个数据。
提示:插槽内容虽然可以访问组件使用者的数据,却无法访问拥有插槽的组件数据。
App根组件
{{title}}
豫章故郡,洪都新府
星分翼轸,地接衡庐
落款:王勃
说明:在上一小节提到过插槽内容只可以访问组件使用者的数据,却无法访问拥有插槽的组件数据。为此,我们提出了作用域插槽。作用域插槽能够同时访问到组件使用者以及拥有插槽的组件两者的数据。
具体的做法如同对组件传递props一般,我们可以向一个插槽的出口上传递属性。
使用步骤:
v-slot:插槽名指定接收值,接收值为任意的名字。提示:虽然和props很像,但我们并不用声明props选项,如下图所示。

TEST组件
App根组件
{{scope.info.address}}
说明:在ES6中我们曾经提到编程风范。如果想要提取对象中的某个属性,最好的方式是解构赋值。
{{info.address}}
说明:vue官方提供了v-for、v-model等诸多内置指令,除此之外,Vue还允许开发者自定义指令。vue的自定义指令分为私有自定义指令和全局自定义指令。私有自定义指令仅在定义指令的组件内可用;而全局自定义指令可在项目中任意位置使用。
作用:自定义指令主要是为了重用涉及普通元素的底层DOM访问的逻辑。
提示:自定义指令在使用时必须以v-开头,但在声明时却无需加上v-。还有,只有当所需功能只能通过直接的 DOM 操作来实现时,才应该使用自定义指令。其他情况下应该尽可能地使用 v-bind 这样的内置指令来声明式地使用模板,这样更高效,也对服务端渲染更友好。
说明:在directives选项中可以自定义指令。下面以自定义指令v-focus来实现文本框自动聚焦为例。
使用步骤:
MyHome 组件
说明:与注册组件类似,在directives选项中创建的指令仅仅是局部的。如果想要声明一个全局的自定义指令,我们需要在main.js中使用app.directive()来声明。
const app = createApp(App);
app.directive('focus',{
mounted(el){
el.focus()
}
})
说明:一个指令对象可以提供多种可选的生命周期钩子。
钩子参数:
el:指令绑定到的元素。这可以用于直接操作 DOM。binding:一个对象,包含以下属性。
value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2。oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用。arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"。modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }。instance:使用该指令的组件实例。dir:指令的定义对象。vnode:代表绑定元素的底层 VNode。prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}
说明:对于自定义指令来说,很多钩子是不经常使用的。经常使用的钩子使用mounted和updated。为此,我们可以直接定义一个函数来定义指令,而无需再传对象。
app.directive('color', (el, binding) => {
// 这会在 `mounted` 和 `updated` 时都调用
el.style.color = binding.value
})