vue實現(xiàn)搜索功能且關(guān)鍵字高亮,介紹

很多伙伴在自己用vue學(xué)習(xí)開發(fā)的過程中耸峭,不像真正的工作開發(fā)桩蓉,有后端支持,可以從后端那里調(diào)接口拿到真實的數(shù)據(jù)進行展示劳闹。但為了模擬這個過程院究,我們經(jīng)常使用自己在本地編寫模擬的數(shù)據(jù)進行使用,然后通過發(fā)送ajax請求得到我們本地的數(shù)據(jù)本涕。
這次业汰,超人鴨就使用本地編寫的json數(shù)據(jù),通過axios發(fā)送ajax請求菩颖,來獲取本地數(shù)據(jù)样漆,然后實現(xiàn)搜索功能,并在這個功能上加以拓展晦闰,實現(xiàn)搜索關(guān)鍵字的高亮放祟。
我們一步一步來鳍怨,首先使用axios
axios是現(xiàn)在vue官方推薦使用一個插件,用來發(fā)送http請求跪妥,如果只是簡單的調(diào)用接口鞋喇,那用法也非常簡單。

//通過npm安裝
npm install axios --save
//引入和掛載,在main.js里面
import axios from 'axios'
Vue.prototype.$axios = axios
//在組件中使用
this.$axios.get(url)
 .then((res) => {
   //成功的回調(diào)
 })

上面是axios的基礎(chǔ)用法骗奖,下面也會看到具體的使用

然后編寫我們的json數(shù)據(jù)
因為是本地的模擬數(shù)據(jù)确徙,所以推薦放在項目中static文件夾里面,下面是超人鴨本次編寫的json文件device.json,大家可以不用關(guān)心數(shù)據(jù)本身有什么含義执桌,看一下json數(shù)據(jù)的編寫格式

{
  "data":{
    "deviceList":[
       {"name":"設(shè)備一","date":"2019-03-24","adress":"深圳","type":"電動"},
       {"name":"設(shè)備二","date":"2019-03-24","adress":"上海","type":"汽油"},
       {"name":"設(shè)備三","date":"2019-03-24","adress":"北京","type":"電動"},
       {"name":"設(shè)備四","date":"2019-03-24","adress":"廣州","type":"汽油"}
       ......
    ]
  }
}

這就寫好一個json文件鄙皇,我們來看看在組件中如何通過axios調(diào)用到這個json文件:
//在組件的created鉤子函數(shù)中調(diào)用
created(){
//寫上文件的路徑
this.$axios.get('../../../static/mock/device.json')
.then((res) => {
console.log(res)
})
}
看看打印出來的結(jié)果:


image.png

可以看到已經(jīng)調(diào)用成功,我們要的數(shù)據(jù)在res.data.data.deviceList里面
回到這次的主題仰挣,實現(xiàn)搜索且關(guān)鍵字高亮
先看看最終效果:


image.png

有一個輸入框伴逸,當(dāng)我輸入 '設(shè)備' 兩個字后:


image.png

當(dāng)我輸入 '廣州' 兩個字后:
image.png

可以看到完成了超人鴨所描述的效果,下面我將通過代碼和代碼中的注釋來介紹功能的實現(xiàn),樣式部分就不寫出來了膘壶,沒什么影響错蝴。
現(xiàn)在簡單的html結(jié)構(gòu)和data里面的變量:
<template>
  <div class="bright-index">
    <div class="search-box">
      <input type="text" v-model="keyword" class="input" placeholder="請輸入搜索內(nèi)容, 提示:搜索設(shè)備">
      <button class="btn" @click="search">搜索</button>
    </div>
    <div class="content-box">
      <div class="content-card" v-for="(item ,index) in resultList" :key="index">
        設(shè)備名稱:<span v-html="item.name"></span>
        <span>日期:<span v-html="item.date"></span></span>
        <span>地址:<span v-html="item.adress"></span></span>
        <span>類型:<span v-html="item.type"></span></span>
      </div>
      <h2 v-if="isShowTip">沒有搜索到匹配結(jié)果</h2>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      keyword: '',   //與輸入框綁定的變量
      deviceList: [],  // 調(diào)用json文件獲取的全部數(shù)據(jù)
      resultList: [],   //真正展示的數(shù)據(jù),也就是篩選后的數(shù)據(jù)
      isShowTip: false  //當(dāng)搜索不到數(shù)據(jù)時為true
    }
  }
