圖文詳解Vue3實作全域搜尋框步驟(附程式碼)
這篇文章為大家帶來了關於Vue3的相關知識,其中主要介紹了Vue3是怎麼實現一個全局搜尋框,我會分享一下自己完整實現的思路,下面一起來看一下吧,希望對需要的朋友有幫助。
Vue3 如何實作一個全域搜尋框
前言:自從學習 vue 以來,就對 vue 官網全域的 command K 調出全域關鍵字搜尋這個功能心心念念。恰好最近專案也是需要實現一個全域搜尋的功能,也正好可以正大光明的帶薪學習這個功能的想法。網路上的教程水平參差不齊,而恰好之前的項目中我有做過一個類似於全局彈出麵包屑的功能,於是舉一反三寫出了一個我們項目需要的全局搜索框,特來分享一下自己的思路。
注意:本文不會馬上教你如何寫程式碼,而是作為一個引路人,一步一步引導你去理解這個元件的設計思路。會以「假如我是個初學者,如果我在學習這個知識的時候,別人能這樣告訴我,那麼我也可以很快的去理解」 的角度去講解,授人以魚不如授人以漁。希望你在閱讀本文的時候可以拓展思路,舉一反三。
一. 文件準備
你需要先準備三個文件,來完成這個全域搜尋框
SearchBar.ts 檔案
SearchBar.vue 檔案
#useSearch.ts 檔案
二. 搜尋框的樣式
樣式問題不是本文的重點,你可以花費五分鐘在SearchBar.vue 檔案內速寫一個非常簡易的正方形div 包裹著一個input 標籤即可快速進行下面的學習。
但是首先我們需要理清思路,這個元件是會出現在我們頁面的最頂端的,所以它元件內部需要用到絕對佈局。我們去SearchBar.vue
去設定一個樣式給最外層的div
,這裡其它樣式的寫法使用的是Uno CSS
,沒用過的小夥伴也不需要擔心,它只是單純的樣式,和本文中心內容不牽扯。 (CSS寫成計算屬性在這個場景也毫無特殊意義,只是單純設計時考慮多了)
#三.渲染函數h 和render 函數(重點)
打開之前準備的SearchBar.ts 文件,從vue 引入這兩個函數,並且把上一步寫好的簡陋版搜尋框(SearchBar.vue)引入這個檔案內。
看過我之前文章Vue3實作一個Toast 的讀者可能會比較熟悉一點點,但在那一篇文章內由於我也是初次接觸這兩個函數,所以當時總結的也不是特別精確,所以重新捋清思路,這裡再講解一下。
首先我們從官網的介紹,先來看看這個函數的定義。
可以看出,這個函數第一個參數是必填的,可以是一個string
和Component
,這篇文章重點討論參數為Component
的情況。重點是這個函數的回傳值,是一個VNode
,這個你一定不陌生,Virtual Node ,看這篇文章的讀者可能對虛擬dom 的原理可能不是那麼清楚,但我相信你們一定知道它的基本機制。 Vue 其實是先渲染 虛擬 dom -->然後 轉換成真實 dom。先別急著寫程式碼,我想你可能更清楚這樣的寫法,例如我們前面在 SearchBar.vue 檔案內寫的簡單的彈出框。
整個元件的樣式都是在Vue 提供的 元件內寫的,但是你要知道,Vue 在底層還是透過呼叫h() 來完成虛擬dom 的建置。而 只是 Vue 為了讓你用熟悉的原生 html 開發而為你提供的語法糖?而已。 (嗯,你可以這樣理解)
那麼我們可以根據上面h() 函數的介紹,它接收的第一個參數可以是
Component
,那我們這個SearchBar.vue
不就是元件嗎?那如果我不想使用 去展示這個組件的話,我是否可以這樣寫呢?h(SearchBar.vue)
。沒錯,是的,你就是可以這樣寫。別忘了h
的回傳值就是我們想要拿到的 Vnode ,所以依照正確的寫法是這樣的。
三. 寫SearchBarMaker 建構子和present 方法
- ##讓我們回到
SearchBar.ts
檔案。
- 首先思考,這個搜尋框一定有一個出現的函數,和一個消失的函數?,ok,起名字,一個
present,一個dismiss 。
- 接下來我需要建立一個
VNode ,然後想辦法處理成真實 dom。經過上面的學習,第一步馬上就可以想到下面的寫法。
- 下面這位比較是重量級,
render()
函數。虛擬
dom 有了,真實dom 該如何拿到? Vue 為我們提供了這樣一個函數,這裡我們需要重點去看這個函數的型別是值,是一個 RootRenderFunction類型的。
- 這裡我們轉換思路,我們看一下
render 函數的第二個參數是一個container:HostElement ,然後讓我們打開我們main.ts 文件,我們跳進mount
的定義部分,
發現神奇的地方了嗎,我們雖然不知道
HostElement 的型別是什麼,但你知道你mount
函數內填的參數是什麼了嗎? (忘掉的轉頭自覺複習官網哈。)
沒錯,就是全局唯一的一個真實
dom,一個樸實無華的id叫app的
div 元素。 由於篇幅限制,在這裡你可以先暫時簡單的理解,render 函數會將你的虛擬dom 包裝成一個真實dom 元素,但是你需要給它一個真實的外殼dom 來告訴它將虛擬dom 渲染到哪個位置。
ok,拿到一個包裝好的虛擬 dom ,接下來就是告訴瀏覽器在哪裡渲染這個元素。這裡我們需要思考?,既然是全局都可以彈出的,並且需要在所有元件之上彈出。
那麼最簡單的方法就是讓它出現在body的第一個元素,那麼它一定會和我們網頁所有的元件同級(tips:通常我們所有的頁面組成都會寫在body內 的一個div 內。什麼?你問我為什麼?請開啟你的index.html 看一下,你是否忘記了我們的App.vue 是掛在這個真實的,id為app 的元素內的)
那其實我們的操作的思路就是非常簡單的,當我按下全域搜尋按鈕,那麼你就在
<div id="app"> 的元素之前插入我的元件即可。 <img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/000/020/659e9d144b30400682dc69d8bf2b853c-18.png" class="lazy" alt="圖文詳解Vue3實作全域搜尋框步驟(附程式碼)" loading="lazy"><li><p>ok,到這裡我們已經可以看到基本效果了,我們來測試一下。讓我們在 <code>App.vue
元件內隨便寫一個按鈕,然後呼叫SearchBarCreator
實例身上的present
方法。 (maker 感覺不是那麼合理,之後我們將SearchBarMaker
變更為SeachBarCreator
的叫法,僅僅是名字變了而已,邏輯什麼的根本沒變哦.?)效果如下:
到這裡searchBar 已經可以呈現在頁面上了,但是我們還不知道怎樣讓它消失,其實也非常簡單,我們只需要在適當的時機移除這個dom 元素。
在這裡我們需要知道一點,我們需要將
searchBar
提升到目前檔案的全域,不能只在open
中去new
了。ok,我們測試一下
在上面的這種情況下,我們已經可以在
App.vue
檔案內去new
一個實例來呼叫這個搜尋框了。但是我們加入現在需要在XXX.vue
檔案內呼叫這個搜尋框呢?我還需要重新去引入,然後重新new
嗎? nonono,某位大佬說過,程式設計師都是很懶的,不可能寫這種低階的重複程式碼的。那麼該如何實現呢打開我們之前準備的useSearch.ts 文件,我們把之前在
App.vue
的全局生成的這個SearchBar 實例轉換思路,使它在全域的一個ts 檔案內產生一個,然後把這個實例本身的一些方法封裝成函數,並暴露給外部。那我就可以在全域任意一個地方去呼叫這個實例身上的這兩個方法。讓我們在
App.vue
去試試看。
這是我們之前的App.vue
檔案的呼叫方法。我們改造一下它。
我們再次測試一下功能有沒有什麼問題
如此一來就方便很多了,我們可以在任意位置去調用這個「唯一的搜尋框」再此之前,我們需要先理解一個概念,注意我們的
main.ts
文件,我們是把誰掛在了全局的那一個id='app'
的真實dom 下的?
沒錯,就是前面我們提到的 App.vue 元件。那麼假如我在這個
App.vue
元件掛載的時候,給全域window 物件身上加上一個鍵盤事件,是不是就可以了呢?怎麼添加呢?其實非常非常簡單,要用到見組合按鍵,我們就需要使用到“keydown”,具體為什麼不是“keypress” ,讀者可以自行查閱這兩者的區別,不屬於本文的主要探討內容。這時候,我們先來按一下
command
看看列印的內容是什麼。這裡重點的內容是該鍵盤事件身上的metaKey
屬性。在這裡我們也可以推導出按下「ctrl」 的事件為
keydown# 事件支援多個按鍵同時按下。當我們同時按下 “command” 和 “K” 鍵,會發生什麼事?
但我們發現好像並沒有
K:true
這個屬性呀,那我們怎麼去判斷呢?別急接著往下看。-
我們可以看到鍵盤事件event 身上有個key 屬性,它的值恰好是字串類型的“ k”,
我們測試一下,我們去吧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 ,這個點只是類別名稱選擇器,不要忘記了基礎知識。
測試效果:
四. 最佳化SearchBarCreator 建構子的程式碼邏輯
#寫到這裡的時候,你可能發現了一個小問題,當我一直去按搜尋按鈕的時候,它會出現多個搜尋框,但是我們希望的是它在全域只能出現一個搜尋框。換個角度思考,也就是同一時間,這個被我們 new
出來的 SeachBar
實例只能出現一個。思考一下?,我加一個變量,isShowing 是否正在被展示
,如果正在被展示的話,那麼用戶再次調用present
的時候,我就去調用實例自身的dismiss
方法讓它消失,是否可行呢? 測試一下:
OK,看來完美解決目前的問題了。
五. 寫全域唯一的呼叫實例
#六. 在新增全域的快捷鍵Command K
這裡我直接公佈寫法,js 允許我們這樣判斷是否同時按下兩個按鍵。
七. 新增出現的動畫
#八.自動對焦
在彈出框的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("不需要关闭") } } }
推薦學習:《vue.js影片教學》
以上是圖文詳解Vue3實作全域搜尋框步驟(附程式碼)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

