Vue3 如何实现一个全局搜寻框

前言:自从学习 vue 以来,就对 vue 官网全局的 command + K 调出全局关键词搜寻这个性能心心念念。恰好最近我的项目也是须要实现一个全局搜寻的性能,也正好能够正大光明的带薪学习这个性能的思路。网上的教程程度参差不齐,而恰好之前的我的项目中我有做过一个相似于全局弹出面包屑的性能,于是触类旁通写出了一个咱们我的项目须要的全局搜寻框,特来分享一下本人的思路。

留神:本文不会马上教你如何编写代码,而是作为一个引路人,一步一步疏导你全了解思路。会以 “如果我是一个初学者,如果我在学习这个常识的时候,他人能这样通知我,那么我也能够很快的去了解” 的角度去解说 ,授人以鱼不如授人以渔。心愿你在浏览本文的时候能够拓展思路,触类旁通。

一. 文件筹备

后期你须要筹备三个文件,来实现这个全局搜寻框

  1. SearchBar.ts 文件
  2. SearchBar.vue 文件
  3. useSearch.ts 文件

二. 搜寻框的款式

款式问题不是本文的重点,你能够破费五分钟在 SearchBar.vue 文件内速写一个十分繁难的正方形 div 包裹着一个 input 标签即可疾速进行上面的学习。

然而首先咱们须要理清思路,这个组件是会呈现在咱们页面的最顶部的,所以它组件外部须要用到相对布局。咱们去 SearchBar.vue 去设置一个款式给最外层的 div,这里其它款式的写法应用的是 Uno CSS,没用过的小伙伴也不须要放心,它只是单纯的款式,和本文核心内容不牵扯。(CSS写成计算属性在这个场景也毫无非凡意义,只是单纯设计时思考多了)


三. 渲染函数 hrender 函数(重点)

  1. 关上之前筹备的 SearchBar.ts 文件,从 vue 里引入这两个函数,并且把在上一步写好的简陋版搜寻框(SearchBar.vue)引入到这个文件内。
  2. 看过我之前文章 Vue3实现一个 Toast 的读者可能会比拟相熟一点点,然而在那一篇文章内因为我也是首次接触这两个函数,所以过后总结的也不是特地准确,所以从新捋清思路,这里再解说一下。
  3. 首先咱们从官网的介绍,先看一下这个函数的定义。

    能够看出,这个函数第一个参数是必填的,能够是一个 stringComponent,这篇文章重点探讨参数为 Component 的状况。重点是这个函数的返回值,是一个 VNode,这个你肯定不生疏,Virtual Node ,看本篇文章的读者可能对虚构 dom 的原理可能不是那么分明,然而我置信你们肯定晓得它的根本机制。Vue 其实是先渲染 虚构 dom -->而后 转换成实在 dom
  4. 先别急着写代码,我想你可能更分明这样的写法,比方咱们后面在 SearchBar.vue 文件内写的简略的弹出框。

    整个组件的款式都是在 Vue 提供的 \<template> 组件内写的,然而你要晓得,Vue 在底层还是通过调用 h() 来实现虚构 dom 的构建。而 \<template> 仅仅只是 Vue 为了让你用相熟的原生 html 开发而为你提供的语法糖而已。(嗯,你能够这样了解)
  5. 那么咱们能够依据下面 h() 函数的介绍,它接管的第一参数能够是 Component ,那咱们这个 SearchBar.vue 不就是组件吗?那如果我不想应用 \<template> 去展现这个组件的话,我是否能够这样写呢?h(SearchBar.vue)。没错,是的,你就是能够这样写。别忘了 h 的返回值就是咱们想拿到的 Vnode ,所以依照正确的写法是这样的。