</script>

我們先實現(xiàn)搜索功能颓芭,高亮下一步再實現(xiàn)

//首先得到數(shù)據(jù)顷锰,在created里面使用axios
created() {
  this.$axios.get('../../../static/mock/device.json')
    .then((res) => {
      //將json文件中的數(shù)據(jù)賦值給data里面的deviceList
      this.deviceList = res.data.data.deviceList
    })
}
//然后我們給按鈕綁定了一個點擊事件search,在這個事件中做處理
methods:{
  search() {
    if (this.keyword == '') {   //如果沒有輸入內(nèi)容亡问,不讓往下執(zhí)行
      return
    }
    this.resultList = []   //每次搜索對結(jié)果數(shù)組做初始化
    this.deviceList.forEach((item) => {  //把初始數(shù)據(jù)進行遍歷
    /**
      下面是把初始數(shù)據(jù)中的每一條數(shù)據(jù)的四個字段分別去和輸入的內(nèi)容進行匹配官紫,
      如果某一字段中包含輸入的內(nèi)容,就往resultList中加
    **/
      if (item.name.indexOf(this.keyword) > -1 ||
        item.date.indexOf(this.keyword) > -1 ||
        item.adress.indexOf(this.keyword) > -1 ||
        item.type.indexOf(this.keyword) > -1) {
        this.resultList.push(item)
      }
    })
    if (this.resultList.length == 0) {   //如果沒有匹配結(jié)果州藕,就顯示提示信息
      this.isShowTip = true
    }
  }
}

因為是本地的模擬數(shù)據(jù)束世,所以采用最原始的方式,去遍歷判斷篩選床玻。

這樣就完成了搜索功能毁涉,接下來超人鴨來實現(xiàn)關(guān)鍵字高亮
注意到上面我放的html結(jié)構(gòu)嗎,里面展示數(shù)據(jù)用的不是平常的插值表達式锈死,也就是兩個大括號贫堰,而是用v-html,可以用來展示html代碼的而不是簡單的字符串馅精。
說到這严嗜,那實現(xiàn)高亮的思路就是將關(guān)鍵字也就是你輸入的內(nèi)容,在每一條數(shù)據(jù)中替換成html字符串
接上上面的代碼洲敢,還是在search方法里面

//將得到的每一條數(shù)據(jù)中的每一個字段進行處理,brightKeyword就是變高亮的方法
this.resultList.map((item) => {  //遍歷
  item.name = this.brightKeyword(item.name)
  item.date = this.brightKeyword(item.date)
  item.adress = this.brightKeyword(item.adress)
  item.type = this.brightKeyword(item.type)
}) //到這里search方法結(jié)束
---------------------------------
brightKeyword(val) {
  let keyword = this.keyword   //獲取輸入框輸入的內(nèi)容
  if (val.indexOf(keyword) !== -1) {  //判斷這個字段中是否包含keyword
    //如果包含的話漫玄,就把這個字段中的那一部分進行替換成html字符
    return val.replace(keyword, `<font color='#42cccc'>${keyword}</font>`)
  } else {
    return val
  }
}

將每一個字段都進行一次處理,來實現(xiàn)關(guān)鍵字高亮的效果

但是,如果按照超人鴨上面的代碼去實現(xiàn)的話睦优,會出現(xiàn)一個問題渗常,也是本地用模擬數(shù)據(jù)開發(fā)才會出現(xiàn)的問題,下面給大家模擬一下:
當(dāng)我輸入 '設(shè)備' 兩個字:


image.png

看起來沒問題汗盘,當(dāng)我再輸入 '深圳' 兩個字時:


image.png

發(fā)現(xiàn)沒有皱碘,數(shù)據(jù)是對的,但高亮是錯的隐孽,上一次搜索的關(guān)鍵字高亮在第二次搜索還是高亮的癌椿。
為什么會出現(xiàn)這種情況呢?
這里要說到一個概念菱阵,對象的淺拷貝踢俄,看看這種情況:

    let obj1 = { foo: 1 }
    let obj2 = obj1
    obj2.foo = 3
    console.log(obj1.foo)  //打印出來之后是3

