1 前言
关于vue的知识通过前面的章节我们已经学的差不多了,所以本章将带大家实践一下、深刻体会vue提供的便利所在。
注意,由于我个人喜欢使用TypeScript
与scss
语法,所以我在vue项目中会直接使用这两者,不会安装、使用这两者的可以参考前面的文章。
包括项目建立等基础性内容,这里也不再重复赘述了。
1.1 按钮组件
对于“按钮”这种非常通用的组件来说,一般我们都会将其放在components
文件夹下:
由于实现一个基本的按钮过于简单,所以我这里直接将代码完成如上图。
注意2号位置,使用的是vue的插槽知识,忘了的可以到 Vue组件全面指南回顾。
至于3号样式中的内容就属于css的知识了,用于设置该按钮组件的各种基本样式,不会的自己多敲一敲就好了。
注意scss中这里使用的&
符号,代表的是父标签,比如这里&:hover
等价于.Mbutton:hover
完成代码如下:
<script setup lang="ts">
</script>
<template>
<button class="MButton">
<slot></slot>
</button>
</template>
<style scoped lang="scss">
.MButton {
height: 25px;
background-color: #ecf5ff;
color: #48a2ff;
border-radius: 5px;
cursor: pointer;
user-select: none;
border: solid 1px #a0cfff;
&:hover {
background-color: #409eff;
color: #e5f2ff;
}
}
</style>
使用方法如下:
使用vue组件,其实和使用普通的标签没有太大的区别,正如上图所示:
- 首先你需要将组件导入到要使用的页面中
- 然后将导入的该组件直接写在template标签中即可
- 如果想要绑定基本事件,那就直接绑定即可,这里涉及到前面提到过的“透传”概念。
至于写在标签中的字符串“测试按钮”,也会通过插槽传入其内部slot
标签位置。
注意,这里标签的外围之所以会出现一个空隙,是因为其默认行为导致的,你可以在assets/main.css
这个全局样式文件中添加以下代码将其取消掉:
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
本节的代码实例中实际使用的是less,但其和scss语法几乎一致,至少本文是一致的,所以你可以直接将less更改为scss、其它的保持不变即可。
1.2 input组件
按钮控件比较简单,没用到太多的vue技术,所以这里再来一个稍微复杂一点的input
标签:
样式就不再多说了,可以参考后文源码自己参照一下即可,比较简单,这里主要讲解vue的语法运用。
首先注意,此时我并没有将input
标签作为该组件的根标签,所以外面想要绑定input
标签内的值就不能直接使用v-model
。
但为了能够让外面依旧可以这样用,这里我就做了一个中间层,也就是第1步,先在组件内部定义一个响应式变量input_value
绑定到input
标签上同步数据。
然后就是第2步,通过定义props
、emits
,就可以实现一个自定义的v-model
双向绑定值,比如我这里就是value
这个变量名。
紧接着来到第三步,使用watch
函数进行监视,一旦input
标签内的值发送变化,那么会自动同步到input_value
变量上,一旦input_value
值发送变化,就会立马触发相应的watch
函数,比如我这里就是通过emits
来更新外部的值。
同样的,如果外面传入了value
值、并且发生了变化,也会立马同步给input_value
,然后input_value
继续将数据同步给真正的input
标签。
简单来说就是,通过前面这一系列的步骤,就实现了v-model
中的标签数据与响应式变量双向同步的功能。
这里响应式变量指的是外部使用该组件传入的响应式变量,也就是这里触发的update:value
方法与value
值。
最后第4步,还添加了一个placeholder
属性,让外部填充input
标签的占位符,此时使用方式如下:
可能还存在很多细节之处没有处理到,可以自己在继续完善,完整代码如下:
<script setup lang="ts">
import { ref, watch } from 'vue';
const props = defineProps(['value', 'placeholder'])
const emits = defineEmits(['update:value'])
const input_value = ref('');
watch(input_value, () => {
emits('update:value', input_value.value)
})
if (props.value) {
watch(props.value, () => {
input_value.value = props.value;
})
}
</script>
<template>
<div class="MInput">
<input v-model="input_value" :placeholder="props.placeholder">
</div>
</template>
<style scoped lang="scss">
.MInput {
display: inline-block;
input {
width: 200px;
height: 25px;
border: 1px solid #dcdfe6;
border-radius: 5px;
outline: none;
padding: 0 10px;
font-size: 13px;
color: #8e9092;
&::placeholder {
color: #ddd;
}
}
}
</style>
1.3 简单总结
总的来说,vue中组件是一个和html中标签很像的东西,所以我相信你是可以很快上手的,当然如果你觉得还是不够熟悉,可以自己再试着复现几个常用组件练练手。
大多数时候我们是不需要自己去实现这些控件的,因为前端有很多开源库,就和上面两个组件一样,已经被别人封装好了,我们可以直接拿来用。
但想要熟练使用这些开源组件的前提是你得对这里基本的技术细节有一定的了解才行,我这里简单总结几个常用的组件功能:
props
:用于外部向组件内部传值的,一般被用于v-model
以及使用:
直接绑定的响应式变量,但同样也包括非响应式变量,比如前面input
标签中的placeholder
参数。emits
:用于内部触发外部函数的,常用于v-model
反向更新父组件响应式变量的值,同样也常用于使用@
符号绑定的自定义事件函数。watch
:用于监视响应式变量的变化slot
:常用于外部向组件内部传递html标签代码
不管怎么说,即使是复制粘贴使用开源库,我觉得你也应该至少学会使用这4个基本的vue功能,
2 目录树
开源UI库对于常用的前端控件已经实现的非常好了,比如后文将要介绍的Element-Plus开源库的使用。
但对于一些特殊的、不那么通用的组件,一般开源库都不会提供相应的实现,此时就需要我们自己封装了。
比如文本要介绍的“目录树组件”,在开源库中就很少有实现的,而其在一些需要显示文件目录的地方又确实很有用。
所以本文就带大家来看看一个目录树的实现逻辑。
2.1 代码思路
写代码前思路很重要,目录树大家应该都很熟悉,比如windows系统上的文件资源管理器中,就有很多目录,只不过它的目录结构并不允许多个层次的文件同时出现。
所以这里我以vscode
中目录树为基准:
它就可以很好的将不同目录下的文件全部展示到一个控件中去。
而我们的目的就是用vue实现出这样一个目录结构,经过前面的我的摸索,大概可以将目录树分为两个结构:
DirNode
:目录节点。
目录树最外层肯定是一个目录,也就是这里的DirNode
,每个目录里面只有两个东西:目录、文件,其中文件可以直接展示,而子目录则继续使用DirNode
表示,这样就完成了无限递归嵌套的任务,无论你有多少层,都可以用DirNode
表示出来。
FileTree
:目录树
这个结构主要是用来整理上面的DirNode
样式的,比如我们一般需要设置目录的最大高度,如果超出了这个高度就需要显示滑动条。
有了思路,我们就可以开始来写代码了,根据前面的思路,这里就将其分开作为两个组件进行实现。