1.动态计算dom宽高

实现
import { debounce } from 'lodash-es';
import type { Directive, App } from 'vue';
const map = new WeakMap();
const resize = (entries: ResizeObserverEntry[]) => {
entries.forEach((entry) => {
const handler = map.get(entry.target);
handler && handler(entry.contentRect);
});
};
const debounceReize = debounce(resize, 50);
const ob = new ResizeObserver(debounceReize);

const resizeDirective: Directive = {
mounted(el, binding) {
const { value } = binding;
map.set(el, value);
value && value(el.getBoundingClientRect());
ob.observe(el);
},
unmounted(el) {
ob.unobserve(el);
map.delete(el);
},
};

export function setupResizeDirective(app: App) {
app.directive('resize', resizeDirective);
}

export default resizeDirective;

2.文本高亮

import { DirectiveBinding } from 'vue';

/**
* 默认高亮样式
*/
const DEFAULT_HIGHLIGHT = 'text-[#F5313E]';

/**
* 清除em标签
* @param content 文本
*/
export const clearEm = (content = '') => {
return content && typeof content === 'string'
? content.replace(/<em>(.*?)<\/em>/g, '$1')
: content;
};

/**
* 清除高亮样式
* @param content 文本
* @param highlightClass 高亮样式
*/
export const clearHighlight = (content = '', highlightClass = DEFAULT_HIGHLIGHT) => {
return content && typeof content === 'string'
? content.replace(new RegExp(`<span class="${highlightClass}">(.*?)<\/span>`, 'gi'), '$1')
: content;
};

/**
* 清除em标签与高亮样式
* @param content 文本
* @param highlightClass 高亮样式
*/
export const clearAllHighlight = (content = '', highlightClass = DEFAULT_HIGHLIGHT) => {
return content ? clearEm(clearHighlight(content, highlightClass)) : content;
};

/**
* 转义正则特殊字符
* @param content 文本
*/
export function escapeRegExp(content = '') {
return content && typeof content === 'string'
? content.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
: content;
}

/**
* 设置高亮
* @param content 文本
* @param keyword 关键词/关键词组
* @param highlightClass 高亮样式
*/
const setHighlight = (
content = '',
keyword: string | string[],
highlightClass = DEFAULT_HIGHLIGHT,
) => {
content = clearHighlight(content, highlightClass) || '';
// 匹配关键词
if (typeof keyword === 'string' && keyword) {
const regex = new RegExp(`(${escapeRegExp(keyword)})`, 'gi');
return content.replace(regex, `<span class="${highlightClass}">$1</span>`);
}
// 精确匹配
else if (Array.isArray(keyword) && keyword.includes(content)) {
return `<span class="${highlightClass}">${content}</span>`;
} else {
return content.replace(/<em>(.*?)<\/em>/g, `<span class="${highlightClass}">$1</span>`);
}
};

const handleHighlight = (el: HTMLElement, binding: DirectiveBinding) => {
/**
* @modifier v-highlight.clear 清除高亮
* @modifier v-highlight.keyword 关键词匹配高亮
*/
const { clear = false, keyword = false } = binding.modifiers;

const content = binding.value || el.textContent || '';
const highlightClass = binding.arg || DEFAULT_HIGHLIGHT;

// 清除所有高亮
if (clear) {
el.innerHTML = clearAllHighlight(content, highlightClass);
}
// 根据关键词进行高亮
else if (keyword) {
const { text = '', keyword: keywordText } = binding.value;
const replaceContent = setHighlight(clearEm(text || ''), keywordText, highlightClass);
el.innerHTML = replaceContent;
}
// 如果内容中包含<em>标签,进行转换高亮
else if (/<em>(.*?)<\/em>/g.test(content)) {
const replaceContent = setHighlight(content, '', highlightClass);
el.innerHTML = replaceContent;
} else {
el.innerHTML = content;
}
};

export const vHighlight = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
handleHighlight(el, binding);
},
updated(el: HTMLElement, binding: DirectiveBinding) {
handleHighlight(el, binding);
},
};