這就是對象的淺拷貝,數(shù)組也會出現(xiàn)這種情況晴及。這是因為js中對象和數(shù)組是引用數(shù)據(jù)類型都办,存儲方式和基本數(shù)據(jù)類型(字符串、數(shù)字等)不一樣虑稼,直接賦值給另一個對象的話琳钉,另一個對象改變屬性自己的屬性也會跟著改變。(如果這塊不明白的話蛛倦,可以先知道這么一回事就好歌懒,等超人鴨再出一篇詳細介紹js存儲對象的方式)

回到我們的例子中,為什么例子會出現(xiàn)對象的淺拷貝呢?
我們的原始數(shù)據(jù),也就是從json文件中的獲取到的數(shù)據(jù)溯壶,賦值給我們組件中data的deviceList歼培,而這個過程,只執(zhí)行了一次茸塞,也就是說deviceList這個原始數(shù)組在之后的執(zhí)行中都沒有重新去獲取。然后在點擊搜索后遍歷匹配的過程中查剖,resultList去push deviceList中的某一項钾虐,而deviceList的每一項都是一個對象,而這些過程都是對象的淺拷貝笋庄,所以在之后的高亮處理中效扫,改變了某一個對象里面的屬性,deviceList也會跟著受影響直砂。
如何解決
如果在真正與后端配合開發(fā)的過程中菌仁,每次點擊搜索,都會再去調(diào)用一次接口静暂,重新獲取數(shù)據(jù)济丘。那我們也模擬這個過程,每次點擊搜索時都重新去獲取數(shù)據(jù),保證每次原始數(shù)據(jù)deviceList都是新的摹迷。這個實現(xiàn)也非常簡單疟赊,不要在created中用axios調(diào)用json文件,而是search方法一開始調(diào)用:

search() {
    this.$axios.get('../../../static/mock/device.json')
    .then((res) => {
      //將json文件中的數(shù)據(jù)賦值給data里面的deviceList
      this.deviceList = res.data.data.deviceList
    })
    ....
}

看起來似乎沒問題了峡碉?
熟悉axios插件的伙伴都知道近哟,axios是用promise封裝的,而promise是用來處理異步操作的鲫寄。異步就是不能確定它什么時候能執(zhí)行完的操作吉执,所以這樣寫的話,不能確定它是什么時候執(zhí)行完成地来,所以不能確定deviceList什么時候才能獲取到數(shù)據(jù)戳玫,當(dāng)方法繼續(xù)往后走而deviceList 沒有數(shù)據(jù)就會報錯。

正確的寫法應(yīng)該在獲取數(shù)據(jù)成功后靠抑,才去執(zhí)行下面的操作量九,也就是獲取數(shù)據(jù)成功后的回調(diào)函數(shù)里,promise支持這樣的寫法:

search() {
    this.$axios.get('../../../static/mock/device.json')
    .then((res) => {
      //將json文件中的數(shù)據(jù)賦值給data里面的deviceList
      this.deviceList = res.data.data.deviceList
    }).then(() => {
        //把上面search寫的代碼放在這里面
    })
}

到這里就完成了颂碧,確保了每次都是獲取新的數(shù)據(jù)而且在獲取數(shù)據(jù)成功后才執(zhí)行下一步操作荠列。實現(xiàn)了用模擬數(shù)據(jù)進行搜索,關(guān)鍵字高亮的效果载城。下面超人鴨放上完整代碼肌似,不足的地方請大家多多指教。

