Vue进阶之旅:核心技术与实战
文章目录
在Vue开发中,自定义指令、插槽与路由是非常重要的技术点,它们能够极大地提升我们开发的效率和灵活性。今天我们就一起来深入学习这些内容。
一、自定义指令
自定义指令允许我们封装一些DOM操作,扩展额外功能。
(一)注册方式
自己调用自己:
代码解释:
- 模板部分(
<template>
):
<template>
<div>
<h1>自定义指令</h1>
<input ref="inp" type="text">
</div>
</template>
此部分定义了一个 Vue 组件的 HTML 结构,包含一个标题 <h1>
和一个文本输入框 <input>
,ref="inp"
为输入框添加了一个引用名称。
- 脚本部分(
<script>
):
export default {
mounted () {
this.$refs.inp.focus();
}
}
此部分定义了一个 Vue 组件,在组件挂载完成(mounted
生命周期)时,通过 this.$refs.inp
找到 ref
为 inp
的输入框元素,并调用 focus()
方法让其获得焦点。
功能总结:
该 Vue 组件的主要功能是在页面上展示一个包含标题和文本输入框的区域,并在组件挂载完成时使输入框自动获得焦点,方便用户直接输入,提升用户体验。
接下来就是使用指令来获得焦点:
- 全局注册
全局注册使用Vue.directive
方法,语法如下:
Vue.directive('指令名', {
inserted (el) {
// 可以对el标签扩展额外功能,例如让元素获取焦点
el.focus()
}
})
在这里,inserted
钩子函数在被绑定元素插入父节点时调用 ,el
表示被绑定的DOM元素。举个例子:
全局注册解释:
在 Vue 中,全局注册是将组件、指令或过滤器等添加到 Vue 实例的全局范围,以便在整个应用的任何组件中使用,无需在每个组件中单独导入或声明。
下述代码中的全局注册:
Vue.directive('focus', {
inserted (el) {
el.focus();
}
})
Vue.directive('focus', {...})
用于全局注册自定义指令focus
。inserted(el) { el.focus() }
是focus
指令的一个钩子函数,当元素被插入页面时触发,使元素获得焦点。
使用场景:
<input v-focus ref="inp" type="text">
- 这里使用
v-focus
指令,因为它是全局注册的,无需额外导入或声明,元素插入页面时,会自动执行inserted
函数使输入框聚焦。
总结:
全局注册可在多个组件复用,避免重复代码,提高开发效率和可维护性,上述代码的 focus
指令就体现了这点。
- 局部注册
在组件内部通过directives
选项进行局部注册,示例代码如下:
export default {
directives: {
'指令名': {
inserted (el) {
el.focus()
}
}
}
}
在标签中使用时,写法为 <input v-指令名 type="text">
。通过自定义指令,我们可以避免在 mounted
钩子函数中使用 this.$refs.inp.focus()
这种较为繁琐的获取焦点方式,代码更加简洁。
局部注册解释:
在 Vue 中,局部注册是指将自定义指令、组件或过滤器等仅在某个组件内部进行注册的操作,使其仅在该组件内可用,而不会影响到其他组件。
代码中的局部注册:
export default {
directives : {
focus : {
inserted (el) {
el.focus();
}
}
}
}
- 在这个 Vue 组件的
script
部分,通过directives
属性进行了自定义指令的注册。 focus : {...}
定义了一个名为focus
的自定义指令。inserted (el) {...}
是该指令的一个钩子函数,当使用v-focus
指令的元素被插入页面时,会执行el.focus()
操作,使元素获得焦点。
使用场景:
<input v-focus ref="inp" type="text">
- 这里使用
v-focus
指令,在上述代码中,focus 指令只在当前组件的输入框中使用,确保了其仅在该组件内发挥作用,不会影响其他组件,这就是典型的局部注册使用场景。
总结:
上述代码中的 directives
属性的使用就是局部注册的体现,仅在当前组件内注册了 focus
指令,使其仅能在本组件内使用,与全局注册相比,其作用范围受限,但对于只在特定组件中使用的功能,局部注册能保持代码的简洁和独立性。
(二)指令的值
指令可以通过 “等号” 的形式绑定参数值,如 <div v-color="color">我是内容</div>
。在指令的定义中,通过 binding.value
可以拿到指令值,指令值修改会触发 update
函数。示例代码如下:
directives: {
color: {
inserted (el, binding) {
// 设置元素文字颜色为指令值
el.style.color = binding.value
},
update (el, binding) {
// 指令值更新时,更新元素文字颜色
el.style.color = binding.value
}
}
}
这种方式可以应对更复杂的指令封装场景。
代码示例:
<template>
<div>
<!-- 使用 v-color 指令,并将其绑定到 color1 数据 -->
<h1 v-color="color1">自定义指令1</h1>
<!-- 使用 v-color 指令,并将其绑定到 color2 数据 -->
<h1 v-color="color2">自定义指令2</h1>
</div>
</template>
<script>
export default {
data () {
return {
// 定义 color1 的数据,初始值为红色
color1 : 'red',
// 定义 color2 的数据,初始值为绿色
color2 : 'green'
}
},
directives : {
color : {
inserted (el, binding) {
// inserted 钩子函数:当指令所在元素插入页面时,将元素的颜色设置为指令绑定的值
el.style.color = binding.value;
},
update (el, binding) {
// update 钩子函数:当指令绑定的值更新时,更新元素的颜色
el.style.color = binding.value;
}
}
}
}
</script>
解释:
- 在
template
部分,我们使用了v-color
自定义指令,并将其绑定到组件中的color1
和color2
数据。 - 在
script
部分:- 首先,通过
data
函数定义了color1
和color2
的数据,分别初始化为red
和green
。 - 然后,在
directives
对象中定义了color
自定义指令。 - 对于
color
自定义指令:inserted
钩子函数:当使用v-color
指令的元素插入到页面时,会调用该函数。通过binding.value
可以获取到指令绑定的值,将元素的颜色样式设置为该值。例如,对于<h1 v-color="color1">自定义指令1</h1>
,会将该h1
元素的颜色设置为red
。update
钩子函数:当指令绑定的值发生更新时,会调用该函数。同样通过binding.value
获取更新后的值,并更新元素的颜色。例如,如果color1
的值从red
变为blue
,元素的颜色会更新为blue
。
- 首先,通过
使用场景和优势:
- 这种自定义指令的方式适用于对元素样式或其他 DOM 属性进行动态控制的场景。
- 通过
binding.value
可以灵活地将不同的数据值绑定到指令上,实现元素样式或行为的动态调整。 - 当数据发生变化时,
update
钩子函数会自动更新元素的样式,无需手动操作 DOM,符合 Vue 的数据驱动视图的理念。
注意事项:
- 确保
binding.value
提供的值符合元素属性的要求,例如,在设置颜色时,需要提供有效的颜色值,如颜色名称、十六进制代码或 RGB 值等。 - 合理使用
inserted
和update
钩子函数,根据不同的生命周期阶段实现相应的逻辑,避免冗余代码和性能问题。
通过这种方式,可以实现对元素样式的动态管理,在开发中可以根据不同的业务需求,扩展更多功能,如动态改变元素的大小、位置等,为开发更具交互性和动态性的 Vue 组件提供了很大的便利。
(三)v-loading指令封装
在实际开发中,数据请求时页面可能会空白,影响用户体验。我们可以封装 v-loading
指令来实现加载中的效果。
- 核心思路
- 准备一个
loading
类,通过伪元素定位,设置宽高实现蒙层效果,让用户知晓数据正在加载。。 - 利用
v-loading
指令,将其绑定到需要显示加载状态的元素上,并关联一个布尔变量。在数据请求开始时,将该变量置为true
,添加loading
类,显示蒙层;数据请求结束后,将该变量置为false
,移除loading
类,隐藏蒙层。
- 准备一个
- 代码实现
CSS 部分
.loading:before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff url('./loading.gif') no-repeat center;
}
.box {
width: 800px;
min-height: 500px;
border: 3px solid orange;
border-radius: 5px;
position: relative;
}
.news {
display: flex;
height: 120px;
width: 600px;
margin: 0 auto;
padding: 20px 0;
cursor: pointer;
}
.news.left {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding-right: 10px;
}
.news.left.title {
font-size: 20px;
}
.news.left.info {
color: #999999;
}
.news.left.info span {
margin-right: 20px;
}
.news.right {
width: 160px;
height: 120px;
}
.news.right img {
width: 100%;
height: 100%;
object-fit: cover;
}
.loading:before
样式:content: '';
:使用伪元素添加内容。position: absolute;
:设置绝对定位,使其覆盖父元素。left: 0; top: 0; width: 100%; height: 100%;
:确保覆盖整个父元素。background: #fff url('./loading.gif') no-repeat center;
:使用白色背景并在中心位置显示loading.gif
图片,实现加载效果。
JavaScript 部分
<template>
<div class="main">
<div class="box" v-loading="isLoading">
<ul>
<li v-for="item in list" :key="item.id" class="news">
<div class="left">
<div class="title">{
{ item.title }}</div>
<div class="info">
<span>{
{ item.source }}</span>
<span>{
{ item.time }}