本文是一篇筆者關(guān)于replace API的筆記,希望看完在項(xiàng)目中有所思考和幫助。
正文開始...
在開始正文之前,主要是利用字符串replace這個API,你將要了解以下幾個知識。
1、字符串replace替換。
2、如何擴(kuò)展elementUI組件源碼支持下拉框關(guān)鍵字搜索高亮。
3、正則匹配對應(yīng)結(jié)果,replace高階用法。
了解需求
比如,現(xiàn)在一個常用的下拉框,我需要搜索關(guān)鍵詞模糊匹配,我們看下代碼。
<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>
當(dāng)我在el-select組件上添加filterable屬性后,就可以關(guān)鍵詞過濾了,但是只是過濾了,但是我想關(guān)鍵詞高亮。
你會發(fā)現(xiàn)el-select顯示的label并沒有提供插槽或者其他方式去自定義顯示label,源碼里是直接顯示的。
<!--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>
我們嘗試修改擴(kuò)展增強(qiáng)下這個option,于是想辦法去修改currentLabel,但是你會發(fā)現(xiàn)你想讓computed的currentLabel返回一個jsx貌似不太可能,因?yàn)殇秩境鰜淼臅?biāo)簽,所以只能考慮重寫render方法。
重寫Option源碼
于是我們重寫render,新建一個extendElement.js。
// src/extendElement.js
// eslint-disable-next-line import/prefer-default-export
export const extendElemenUI = (ElementUI) => {
const { Option } = ElementUI;
// 重寫elementUI下拉框的Option,讓其支持模糊搜索關(guān)鍵字高亮
// 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渲染標(biāo)簽的關(guān)鍵在于domPropsInnerHTML這個接口,如果在模版中我們就是使用v-html去代替。
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;
// 這里進(jìn)行了擴(kuò)展,主要是想擴(kuò)展ElementUI不支持的功能,一定是在組件未注冊前進(jìn)行攔截,重寫部分組件
extendElemenUI(ElementUI);
Vue.use(ElementUI);
/* eslint-disable no-new */
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app');
我們發(fā)現(xiàn)在高亮關(guān)鍵字有用到這個hightText方法,主要支持關(guān)鍵詞全匹配與部分匹配,默認(rèn)全匹配。
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}`;
};
在上面的一塊代碼中我們發(fā)現(xiàn),非全匹配,我們就用到了replace這個方法,主要是替換匹配到的關(guān)鍵字,但是這個replace我們結(jié)合match,我們發(fā)現(xiàn)無法重復(fù)匹配。
假設(shè)aabbccaa需要高亮aa,如果用不借助數(shù)組或者正則方式處理,我們使用的是replace字符串匹配的方式,那么一旦匹配到就結(jié)束,所以借助了數(shù)組的方式做了一點(diǎn)取巧實(shí)現(xiàn)了全檢索高亮。
看下最終的結(jié)果:

replace
replace高亮關(guān)鍵詞基本就已經(jīng)完成這個需求功能,我們重新看下官方MDNreplace[1]的解釋。
replace()方法返回一個由替換值(replacement)替換部分或所有的模式(pattern)匹配項(xiàng)后的新字符串。模式可以是一個字符串或者一個[正則表達(dá)式](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/RegExp "正則表達(dá)式"),替換值可以是一個字符串或者一個每次匹配都要調(diào)用的回調(diào)函數(shù)。**如果pattern是字符串,則僅替換第一個匹配項(xiàng)。**
所以我們從這段解釋中可以發(fā)現(xiàn),當(dāng)我們使用replace替換,如果pattern是字符串,則僅替換第一個匹配項(xiàng)。
var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace('aa', 111);
console.log(ret) // 111bbbccaa
但是我們發(fā)現(xiàn)匹配模式還可以是正則。
所以如果想全匹配,那么可以用正則來做。
var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace(/aa/g, 111);
console.log(ret) // 111bbbcc111
所以我們也可以將我們上面的hightText方法改成下面這樣。
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的第一個參數(shù)是字符串或者正則,第二個參數(shù)是字符串或者一個函數(shù)。
var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace('aa', 111);
console.log(ret) // 111bbbccaa
var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace(/aa/ig, 111);
console.log(ret) // 111bbbcc111
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]
});
我們看下第二次函數(shù),對應(yīng)的mactch與string是原數(shù)據(jù),$1...$3是對應(yīng)正則匹配的,如果我想把中間對應(yīng)的數(shù)字換成其他的呢?
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技術(shù)學(xué)苑')+$3.replace($3, '-Maic')
});
console.log(newString) //公眾號:Web技術(shù)學(xué)苑-Maic
關(guān)于function的參數(shù)可以參考下面這個表
變量名 | 代表的值 |
match | 匹配的子串。(對應(yīng)于上述的 $&。) |
$1,$2, ... | 假如 replace() 方法的第一個參數(shù)是一個RegExp?[2] 對象,則代表第 n 個括號匹配的字符串。(對應(yīng)于上述的 1,2 等。)例如,如果是用 ?/([^\d]*)(\d*)([^\w]*)/? 這個來匹配,$1? 就是匹配的 ([^\d]*)?,$2? 就是匹配的 (\d*),依次類推... |
offset | 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd'?,匹配到的子字符串是 'bc',那么這個參數(shù)將會是 1) |
string | 被匹配的原字符串。 |
在業(yè)務(wù)中你也會經(jīng)??吹竭@樣的代碼:
var sourceStr = 'aabbbccaa';
const ret = sourceStr.replace(/aa/ig, 111).replace('bbb', 222);
console.log(ret) // 111222cc111'
replace調(diào)用返回的是一個新字符串,所以可以繼續(xù)調(diào)用replace方法,因?yàn)閞eplace是掛載在String.prototype上的方法,所以所有字符串可以鏈?zhǔn)秸{(diào)用。
總結(jié)
- 以一個實(shí)際例子,通過擴(kuò)展el-select的Option組件實(shí)現(xiàn)高亮模糊關(guān)鍵字匹配與全匹配,不過這種方式有缺陷,無法根據(jù)當(dāng)前組件有條件的選擇是否高亮匹配,因?yàn)槲覀兪窃谧郧爸貙懥藃ender,這樣會導(dǎo)致所有下拉組件都會高亮模糊關(guān)鍵字。
- 講解replace這個關(guān)鍵字函數(shù),如果字符串替換就要知道這個API。
- replace支持正則與字符串匹配,如果是字符串,則只會匹配首次,一旦匹配就成功替換,而正則可以做到全局匹配替換。
- 關(guān)于replace第二個參數(shù)是回調(diào)函數(shù)的幾個參數(shù)的講解,當(dāng)是回調(diào)函數(shù)時(shí),第一個是match、string是原字符串,其余的$1,...$n是對應(yīng)正則匹配的內(nèi)容。
- 本文示例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