<template>
  <div class="bright-index">
    <div class="search-box">
      <input type="text" v-model="keyword" class="input" placeholder="請輸入搜索內(nèi)容, 提示:搜索設(shè)備">
      <button class="btn" @click="search">搜索</button>
    </div>
    <div class="content-box">
      <div class="content-card" v-for="(item ,index) in resultList" :key="index">
        設(shè)備名稱:<span v-html="item.name" style="color:#000;"></span>
        <span>日期:<span v-html="item.date"></span></span>
        <span>地址:<span v-html="item.adress"></span></span>
        <span>類型:<span v-html="item.type"></span></span>
      </div>
      <h2 v-if="isShowTip">沒有搜索到匹配結(jié)果</h2>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      keyword: '',
      deviceList: [],
      resultList: [],
      isShowTip: false
    }
  },
  methods: {
    search () {
      this.isShowTip = false
      if (this.keyword == '') {
        this.$message.warning('請輸入搜索內(nèi)容')
        return
      }
      this.$axios.get('../../../static/mock/device.json')
        .then((res) => {
          this.deviceList = res.data.data.deviceList
        }).then(() => {
          this.resultList = []
          this.deviceList.forEach((item) => {
            if (item.name.indexOf(this.keyword) > -1 ||
              item.date.indexOf(this.keyword) > -1 ||
              item.adress.indexOf(this.keyword) > -1 ||
              item.type.indexOf(this.keyword) > -1) {
              this.resultList.push(item)
            }
          })
          if (this.resultList.length == 0) {
            this.isShowTip = true
          }
          this.resultList.map((item) => {
            item.name = this.brightKeyword(item.name)
            item.date = this.brightKeyword(item.date)
            item.adress = this.brightKeyword(item.adress)
            item.type = this.brightKeyword(item.type)
          })
        })
    },
    brightKeyword (val) {
      let keyword = this.keyword
      if (val.indexOf(keyword) !== -1) {
        return val.replace(keyword, `<font color='#42cccc'>${keyword}</font>`)
      } else {
        return val
      }
    }
  }
}
</script>

項目中的html代碼

<div class="search">
    <div class="search_box">
      <el-input
        type="text"
        class="input-search"
        placeholder="請輸入搜索內(nèi)容"
        v-model.trim="searchKey "
        @input="searchList"
        ref="searchInput"
        autocomplete="off"
        autocapitalize="off"
        autocorrect="off"
        autofocus
      ></el-input>
      <el-button type="primary" class="search_btn" @click="searchList">搜索</el-button>
    </div>
    <div>
        <ul>
          <li v-for="item in resultList"
               :key="item.announceId"
          >
            <span v-html="item.contentKey1" @click="searchDetail(item.announceId)"></span>
            <br/>
            <span v-html="item.contentKey2" @click="searchDetail(item.announceId)"></span>
            <br/>
            <span v-html="item.contentKey3" @click="searchDetail(item.announceId)"></span>
            <br/>
            <span v-html="item.contentKey4" @click="searchDetail(item.announceId)"></span>
            <br/>
            <span v-html="item.contentKey5" @click="searchDetail(item.announceId)"></span>
            <br/>
            <span v-html="item.announceName" @click="searchDetail(item.announceId)"></span>
          </li>
        </ul>
       <h2 v-if="isShowTip">沒有搜索到匹配結(jié)果</h2>
    </div>
    <div class="page-footer">
      <el-pagination
        v-if="total>0"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNo"
        :page-sizes="[10,15, 30, 45, 60]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
      </el-pagination>
    </div>
  </div>

項目中的代碼如下js

