前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >全文检索与高亮关键词匹配,用replace就够了

全文检索与高亮关键词匹配,用replace就够了

作者头像
Maic
发布2022-12-21 19:11:48
1.2K0
发布2022-12-21 19:11:48
举报
文章被收录于专栏:Web技术学苑Web技术学苑

全文关键词检索高亮,这个在业务中常有的功能,比如浏览器默认就有个功能,关键词搜索就会匹配你检索的文字,并且会给你高亮,这是怎么实现的呢?

本文是一篇笔者关于replace API的笔记,希望看完在项目中有所思考和帮助。

正文开始...

在开始正文之前,主要是利用字符串replace这个API,你将要了解以下几个知识

1、字符串replace替换

2、如何扩展elementUI组件源码支持下拉框关键字搜索高亮

3、正则匹配对应结果,replace高阶用法

了解需求

比如,现在一个常用的下拉框,我需要搜索关键词模糊匹配,我们看下代码

代码语言:javascript
复制

<el-form-item label="爱好">
    <el-select
      v-model="condition.fv"
      clearable
      filterable
      placeholder="请选择爱好"
    >
      <el-option
        v-for="(item, index) in favData"
        :key="index"
        :label="item"
        :value="item"
      >
      </el-option>
    </el-select>
</el-form-item>
<script>
export default {
  data() {
     return {
        favData: [
          '我喜欢篮球',
          '我喜欢乒乓球',
          '足球',
          '游泳',
          '跳水',
          'aabbccaa',
          'hello aa, test',
        ],
    }
  }
}
</script>

当我在el-select组件上添加filterable属性后,就可以关键词过滤了,但是只是过滤了,但是我想关键词高亮

你会发现el-select显示的label并没有提供插槽或者其他方式去自定义显示label,源码里是直接显示的

代码语言:javascript
复制

<!--https://github.com/ElemeFE/element/blob/dev/packages/select/src/option.vue-->
<template>
  <li
    @mouseenter="hoverItem"
    @click.stop="selectOptionClick"
    class="el-select-dropdown__item"
    v-show="visible"
    :class="{
      'selected': itemSelected,
      'is-disabled': disabled || groupDisabled || limitReached,
      'hover': hover
    }">
    <slot>
      <span>{{ currentLabel }}</span>
    </slot>
  </li>