想要實現頁面的局部刷新,我們只需要實現局部元件(dom)的重新渲染。在Vue中,想要實現這效果最簡單的方式方法就是使用v-if指令。在Vue2中我們除了使用v-if指令讓局部dom的重新渲染,也可以新建一個空白元件,需要刷新局部頁面時跳轉至這個空白元件頁面,然後在空白元件內的beforeRouteEnter守衛中又跳轉回原來的頁面。如下圖所示,如何在Vue3.X中實現點擊刷新按鈕實現紅框範圍內的dom重新加載,並展示對應的加載狀態。由於Vue3.X中scriptsetup語法中組件內守衛只有o

tinymce是一個功能齊全的富文本編輯器插件,但在vue中引入tinymce並不像別的Vue富文本插件一樣那麼順利,tinymce本身並不適配Vue,還需要引入@tinymce/tinymce-vue,並且它是國外的富文本插件,沒有透過中文版本,需要在其官網下載翻譯包(可能需要翻牆)。 1.安裝相關依賴npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2、下載中文包3.引入皮膚和漢化包在項目public資料夾下新建tinymce資料夾,將下載的

vue3+vite:src使用require動態導入圖片報錯和解決方法vue3+vite動態的導入多張圖片vue3如果使用的是typescript開發,就會出現require引入圖片報錯,requireisnotdefined不能像使用vue2這樣imgUrl:require(' …/assets/test.png')導入,是因為typescript不支援require所以用import導入,下面介紹如何解決:使用awaitimport