export default {
  data () {
    return {
      searchKey: '',
      pageNo: 1,
      pageSize: 10,
      total: 0,
      deviceList: [], // 調(diào)用接口獲取的全部數(shù)據(jù)
      resultList: [], // 真正展示的數(shù)據(jù)诉瓦,也就是篩選后的數(shù)據(jù)
      isShowTip: false // 當(dāng)搜索不到數(shù)據(jù)時為true
    }
  },
  created () {
    if (this.$route.query.ytoSearchKey) {
      this.searchKey = this.$route.query.ytoSearchKey
    }
    this.searchList()
  },
  methods: {
    searchList () {
      this.isShowTip = false
      if (this.searchKey === '') {
        return
      }
      if (this.searchKey && this.searchKey.length > 0) {
        return new Promise((resolve, reject) => {
          const params = {
            searchKey: this.searchKey,
            pageNo: this.pageNo,
            limit: this.pageSize
          }
          announceList(params).then(res => {
            this.pageNo = res.data.data.pageNo
            this.deviceList = res.data.data.resultData
            this.total = res.data.data.total
          }).then(() => {
            this.resultList = [] // 每次搜索對結(jié)果數(shù)組做初始化
            this.deviceList.forEach(item => { // 把初始數(shù)據(jù)進行遍歷
              // 下面是把初始數(shù)據(jù)中的每一條數(shù)據(jù)的四個字段分別去和輸入的內(nèi)容進行匹配川队,如果某一字段中包含輸入的內(nèi)容,就往resultList中加
              if (item.announceName.indexOf(this.searchKey) > -1 ||
                item.contentKey1.indexOf(this.searchKey) > -1 ||
                item.contentKey2.indexOf(this.searchKey) > -1 ||
                item.contentKey3.indexOf(this.searchKey) > -1 ||
                item.contentKey4.indexOf(this.searchKey) > -1 ||
                item.contentKey5.indexOf(this.searchKey) > -1) {
                this.resultList.push(item)
              }
            })
            if (this.resultList.length === 0) { // 如果沒有匹配結(jié)果睬澡,就顯示提示信息
              this.isShowTip = true
            }

            // 將得到的每一條數(shù)據(jù)中的每一個字段進行處理,brightKeyword就是變高亮的方法
            this.resultList.map(item => {
              item.announceName = this.brightKeyword(item.announceName)
              item.contentKey1 = this.brightKeyword(item.contentKey1)
              item.contentKey2 = this.brightKeyword(item.contentKey2)
              item.contentKey3 = this.brightKeyword(item.contentKey3)
              item.contentKey4 = this.brightKeyword(item.contentKey4)
              item.contentKey5 = this.brightKeyword(item.contentKey5)
            })
          }).catch((err) => {
            reject(err)
          })
        })
      }
    },
    brightKeyword (val) {
      const keyword = this.searchKey // 獲取輸入框輸入的內(nèi)容
      if (val.indexOf(keyword) !== -1) { // 判斷這個字段中是否包含keyword
        return val.replace(keyword, `<font color="red">${keyword}</font>`)
      } else {
        return val
      }
    },
    // 跳轉(zhuǎn)到詳情頁面
    searchDetail (announceId) {
      this.$router.push({
        path: 'searchDetail',
        query: {
          announceId: announceId
        }
      })
    },
    // 改變每頁顯示數(shù)量
    handleSizeChange (val) {
      this.pageSize = val
      this.searchList()
    },
    // 改變當(dāng)前頁
    handleCurrentChange (val) {
      this.pageNo = val
      this.searchList()
    }
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末固额,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子煞聪,更是在濱河造成了極大的恐慌斗躏,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昔脯,死亡現(xiàn)場離奇詭異啄糙,居然都是意外死亡,警方通過查閱死者的電腦和手機云稚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門隧饼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人静陈,你說我怎么就攤上這事燕雁。” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵贵白,是天一觀的道長率拒。 經(jīng)常有香客問我,道長禁荒,這世上最難降的妖魔是什么猬膨? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮呛伴,結(jié)果婚禮上勃痴,老公的妹妹穿的比我還像新娘。我一直安慰自己热康,他們只是感情好沛申,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著姐军,像睡著了一般铁材。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奕锌,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天著觉,我揣著相機與錄音,去河邊找鬼惊暴。 笑死饼丘,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辽话。 我是一名探鬼主播肄鸽,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼油啤!你這毒婦竟也來了典徘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤益咬,失蹤者是張志新(化名)和其女友劉穎烂斋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體础废,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年罕模,在試婚紗的時候發(fā)現(xiàn)自己被綠了评腺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡淑掌,死狀恐怖蒿讥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤芋绸,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布媒殉,位于F島的核電站,受9級特大地震影響摔敛,放射性物質(zhì)發(fā)生泄漏廷蓉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一马昙、第九天 我趴在偏房一處隱蔽的房頂上張望桃犬。 院中可真熱鬧,春花似錦行楞、人聲如沸攒暇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽形用。三九已至,卻和暖如春证杭,著一層夾襖步出監(jiān)牢的瞬間田度,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工躯砰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留每币,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓琢歇,卻偏偏與公主長得像兰怠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子李茫,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容