五. 编写全局唯一的调用实例
在上面的这种情况下,我们已经可以在 App.vue
文件内去 new
一个实例来调用这个搜索框了。但是我们加入现在需要在 XXX.vue
文件内调用这个搜索框呢?我难道还需要重新去引入,然后重新 new
吗?nonono,某位大佬说过,程序员都是很懒的,不可能写这种低级的重复代码的。那么该如何实现呢
打开我们之前准备的 useSearch.ts 文件,我们把之前在 App.vue
的全局生成的这个 SearchBar 实例转换思路,使它在全局的一个 ts 文件内生成一个,然后把这个实例自身的一些方法封装成函数,暴露给外部。那么我就可以在全局任意一个地方去调用这个实例身上的这两个方法。
让我们在 App.vue
去试一下。
这是我们之前的 App.vue
文件的调用方法。
我们改造一下它。
我们再次测试一下功能有没有什么问题
如此一来就方便很多了,我们可以在任意位置去调用这个“唯一的搜索框”
六. 添加全局的快捷键 Command + K
再此之前,我们需要理解一个概念,注意我们的 main.ts
文件,我们是把谁挂在了全局的那一个
id='app'
的真实 dom 下的?
没错,就是前面我们提到的 App.vue 组件。
那么假如我在这个 App.vue
组件挂载的时候,给全局 window 对象身上添加一个键盘事件,是不是就可以了呢?怎么添加呢?其实非常非常简单,要用到见组合按键,我们就需要使用到 “keydown”,具体为什么不是 “keypress” ,读者可以自行查阅这两者的区别,不属于本文的主要探讨内容。
这时候,我们先来按一下 command
看看打印的内容是什么。这里重点的内容是该键盘事件身上的metaKey
属性。
在这里我们还可以推算出按下 “ctrl” 的事件为
keydown 事件支持多个按键同时按下。当我们同时按下 “command” 和 “K” 键,会发生什么呢?
但是我们发现好像并没有 K:true
这个属性呀,那我们怎么去判断呢?别着急接着往下看。
我们可以看到键盘事件 event 身上有个 key 属性,它的值恰好是字符串类型的 “k”,
这里我直接公布写法,js 允许我们这样判断是否同时按下两个按键。
我们测试一下,我们去吧 App.vue 文件内的这两个按钮给去掉
然后再打印一下我们按下 command
和 k
的时候。
七. 添加出现的动画
在上面我们可以看到,这样突然的出现好像有一丝丝的突兀。我希望这个搜索框在出现的时候,可以有那么一丝丝的平移效果,(类似于下面的效果)该如何做呢?🤔
我这里介绍一种较为简单的思路,我们在 App.vue
文件的 style 内预设一个 Css 动画,并起好名字。叫做"searchInput"
然后回到我们 searBar.vue
的组件去,给我们这个组件最外层的起一个好听的名字,我这里就叫做 searchBarWrapper
。
然后回到我们的 SearchBar.ts
文件内,也就是放我们 SeachBarCreator 构造函数的那个文件内。(tips:不是 useSearch.ts 哦) 我这里解释一下思路,在调用 render 函数后,这个组件其实已经渲染成为一个真实的 dom 元素,只不过我们还没给它指定渲染的位置。既然是真实的 dom ,那么我们就可以通过 document.getElementById这个方法(querySelector同理,一个意思)
拿到这个SearchBar.vue组件
,接下来我只需要在调用 document.body.insertBefore
方法前,给它添加上刚刚我们在 App.vue
里预设好的类名,searchInput
,就完美达成我们想要的效果了。
注意:style ,这个点仅仅是类名选择器,不要忘记了基础知识。
八. 自动聚焦
在弹出框的 input 框实现自动聚焦相比于之前讲的就非常简单了,我在这里一笔带过了。只需要在 nextTick 中调用 input 本身的 focus 方法即可。
总结:
之所以不喜欢使用真代码去写文章而大量使用截图的原因是:我自己在搜索到自己想要的文章后,也会喜欢直接看有没有最后的成品代码,然后直接复制就拿过去用了,而往往忽略了自己动手去实现一遍才是真正理解了的过程。
所以我写代码的时候,尽量不写特别复杂的逻辑,而写一些很简单的几行代码去实现某一个功能。是因为我希望你们真正带入自己的思考,和一步步体会这个实现过程,从而举一反三。
如果你认真看了该文章,你也许会明白现在很多组件库的底层实现原理其实就是这样的,比如全局弹出的dialog ,modal 框等等。我们要去理解组件库组件实现的思路,而不是一味的复制粘贴。
这个搜索框有很多可以更加优化的地方,你们可以带入自己的思考去想一想。比如
1.如何保存搜索历史?
2.如何实现实时的给出搜索联想
与君共勉才是我的初衷...
源码
这里贴出核心代码 SearchBar.ts
文件的源码,希望读者可以仅作为参考使用,希望不要直接复制粘贴。
import { h, render } from "vue" import SearchBar from "./SearchBar.vue" class SearchBarCreator { container: HTMLElement appElement: HTMLElement | null showing: boolean _dismiss: () => void constructor() { this.container = document.createElement("div") this.showing = false this.appElement = document.body.querySelector("#app") this.present.bind(this) this.dismiss.bind(this) this._dismiss = this.dismiss.bind(this) } present() { if (this.showing) { this.dismiss() } else { const SearchBar = h(h(SearchBar)) render(SearchBar, this.container) const searchBarWrapperDOM = this.container.querySelector("#searchBarWrapper") searchBarWrapperDOM?.classList.add("animate-searchInputAnimation") document.body.insertBefore(this.container, document.body.firstChild) this.showing = true this.appElement?.addEventListener("click", this._dismiss) } } dismiss() { if (this.showing && this.container) { render(null, this.container) document.body.removeChild(this.container) this.showing = false this.appElement?.removeEventListener("click", this._dismiss) } else { console.log("不需要关闭") } } }