Vue實作部落格前端,需要實作markdown的解析,如果有程式碼則需要實作程式碼的高亮。 Vue的markdown解析函式庫很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。這些庫都大同小異。這裡選用的是marked,程式碼高亮的函式庫選用的是highlight.js。具體實現步驟如下:一、安裝依賴庫在vue專案下開啟命令窗口,並輸入以下命令npminstallmarked-save//marked用於將markdown轉換成htmlnpmins

前言無論是vue還是react,當我們遇到多處重複程式碼的時候,我們都會想著如何重複使用這些程式碼,而不是一個檔案裡充斥著一堆冗餘程式碼。實際上,vue和react都可以透過抽組件的方式來達到復用,但如果遇到一些很小的程式碼片段,你又不想抽到另外一個檔案的情況下,相比而言,react可以在相同文件裡面宣告對應的小元件,或透過renderfunction來實現,如:constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

最終效果安裝VueCropper組件yarnaddvue-cropper@next上面的安裝值針對Vue3的,如果時Vue2或想使用其他的方式引用,請訪問它的npm官方地址:官方教程。在元件中引用使用時也很簡單,只需要引入對應的元件和它的樣式文件,我這裡沒有在全域引用,只在我的元件檔案中引入import{userInfoByRequest}from'../js/api' import{VueCropper}from'vue-cropper&

vue3+ts+axios+pinia實作無感刷新1.先在專案中下載aiXos和pinianpmipinia--savenpminstallaxios--save2.封裝axios請求-----下載js-cookienpmiJS-cookie-s//引入aixosimporttype{AxiosRequestConfigig ,AxiosResponse}from"axios";importaxiosfrom'axios';import{ElMess

vue3專案打包發佈到伺服器後存取頁面顯示空白1、處理vue.config.js檔案中的publicPath處理如下:const{defineConfig}=require('@vue/cli-service')module.exports=defineConfig({publicPath :process.env.NODE_ENV==='production'?'./':'/&