</template>
<script>
export default {
    name: 'ElOption',
   computed: {
      isObject() {
        return Object.prototype.toString.call(this.value).toLowerCase() === '[object object]';
      },
      currentLabel() {
        return this.label || (this.isObject ? '' : this.value);
      },
  }
</script>

我们尝试修改扩展增强下这个option,于是想办法去修改currentLabel,但是你会发现你想让computedcurrentLabel返回一个jsx貌似不太可能,因为渲染出来的会带标签,所以只能考虑重写render方法

重写Option源码

于是我们重写render,新建一个extendElement.js

代码语言:javascript
复制

// src/extendElement.js
// eslint-disable-next-line import/prefer-default-export
export const extendElemenUI = (ElementUI) => {
  const { Option } = ElementUI;
  // 重写elementUI下拉框的Option,让其支持模糊搜索关键字高亮
  // eslint-disable-next-line no-unused-vars
  Option.render = function (h) {
    const { visible, itemSelected, disabled, groupDisabled, limitReached, selectOptionClick, hoverItem, currentLabel, hover, select: { query } } = this;
    const setSlectClass = () => {
      let str = 'el-select-dropdown__item';
      if (itemSelected) {
        str += ' selected';
      }
      if (disabled || groupDisabled || limitReached) {
        str += ' is-disabled';
      }
      if (hover) {
        str += ' hover';
      }
      return str;
    };
    return (visible ? <li
      on-mouseenter={hoverItem}
      on-click={selectOptionClick}
      class={setSlectClass()}
    >
      <slot>
        <span domPropsInnerHTML={hightText(currentLabel, query, 'all')}></span>
      </slot>
    </li > : null);
  };
};

我们注意到我重写了Option这个组件,我们在install安装前就拦截这个组件,然后重写了Option,主要是在ElementUI注册前完成,jsx渲染标签的关键在于domPropsInnerHTML这个接口,如果在模版中我们就是使用v-html去代替

代码语言:javascript
复制

import Vue from 'vue';
import 'element-ui/lib/theme-chalk/index.css';
import ElementUI from 'element-ui';
import { installCustComponent } from '@/components';
import { extendElemenUI } from './extendElement';
import App from './App';
import router from './router';
import store from './store';
installCustComponent();
Vue.config.productionTip = false;
// 这里进行了扩展,主要是想扩展ElementUI不支持的功能,一定是在组件未注册前进行拦截,重写部分组件
extendElemenUI(ElementUI);
Vue.use(ElementUI);
/* eslint-disable no-new */
new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app');

我们发现在高亮关键字有用到这个hightText方法,主要支持关键词全匹配与部分匹配,默认全匹配

代码语言:javascript
复制

const hightText = (sourceStr, curentVal, reg = 'all') => {
  if (curentVal === '') {
    return sourceStr;
  }
  const ret = sourceStr.match(curentVal);
  const hightStr = Array.isArray(ret) ? ret[0] : '';
  if (reg) {
    // 全匹配
    return sourceStr.split(hightStr).reduce((prev, cur) => {
      if (cur === '') {
        prev.push(`<span class="hight" style="color: red;font-weight:bold">${hightStr}</span>`);
      }
      if (cur) {
        prev.push(cur);
      }
      return prev;
    }, []).join('');
  }
  return hightStr
    ? sourceStr.replace(
      hightStr,
      `<span class="hight" style="color: red;font-weight:bold">${hightStr}</span>`,
    )
    : `${sourceStr}`;
};

在上面的一块代码中我们发现,非全匹配,我们就用到了replace这个方法,主要是替换匹配到的关键字,但是这个replace我们结合match,我们发现无法重复匹配

假设aabbccaa需要高亮aa,如果用不借助数组或者正则方式处理,我们使用的是replace字符串匹配的方式,那么一旦匹配到就结束,所以借助了数组的方式做了一点取巧实现了全检索高亮

看下最终的结果:

replace

replace高亮关键词基本就已经完成这个需求功能,我们重新看下官方MDNreplace[1]的解释

replace()方法返回一个由替换值(replacement)替换部分或所有的模式(pattern)匹配项后的新字符串。模式可以是一个字符串或者一个[正则表达式](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp "正则表达式"),替换值可以是一个字符串或者一个每次匹配都要调用的回调函数。**如果pattern是字符串,则仅替换第一个匹配项。**

所以我们从这段解释中可以发现,当我们使用replace替换,如果pattern是字符串,则仅替换第一个匹配项

代码语言:javascript
复制

var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace('aa', 111);
console.log(ret) // 111bbbccaa

但是我们发现匹配模式还可以是正则

所以如果想全匹配,那么可以用正则来做

代码语言:javascript
复制

var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace(/aa/g, 111);
console.log(ret) // 111bbbcc111

所以我们也可以将我们上面的hightText方法改成下面这样

代码语言:javascript
复制

const hightText = (sourceStr, curentVal, reg = 'all') => {
  if (curentVal === '') {
    return sourceStr;
  }
  const ret = sourceStr.match(curentVal);
  const hightStr = Array.isArray(ret) ? ret[0] : '';
  const hightDom = text => `<span class="hight" style='color: red;font-weight:bold'>${text}</span>`;
  if (hightStr) {
    if (reg) {
      // 全匹配
      return sourceStr.replace(new RegExp(`${hightStr}`, 'ig'), hightDom(hightStr));
    }
    return sourceStr.replace(
      hightStr, hightDom(hightStr),
    );
  }
  return sourceStr;
};

官方的replace语法是这样的str.replace(regexp|substr, newSubStr|function) 也就是说replace的第一个参数是字符串或者正则,第二个参数是字符串或者一个函数

  • 字符串
代码语言:javascript
复制

var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace('aa', 111);
console.log(ret) // 111bbbccaa
  • 正则
代码语言:javascript
复制

var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace(/aa/ig, 111);
console.log(ret) // 111bbbcc111
  • 函数
代码语言:javascript
复制

const str = 'abc12345#$*%'
var newString = str.replace(/([^\d]*)(\d*)([^\w]*)/, function(match, $1, $2, $3, offset, string) {
    console.log(match, offset, string)
    return [$1, $2, $3]
});

我们看下第二次函数,对应的mactch与string是原数据,1...3是对应正则匹配的,如果我想把中间对应的数字换成其他的呢?

代码语言:javascript
复制

const str = 'abc12345#$*%'
var newString = str.replace(/([^\d]*)(\d*)([^\w]*)/, function(match, $1, $2, $3, offset, string) {
    return $1.replace($1, '公众号:') + $2.replace($2, 'Web技术学苑')+$3.replace($3, '-Maic')
});
console.log(newString) //公众号:Web技术学苑-Maic

关于function的参数可以参考下面这个表

代码语言:javascript
复制
匹配的子串。(对应于上述的 $&。)

在业务中你也会经常看到这样的代码

代码语言:javascript
复制

var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace(/aa/ig, 111).replace('bbb', 222);
console.log(ret) // 111222cc111'

replace调用返回的是一个新字符串,所以可以继续调用replace方法,因为replace是挂载在String.prototype上的方法,所以所有字符串可以链式调用

总结

  • 以一个实际例子,通过扩展el-selectOption组件实现高亮模糊关键字匹配与全匹配,不过这种方式有缺陷,无法根据当前组件有条件的选择是否高亮匹配,因为我们是在注册前重写了render,这样会导致所有下拉组件都会高亮模糊关键字
  • 讲解replace这个关键字函数,如果字符串替换就要知道这个API
  • replace支持正则与字符串匹配,如果是字符串,则只会匹配首次,一旦匹配就成功替换,而正则可以做到全局匹配替换
  • 关于replace第二个参数是回调函数的几个参数的讲解,当是回调函数时,第一个是match、string是原字符串,其余的1,...n是对应正则匹配的内容
  • 本文示例code example[3]

参考资料

[1]replace: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace

[2]RegExp: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp

[3]code example: https://github.com/maicFir/lessonNote/tree/master/vue/05-keep-alive

最后,看完觉得有收获的,点个赞,在看,转发,收藏等于学会,欢迎关注Web技术学苑,好好学习,天天向上!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-10-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Web技术学苑 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 了解需求
  • 重写Option源码
  • replace
  • 总结
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档