vue3 自定义指令 从 3 毫秒优化到 0.5 毫秒
暴力实现 1.5~1.8 毫秒
js
function abbreviateFileName(el, fileName, fileType) {
// 如果fileType === 2 也就是文件夹时 不提取扩展名称
const extension =
fileType === 2 ? "" : fileName.slice(fileName.lastIndexOf(".")); // Get the file extension
// Get the file name without the extension
const baseName =
fileType === 2 ? fileName : fileName.slice(0, fileName.lastIndexOf("."));
let targetLength = baseName.length;
const caches = [];
// Iteratively try shorter versions of the file name until it fits
while (targetLength > 0 && el.scrollWidth > el.offsetWidth) {
const partLength = Math.max(1, Math.ceil(targetLength / 2)); // Ensure at least 1 character is used
const abbreviation =
baseName.slice(0, partLength) +
"..." +
baseName.slice(-partLength) +
extension;
// 防止重复计算 优化性能
if (!caches.includes(abbreviation)) {
el.innerText = abbreviation;
caches.push(abbreviation);
}
// Adjust the target length based on whether the abbreviated text fits
if (el.scrollWidth > el.offsetWidth) {
// If still not fitting, reduce the target length
targetLength -= 1;
} else {
// If it fits, try to find a balance (this part is omitted for simplicity but can be implemented if needed)
break;
}
}
caches.length = 0;
}
function applyAbbreviationIfNeeded(el, binding) {
// Return early if the text fits within the element without scrolling
if (el.scrollWidth <= el.offsetWidth) {
return;
}
// Ensure the element contains text in the form of a file name
const originalText = binding.value?.name.trim() || el.innerText.trim();
console.time("applyAbbreviationIfNeeded");
// Abbreviate the file name until it fits within the element
abbreviateFileName(el, originalText, binding.value?.fileType);
// Add a class for showing the tooltip
if (!el.classList.contains("show-my-tooltip")) {
el.classList.add("show-my-tooltip");
}
console.timeEnd("applyAbbreviationIfNeeded");
}
// 实现指令 v-adjust-text
export const adjustTextDirective = {
mounted: applyAbbreviationIfNeeded,
updated: applyAbbreviationIfNeeded,
};
二分查找 0.5~0.8 毫秒
当然,从性能的角度出发,在保持功能不变的基础上对您的代码进行进一步的优化。以下是一些优化策略,旨在减少不必要的计算和 DOM 操作,同时提高代码的效率和可读性:
- 二分查找法替代线性查找:利用二分查找法来快速定位到合适的缩写长度,以减少尝试次数,从而提高性能。
- 避免在每次循环中重复获取文件扩展名:如果文件类型不是文件夹,我们可以在循环外部一次性计算出文件名和扩展名,而不需要在每次迭代中重复这个过程。
- 只在需要时更新 DOM:计算出正确的缩写后再更新
innerText
,以减少对 DOM 的操作,因为 DOM 操作是相对昂贵的。 - 减少类的重复添加:检查元素是否已经包含了特定的类,如果没有,再添加。这样可以避免重复操作 DOM。
下面是根据这些建议修改后的代码:
js
function abbreviateFileName(el, fileName, fileType) {
// 根据文件类型决定是否提取扩展名
const hasExtension = fileType !== 2;
const extension = hasExtension
? fileName.slice(fileName.lastIndexOf("."))
: "";
const baseName = hasExtension
? fileName.slice(0, fileName.lastIndexOf("."))
: fileName;
let low = 1;
let high = baseName.length;
let bestFit = fileName;
// 使用二分查找法寻找最佳缩写
while (low <= high) {
const mid = Math.floor((low + high) / 2);
const testAbbrev = `${baseName.slice(0, mid)}...${baseName.slice(
-mid
)}${extension}`;
el.innerText = testAbbrev; // 临时设置以检测宽度
if (el.scrollWidth > el.offsetWidth) {
high = mid - 1;
} else {
bestFit = testAbbrev; // 找到一个合适的缩写
low = mid + 1;
}
}
el.innerText = bestFit; // 应用最合适的缩写,减少DOM操作
}
function applyAbbreviationIfNeeded(el, binding) {
// 如果文本已经适合显示,就不进行任何操作
if (el.scrollWidth <= el.offsetWidth) {
return;
}
console.time("applyAbbreviationIfNeeded");
const originalText = binding.value?.name.trim() || el.innerText.trim();
el.innerText = originalText; // 先恢复原始文本再检查,确保正确的比较
abbreviateFileName(el, originalText, binding.value?.fileType);
// 避免重复添加同一个类
if (!el.classList.contains("show-my-tooltip")) {
el.classList.add("show-my-tooltip");
}
console.timeEnd("applyAbbreviationIfNeeded");
}
// 实现指令 v-adjust-text
export const adjustTextDirective = {
mounted: applyAbbreviationIfNeeded,
updated: applyAbbreviationIfNeeded,
};
这个版本的代码通过引入更高效的算法(二分查找法)和减少不必要的 DOM 操作(例如,只在最终确定缩写之后更新innerText
),以及避免重复操作(如重复添加类),来提高整体性能。此外,通过缓存原始文本,确保即使在多次调整文本大小或内容后,也能保持文本的完整性。