前期你需要准备三个文件,来完成这个全局搜索框
样式问题不是本文的重点,你可以花费五分钟在 SearchBar.vue 文件内速写一个非常简易的正方形 div 包裹着一个 input 标签即可快速进行下面的学习。
但是首先我们需要理清思路,这个组件是会出现在我们页面的最顶部的,所以它组件内部需要用到绝对布局。我们去 SearchBar.vue
去设置一个样式给最外层的 div
,这里其它样式的写法使用的是 Uno CSS
,没用过的小伙伴也不需要担心,它只是单纯的样式,和本文中心内容不牵扯。(CSS写成计算属性在这个场景也毫无特殊意义,只是单纯设计时考虑多了)
可以看出,这个函数第一个参数是必填的,可以是一个 string
和 Component
,这篇文章重点讨论参数为 Component
的情况。重点是这个函数的返回值,是一个 VNode
,这个你一定不陌生,Virtual Node ,看本篇文章的读者可能对虚拟 dom 的原理可能不是那么清楚,但是我相信你们一定知道它的基本机制。Vue 其实是先渲染 虚拟 dom -->然后 转换成真实 dom。
整个组件的样式都是在 Vue 提供的 <template> 组件内写的,但是你要知道,Vue 在底层还是通过调用 h() 来完成虚拟 dom 的构建。而 <template> 仅仅只是 Vue 为了让你用熟悉的原生 html 开发而为你提供的语法糖🍬而已。(嗯,你可以这样理解)
Component
,那我们这个 SearchBar.vue
不就是组件吗?那如果我不想使用 <template> 去展示这个组件的话,我是否可以这样写呢?h(SearchBar.vue)
。没错,是的,你就是可以这样写。别忘了 h
的返回值就是我们想拿到的 Vnode ,所以按照正确的写法是这样的。
SearchBar.ts
文件。
render()
函数。虚拟 dom 有了,真实dom 该如何拿到呢? Vue 为我们提供了这样一个函数,这里我们需要重点去看这个函数的类型是值,是一个 RootRenderFuncion
类型的。
mount
的定义部分,
发现神奇的地方了吗,我们虽然不知道 HostElement 的类型是什么,但是你知道你 mount
函数内填的参数是什么了吗?(忘掉的转头自觉复习官网哈。)
没错,就是全局唯一的一个真实 dom,一个朴实无华的id叫 app
的 div 元素。
由于篇幅限制,在这里你可以先暂时简单的理解,render 函数会将你的虚拟 dom 包装成一个真实 dom 元素,但是你需要给它一个真实的 外壳dom 来告诉它将虚拟 dom 渲染到哪个位置。
那么最简单的方法就是让它出现在 body的第一个元素,那么它一定会和我们网页所有的组件同级别(tips:通常我们所有的页面构成都会写在 body内 的一个 div 内。什么?你问我为什么?请打开你的 index.html 看一下,你是否忘记了我们的 App.vue 是挂在这个真实的,id为 app 的元素内的)
那其实我们的操作的思路就是非常简单的,当我按下全局搜索按钮,那么你就在 <div id="app">
的元素之前插入我的组件即可。
App.vue
组件内随便写一个按钮,然后调用 SearchBarCreator
实例身上的 present
方法。(maker 感觉不是那么合理,之后我们将 SearchBarMaker
变更为 SeachBarCreator
的叫法,仅仅是名字变了而已,逻辑什么的根本没变哦。🍦)
效果如下: 图片上传处理中...
在这里我们需要知道一点,我们需要将 searchBar
提升到当前文件的全局,不能仅只在 open
中去 new
了。
ok,我们测试一下 图片上传处理中...
写到这里的时候,你可能发现了一个小问题,当我一直去按搜索按钮的时候,它会出现多个搜索框,但是我们希望的是它在全局只能出现一个搜索框。换个角度思考,也就是同一时间,这个被我们 new
出来的 SeachBar
实例只能出现一个。思考一下🤔,我加一个变量,isShowing 是否正在被展示
,如果正在被展示的话,那么用户再次调用 present
的时候,我就去调用实例自身的 dismiss
方法让它消失,是否可行呢?
App.vue
文件内去 new
一个实例来调用这个搜索框了。但是我们加入现在需要在 XXX.vue
文件内调用这个搜索框呢?我难道还需要重新去引入,然后重新 new
吗?nonono,某位大佬说过,程序员都是很懒的,不可能写这种低级的重复代码的。那么该如何实现呢App.vue
的全局生成的这个 SearchBar 实例转换思路,使它在全局的一个 ts 文件内生成一个,然后把这个实例自身的一些方法封装成函数,暴露给外部。那么我就可以在全局任意一个地方去调用这个实例身上的这两个方法。
App.vue
去试一下。
这是我们之前的 App.vue
文件的调用方法。
我们改造一下它。
如此一来就方便很多了,我们可以在任意位置去调用这个“唯一的搜索框”
main.ts
文件,我们是把谁挂在了全局的那一个 id='app'
的真实 dom 下的?
没错,就是前面我们提到的 App.vue 组件。
App.vue
组件挂载的时候,给全局 window 对象身上添加一个键盘事件,是不是就可以了呢?怎么添加呢?其实非常非常简单,要用到见组合按键,我们就需要使用到 “keydown”,具体为什么不是 “keypress” ,读者可以自行查阅这两者的区别,不属于本文的主要探讨内容。
command
看看打印的内容是什么。这里重点的内容是该键盘事件身上的metaKey
属性。
在这里我们还可以推算出按下 “ctrl” 的事件为
但是我们发现好像并没有 K:true
这个属性呀,那我们怎么去判断呢?别着急接着往下看。
这里我直接公布写法,js 允许我们这样判断是否同时按下两个按键。
然后再打印一下我们按下 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
,就完美达成我们想要的效果了。
在弹出框的 input 框实现自动聚焦相比于之前讲的就非常简单了,我在这里一笔带过了。只需要在 nextTick 中调用 input 本身的 focus 方法即可。
之所以不喜欢使用真代码去写文章而大量使用截图的原因是:我自己在搜索到自己想要的文章后,也会喜欢直接看有没有最后的成品代码,然后直接复制就拿过去用了,而往往忽略了自己动手去实现一遍才是真正理解了的过程。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文系转载,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。