三. 编写 SearchBarMaker 构造函数和 present 办法

  1. 让咱们回到 SearchBar.ts 文件。
  2. 首先思考,这个搜寻框肯定有一个呈现的函数,和一个隐没的函数,ok,起名字,一个 present,一个 dismiss
  3. 接下来我须要创立出一个 VNode ,而后想方法解决成实在 dom。通过下面的学习,第一步马上就能够想到上面的写法。
  4. 上面这位更是重量级,render() 函数。虚构 dom 有了,实在dom 该如何拿到呢? Vue 为咱们提供了这样一个函数,这里咱们须要重点去看这个函数的类型是值,是一个 RootRenderFuncion 类型的。
  5. 这里咱们转变一下思路,咱们看一下 render 函数的第二个参数是 一个 container:HostElement ,而后让咱们关上咱们 main.ts 文件,咱们跳进 mount的定义局部,



    发现神奇的中央了吗,咱们尽管不晓得 HostElement 的类型是什么,然而你晓得你 mount 函数内填的参数是什么了吗?(忘掉的转头盲目温习官网哈。)
    没错,就是全局惟一的一个实在 dom,一个奢侈无奈的,id叫 appdiv 元素。

    因为篇幅限度,在这里你能够先临时简略的了解,render 函数会将你的虚构 dom 包装成一个实在 dom 元素,然而你须要给它一个实在的 外壳dom 来通知它将虚构 dom 渲染到哪个地位。
  6. ok,拿到一个包装后的虚构 dom ,接下来就是通知浏览器在哪里渲染这个元素。这里咱们须要思考,既然是全局都能够弹出的,并且须要在所有组件之上弹出。

    那么最简略的办法就是让它呈现在 body的第一个元素,那么它肯定会和咱们网页所有的组件同级别(tips:通常咱们所有的页面形成都会写在 body内 的一个 div 内。什么?你问我为什么?请关上你的 index.html 看一下,你是否遗记了咱们的 App.vue 是挂在这个实在的,id为 app 的元素内的)

    那其实咱们的操作的思路就是非常简单的,当我按下全局搜寻按钮,那么你就在 <div id="app"> 的元素之前插入我的组件即可。
  7. ok,到这里咱们曾经能够看到根本成果了,咱们来测试一下。让咱们在 App.vue 组件内轻易写一个按钮,而后调用 SearchBarCreator 实例身上的 present 办法。(maker 感觉不是那么正当,之后咱们将 SearchBarMaker 变更为 SeachBarCreator 的叫法,仅仅是名字变了而已,逻辑什么的基本没变哦。)

    成果如下:
  8. 到这里 searchBar 曾经能够出现在页面上了,然而咱们还不晓得怎么让它隐没,其实也非常简单,咱们只须要在适合的机会移除这个 dom 元素即可。

    在这里咱们须要晓得一点,咱们须要将 searchBar 晋升到以后文件的全局,不能仅只在 open
    中去 new 了。

    ok,咱们测试一下

四. 优化 SearchBarCreator 构造函数的代码逻辑

写到这里的时候,你可能发现了一个小问题,当我始终去按搜寻按钮的时候,它会呈现多个搜寻框,然而咱们心愿的是它在全局只能呈现一个搜寻框。换个角度思考,也就是同一时间,这个被咱们 new 进去的 SeachBar 实例只能呈现一个。思考一下,我加一个变量,isShowing 是否正在被展现 ,如果正在被展现的话,那么用户再次调用 present 的时候,我就去调用实例本身的 dismiss 办法让它隐没,是否可行呢?

测试一下:

OK,看来完满解决以后的问题了。

五. 编写全局惟一的调用实例

  1. 在下面的这种状况下,咱们曾经能够在 App.vue 文件内去 new 一个实例来调用这个搜寻框了。然而咱们退出当初须要在 XXX.vue 文件内调用这个搜寻框呢?我难道还须要从新去引入,而后从新 new 吗?nonono,某位大佬说过,程序员都是很懒的,不可能写这种低级的反复代码的。那么该如何实现呢
  2. 关上咱们之前筹备的 useSearch.ts 文件,咱们把之前在 App.vue 的全局生成的这个 SearchBar 实例转换思路,使它在全局的一个 ts 文件内生成一个,而后把这个实例本身的一些办法封装成函数,裸露给内部。那么我就能够在全局任意一个中央去调用这个实例身上的这两个办法。
  3. 让咱们在 App.vue 去试一下。

    这是咱们之前的 App.vue 文件的调用办法。

    咱们革新一下它。

    咱们再次测试一下性能有没有什么问题

    如此一来就不便很多了,咱们能够在任意地位去调用这个“惟一的搜寻框”

