在前端页面开发中,我们经常会从接口、用户输入或者本地存储中获取到一批URL数据,这些数据中可能存在大量重复的实体,如果直接将所有URL都渲染成可点击的链接,会导致页面出现大量重复内容,既影响页面美观度,也会降低用户的使用体验。因此我们需要先对重复的URL实体进行去重,之后仅渲染一次对应的链接。

核心实现思路
整个功能的实现可以分为三个核心步骤,每个步骤的逻辑都相对独立,方便后续扩展和维护:
- 数据收集:先获取到所有需要处理的URL实体数据,这些数据可以是数组形式,也可以是其他可迭代的数据结构。
- 去重处理:使用合适的方法对收集到的URL进行去重,这里需要注意URL的格式可能存在差异,比如是否带协议头、是否有末尾斜杠等情况,需要根据实际业务场景做归一化处理。
- 链接渲染:将去重后的URL列表渲染成对应的可点击链接,同时可以添加对应的链接文本或者描述信息。
URL归一化处理
在实际业务中,同一个URL可能会有不同的书写形式,比如http://ipipp.com和https://ipipp.com/可能指向同一个地址,如果不做归一化处理,去重逻辑会失效。我们可以先对URL做统一的格式化处理,再执行去重操作:
// URL归一化函数,统一处理URL格式
function normalizeUrl(url) {
try {
// 补全协议头,默认使用https
let processedUrl = url;
if (!processedUrl.startsWith('http://') && !processedUrl.startsWith('https://')) {
processedUrl = 'https://' + processedUrl;
}
// 去除末尾的斜杠
processedUrl = processedUrl.replace(//+$/, '');
// 转为小写,避免大小写差异导致的去重失效
return processedUrl.toLowerCase();
} catch (e) {
// 如果URL格式不合法,直接返回原始值
return url;
}
}
基础去重渲染实现
如果获取到的URL数据是一个简单的字符串数组,我们可以使用Set数据结构快速实现去重,之后遍历去重后的数组渲染链接:
// 原始URL数组,包含重复实体
const rawUrlList = [
'http://ipipp.com',
'https://ipipp.com/',
'http://ipipp.com/article/1',
'http://ipipp.com',
'https://ipipp.com/article/2',
'https://ipipp.com/'
];
// 第一步:对所有URL做归一化处理
const normalizedUrlList = rawUrlList.map(url => normalizeUrl(url));
// 第二步:使用Set去重
const uniqueUrlSet = new Set(normalizedUrlList);
const uniqueUrlList = Array.from(uniqueUrlSet);
// 第三步:渲染链接到页面
function renderUniqueLinks(containerId) {
const container = document.getElementById(containerId);
if (!container) return;
// 清空容器原有内容
container.innerHTML = '';
// 遍历去重后的URL列表,生成链接元素
uniqueUrlList.forEach(url => {
const linkElement = document.createElement('a');
linkElement.href = url;
linkElement.textContent = url;
linkElement.target = '_blank';
container.appendChild(linkElement);
// 每个链接之间添加换行
container.appendChild(document.createElement('br'));
});
}
// 调用渲染函数,假设页面有id为linkContainer的元素
renderUniqueLinks('linkContainer');
复杂场景下的去重渲染
如果URL实体不是单纯的字符串,而是包含更多信息,比如链接描述、分类等属性,我们需要基于URL字段去重,同时保留对应的附加信息:
// 带有附加信息的URL实体数组
const urlEntityList = [
{ url: 'http://ipipp.com', desc: '首页', category: '导航' },
{ url: 'https://ipipp.com/', desc: '主页', category: '导航' },
{ url: 'http://ipipp.com/article/1', desc: '第一篇文章', category: '内容' },
{ url: 'http://ipipp.com', desc: '站点首页', category: '导航' }
];
// 基于URL去重,保留第一个出现的实体信息
function deduplicateUrlEntities(entityList) {
const urlMap = new Map();
entityList.forEach(entity => {
const normalizedUrl = normalizeUrl(entity.url);
// 如果Map中不存在该URL,才存入
if (!urlMap.has(normalizedUrl)) {
urlMap.set(normalizedUrl, entity);
}
});
return Array.from(urlMap.values());
}
// 去重后的实体列表
const uniqueEntities = deduplicateUrlEntities(urlEntityList);
// 渲染带有描述的链接
function renderEntityLinks(containerId, entityList) {
const container = document.getElementById(containerId);
if (!container) return;
container.innerHTML = '';
entityList.forEach(entity => {
const linkElement = document.createElement('a');
linkElement.href = normalizeUrl(entity.url);
// 使用描述作为链接文本,没有描述则使用URL
linkElement.textContent = entity.desc || entity.url;
linkElement.target = '_blank';
container.appendChild(linkElement);
// 添加分类信息展示
const categorySpan = document.createElement('span');
categorySpan.textContent = ` [分类: ${entity.category}]`;
categorySpan.style.color = '#666';
categorySpan.style.marginLeft = '8px';
container.appendChild(categorySpan);
container.appendChild(document.createElement('br'));
});
}
renderEntityLinks('linkContainer', uniqueEntities);
注意事项
在实际使用过程中,还需要注意以下几点问题:
- 如果URL中包含动态参数,比如
http://ipipp.com?id=1和http://ipipp.com?id=2,需要根据业务需求判断是否属于重复实体,如果动态参数不同代表不同内容,就不需要做去重处理。 - 去重操作最好放在数据获取之后、渲染之前执行,避免重复操作DOM,提升页面性能。
- 如果页面需要支持URL的增删操作,每次数据变更后都需要重新执行去重逻辑,保证渲染的链接始终是去重后的结果。