vue3中用30行代码实现一个cmd k全局搜索框
概述
本文主要介绍在vue3中用h函数动态生成组件,通过函数调用的方式去挂载dom,控制组件的显示和隐藏,例如各类ui框架中,$message等方法的调用实现。
下面通过一个全局搜索框的例子来介绍实现方法
实现
需要准备3个文件 :一个搜索框ui组件serachBar.vue,(构造函数类searchBarC.ts,一个方法调用处index.vue 30行代码内)
serachBar.vue
首先先完成一个常规的vue组件,用于视图显示,并补充部分样式,这个组件大家根据各自实际需要去写就行了,你可以是一个对话框,一个确认框,也可以像该案例中一个搜索框。
我这里写了一个非常简陋的搜索框,大家不嫌弃可以简单套用一下。 这里加了一个渐出动画
<template>
<div class="search-bar">
<input
v-model="state.input"
placeholder="请输入内容"
@keyup.enter.native="handle"
/>
<button @click="handle">搜索</button>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive } from "vue";
const state = reactive({
input: "",
});
function handle() {
console.log("handle");
}
</script>
<style lang="less" scoped>
.search-bar {
position: absolute;
top: 30%;
left: 50%;
transform: translate(-50%, 0);
z-index: 1000;
width: 60%;
height: 60px;
line-height: 60px;
text-align: center;
background: rgb(78 78 78 / 5%);
backdrop-filter: blur(8px);
border-radius: 8px;
overflow: hidden;
box-shadow: 10px 5px 5px #8d80ad;
animation: searchShow 1s;
}
@keyframes searchShow {
0% {
opacity: 0;
transform: translate(-50%, 50px);
}
100% {
opacity: 1;
transform: translate(-50%, 0);
}
}
</style>
searchBarC.ts
我们用一个构造类去封装组件的生成和挂载的行为,主要讲一下这个show方法。 整个过程分为3步:
- 通过h函数,把我们提供的.vue组件转化为一个VNode虚拟节点。(因为Vue 其实就是先渲染 虚拟 dom -->然后 转换成真实 dom,而平常通过template的写法其实就是个语法糖,最终内部都要转为VNode的。)
- 调用render函数,将刚才的虚拟节点挂载到真实的dom上,也就是生成真实dom结构的过程。(createApp(App).mount("#app") 这种写法总熟悉了吧,其实是一样的道理)
- 到这里时,我们只是创建了一个dom结构,但是没有添加浏览器的页面结构内,所以最后一步只需要把appendChild把他塞进去就好了
import { h, render } from "vue";
import search from "./serachBar.vue";
class SerachBarCreator {
container: HTMLDivElement;
constructor() {
this.container = document.createElement("div");
}
show() {
let vnode = h(search);
render(vnode, this.container);
document.body.appendChild(this.container);
}
hide() {
document.body.removeChild(this.container);
}
}
let searchBar = new SerachBarCreator();
export default searchBar;
到这里,其实我们已经写完了,剩下的就是调用它了。
index.vue
调用就更简单了,因为实例化的过程在内部就做了,导出直接是实例,所以只要执行实例的方法就好了。
import searchBar from "./searchBarC";
function handle() {
searchBar.show();
}
function handle2() {
searchBar.hide();
}
另外需要实现command+k组合键唤起,只要添加一个键盘输入的监听事件就好了
onMounted(() => {
// keydown 事件支持多个按键同时按下
window.addEventListener("keydown", (e) => {
if (e.metaKey && e.key === "k") {
searchBar.show();
}
// esc键
if (e.key === "Escape") {
searchBar.hide();
}
});
});
到此为止就全部完成了,快去按下按键试试吧
效果展示
思考
在这个过程中其实有一个细节点,当时我在实现的时候想到,如果多次调用show方法,会不会导致多个组件被渲染到页面上,那么就需要实现一个单例模型去限制这种情况。
但是在实际操作中发现 多次执行document.body.appendChild(this.container);
这一步并不会多次添加dom到页面当中,仍然只会保持一个,这是为什么呢?
后来我去查了MDN-appendChild,发现里面是这样写到:
-
Node.appendChild()
方法将一个节点附加到指定父节点的子节点列表的末尾处。如果将被插入的节点已经存在于当前文档的文档树中,那么appendChild()
只会将它从原先的位置移动到新的位置(不需要事先移除要移动的节点)。 -
这意味着,一个节点不可能同时出现在文档的不同位置。所以,如果某个节点已经拥有父节点,在被传递给此方法后,它首先会被移除,再被插入到新的位置。若要保留已在文档中的节点,可以先使用
Node.cloneNode()
方法来为它创建一个副本,再将副本附加到目标父节点下。请注意,用cloneNode
制作的副本不会自动保持同步。
因为创建这个节点只在构造函数初始化的过程中执行了一次, this.container = document.createElement("div");
后续我们都是在操作这同一个节点,但是如果我们多次执行了document.createElement,那就会创建出多个不同的节点,此时appendChild 就会添加多个不同的节点进去。
以上~
转载自:https://juejin.cn/post/7183609407337398328