六. 增加全局的快捷键 Command + K

  1. 再此之前,咱们须要了解一个概念,留神咱们的 main.ts 文件,咱们是把谁挂在了全局的那一个 id='app' 的实在 dom 下的?

    没错,就是后面咱们提到的 App.vue 组件。
  2. 那么如果我在这个 App.vue 组件挂载的时候,给全局 window 对象身上增加一个键盘事件,是不是就能够了呢?怎么增加呢?其实十分非常简单,要用到见组合按键,咱们就须要应用到 “keydown”,具体为什么不是 “keypress” ,读者能够自行查阅这两者的区别,不属于本文的次要探讨内容。
  3. 这时候,咱们先来按一下 command 看看打印的内容是什么。这里重点的内容是该键盘事件身上的metaKey 属性。

    在这里咱们还能够推算出按下 “ctrl” 的事件为
  4. keydown 事件反对多个按键同时按下。当咱们同时按下 “command” 和 “K” 键,会产生什么呢?

    然而咱们发现如同并没有 K:true 这个属性呀,那咱们怎么去判断呢?别着急接着往下看。
  5. 咱们能够看到键盘事件 event 身上有个 key 属性,它的值恰好是字符串类型的 “k”


    这里我间接颁布写法,js 容许咱们这样判断是否同时按下两个按键。
  6. 咱们测试一下,咱们去吧 App.vue 文件内的这两个按钮给去掉

    而后再打印一下咱们按下 commandk 的时候。

    测试一下:

七. 增加呈现的动画

  1. 在下面咱们能够看到,这样忽然的呈现如同有一丝丝的突兀。我心愿这个搜寻框在呈现的时候,能够有那么一丝丝的平移成果,(相似于上面的成果)该如何做呢?
  2. 我这里介绍一种较为简单的思路,咱们在 App.vue 文件的 style 内预设一个 Css 动画,并起好名字。叫做 "searchInput"
  3. 而后回到咱们 searBar.vue 的组件去,给咱们这个组件最外层的起一个好听的名字,我这里就叫做 searchBarWrapper
  4. 而后回到咱们的 SearchBar.ts 文件内,也就是放咱们 SeachBarCreator 构造函数的那个文件内。(tips:不是 useSearch.ts 哦)
    我这里解释一下思路,在调用 render 函数后,这个组件其实曾经渲染成为一个实在的 dom 元素,只不过咱们还没给它指定渲染的地位。既然是实在的 dom ,那么咱们就能够通过 document.getElementById这个办法(querySelector同理,一个意思)拿到这个SearchBar.vue组件 ,接下来我只须要在调用 document.body.insertBefore 办法前,给它增加上刚刚咱们在 App.vue 里预设好的类名,searchInput ,就完满达成咱们想要的成果了。
  5. 留神:style ,这个点仅仅是类名选择器,不要遗记了基础知识。
  6. 测试一下成果:

八. 主动聚焦

在弹出框的 input 框实现主动聚焦相比于之前讲的就非常简单了,我在这里一笔带过了。只须要在 nextTick 中调用 input 自身的 focus 办法即可。

总结:

之所以不喜爱应用真代码去写文章而大量应用截图的起因是:我本人在搜寻到本人想要的文章后,也会喜爱间接看有没有最初的成品代码,而后间接复制就拿过来用了,而往往疏忽了本人入手去实现一遍才是真正了解了的过程。

所以我写代码的时候,尽量不写特地简单的逻辑,而写一些很简略的几行代码去实现某一个性能。是因为我心愿你们真正带入本人的思考,和一步步领会这个实现过程,从而触类旁通。

如果你认真看了该文章,你兴许会明确当初很多组件库的底层实现原理其实就是这样的,比方全局弹出的dialogmodal 框等等。咱们要去了解组件库组件实现的思路,而不是一味的复制粘贴。

这个搜寻框有很多能够更加优化的中央,你们能够带入本人的思考去想一想。比方

1.如何保留搜寻历史?
2.如何实现实时的给出搜寻联想

与君共勉才是我的初衷...