截取URL域名最稳妥的方案是使用原生new URL()方法,它能直接解析出协议、域名、端口等全部组成部分,只有在处理非标准格式URL时才需要配合正则表达式。
快问快答:js截取url域名怎么才能不出错
很多前端开发者在做跳转统计、接口鉴权、白名单校验时,都需要从完整地址里抽出域名部分,有人用split('/')切,有人用indexOf找,结果一遇到带端口、带参数、带hash的链接就翻车。
核心原理:URL对象把域名拆好了等你拿
现代浏览器和Node.js都内置了URL构造函数,传入完整地址后,它会自动解析出各个组成部分,这是目前js截取url域名最标准的做法。
const url = new URL('https://user:pass@www.example.com:8080/path/page?name=test#section');
console.log(url.hostname); // 输出 www.example.com
console.log(url.host); // 输出 www.example.com:8080
console.log(url.protocol); // 输出 https:
console.log(url.origin); // 输出 https://www.example.com:8080
hostname只返回域名部分,不含端口号,这是多数场景下最需要的值。host则把端口一起带出来,这两个属性要分清。
从完整URL提取域名的标准写法
实际开发中,我们拿到的可能是完整的地址,也可能是不带协议的路径,分情况处理:
// 场景一:完整URL
function getDomainFromFullUrl(fullUrl) {
try {
return new URL(fullUrl).hostname;
} catch (e) {
return '';
}
}
// 场景二:可能缺失协议的地址
function getDomainSmart(rawUrl) {
if (!rawUrl) return '';
const normalized = rawUrl.startsWith('http') ? rawUrl : `https://${rawUrl}`;
return new URL(normalized).hostname;
}
这里有个关键点:new URL()对协议要求严格,缺了http://或https://会直接抛异常,所以处理用户输入时,要先补全协议再解析。
js获取当前域名与页面URL处理的区别
页面环境里拿当前站点的域名比较简单,但要注意区分几个API:
window.location.hostname:当前域名的纯主机名部分window.location.host:包含端口号的完整主机部分window.location.origin:协议+主机+端口,如https://www.example.com:8080document.domain:旧式写法,子域可以修改,但主域不能跨域获取
多数情况下直接读window.location.hostname就够了,但如果你需要对当前页面URL做更复杂的解析,还是要走new URL(window.location.href)这条路。
js正则截取域名的适用场景与坑
正则方式仍然是不少老项目里的主流写法,而且某些场景下它确实有存在价值。
什么情况下必须用正则
- 输入是纯域名字符串,没有协议和路径,用URL对象反而要多做拼接
- 需要从一段混合文本里捞出域名,比如爬虫抓取的内容、日志文件里的零散字段
- 处理古老的IE环境(现在这个比例已经很低,微信内置浏览器也支持URL对象了)
一个能用的正则模板
function extractDomainWithRegex(input) {
const match = input.match(/^(?:https?://)?(?:[^@n]+@)?([^:/n]+)/im);
return match ? match[1] : '';
}
// 用法
console.log(extractDomainWithRegex('https://www.example.com/path')); // www.example.com
console.log(extractDomainWithRegex('ftp://files.example.org/file')); // files.example.org
console.log(extractDomainWithRegex('直接输入域名example.com')); // 直接输入域名example // 注意这个坑
看到最后一行了吗?这个正则遇到无协议、无分隔符的纯文本时会截断到空格或中文边界,这不是正则本身的问题,而是输入格式不确定时必然面临的歧义。
正则匹配域名的复杂度边界
行业共识认为,完全用正则覆盖所有合法域名格式是不现实的国际化域名(中文域名、带音标的拼音域名)、IP地址、localhost、带端口的情况,规则层层叠加,正则表达式会膨胀到难以维护的程度。
所以我的建议是:
- 规范化输入场景(如表单提交、API参数)优先用
new URL() - 非结构化文本(如论坛帖子、聊天记录)才用正则先粗筛,再用URL对象二次验证
- 永远不要试图用一条正则解决所有边界情况
细节坑位:端口、子域名、IP地址与特殊场景
截取域名看起来简单,但真实业务里的URL五花八门,有几个高频坑值得单独拿出来说。
端口号:要还是不要
一个常见疑问是:js获取url参数和域名时,端口到底算不算域名的一部分?答案是不算,域名是域名,端口是端口。
| 方法 | 输入 | 输出 | 说明 |
|---|---|---|---|
url.hostname |
http://a.com:3000 |
a.com |
推荐,纯域名 |
url.host |
http://a.com:3000 |
a.com:3000 |
带端口,适合做完整来源校验 |
| 正则提取 | http://a.com:3000 |
a.com |
按需设计 |
如果你拿来做跨域比较,用hostname;如果做来源归属统计,建议用host,因为80端口和8080端口指向的服务可能完全不同。
子域名是整体,不要擅自拆
有开发者习惯把www.example.com拆成example.com再存,这会导致两个问题:
- 无法区分
www.example.com和m.example.com两个独立站点 - 如果站点部署在子域隔离环境,拆掉后可能误判来源
保持原样抽取,拆分逻辑交给业务层按需处理,不要在设计公共函数时嵌入太具体的业务偏好。
IP地址和localhost这类特例
const url1 = new URL('http://127.0.0.1:8080/test');
console.log(url1.hostname); // 127.0.0.1
console.log(url1.host); // 127.0.0.1:8080
const url2 = new URL('http://localhost:3000/a');
console.log(url2.hostname); // localhost
这段代码跑在任何现代浏览器里都不会报错,但有一个容易被忽略的边界:IPv6地址。
const url3 = new URL('http://[::1]:8080/test');
console.log(url3.hostname); // [::1],带方括号
console.log(url3.host); // [::1]:8080
做校验时如果用了includes或startsWith这类方法,要注意IPv6地址带着方括号一起返回,直接拼接字符串极易踩坑。
实操对比:不同场景选型指南
为了帮你在具体项目里快速决策,下面用一份对照表把方案差异展示清楚。
方案横向对比
| 场景 | 推荐方案 | 代码量 | 兼容性 | 备注 |
|---|---|---|---|---|
| 完整URL解析 | new URL() |
1行 | 现代浏览器+Node 10+ | 首选 |
| 当前页面域名 | location.hostname |
1行 | 全兼容 | 最省事 |
| 非结构化文本 | 正则粗筛+URL二次校验 | 3-10行 | 视实现方式而定 | 别用单一正则 |
| 纯域名字符串 | 正则模板 | 1-3行 | 全兼容 | 需要先约定输入约束 |
| 需要协议+域名+端口组合 | url.origin |
1行 | 现代浏览器 | 比手工拼字符串稳 |
实际开发中的完整处理链路
拿一个典型的白名单校验场景举例:
// 定义一个通用函数,返回域名对象
function parseUrlInfo(input) {
const url = new URL(input.startsWith('http') ? input : `https://${input}`);
return {
domain: url.hostname,
port: url.port,
protocol: url.protocol,
path: url.pathname,
params: Object.fromEntries(url.searchParams.entries()),
hash: url.hash
};
}
// 白名单校验
const whiteList = ['example.com', 'api.example.com'];
const target = 'https://api.example.com/v1/data?key=123';
const { domain } = parseUrlInfo(target);
if (whiteList.includes(domain)) {
// 放行
} else {
// 拦截
}
这个例子把url的参数、路径、域名一次性拆干净,如果你还需要js获取url参数和域名之间的关联逻辑,直接在params对象上操作即可。
为何不推荐手写字符串切割
项目初期图省事用split('/')[2],看起来短时间内没问题,但一旦出现:
- URL带参数的路径部分包含
- hash里的值恰好带
http:// - 协议缺失时索引错位
这些情况都会让切割逻辑漏水。维护成本远超那几行代码的收益,这是常见的时间换空间陷阱。
js截取url域名常见问题解答
Q:域名带www和不带www,怎样统一处理
这取决于具体业务是否需要区分两者,从技术角度讲,www.example.com和example.com属于不同主机名,是否视为同一个站点取决于DNS配置和Cookie设置,技术处理上可以这样:
const hostname = new URL(url).hostname; const noWww = hostname.replace(/^www./, '');
但请记住:不要默认去掉www,如果统一去掉后服务端校验逻辑没跟上,可能绕过安全设置,建议先明确业务规则,再决定是否执行替换。
Q:从完整的URL里同时拆分域名和查询参数,怎么做最合适
直接用URL对象一次性取出所有部分就行,这是它的本职功能:
const url = new URL('https://example.com/path?page=1&size=20#title');
const domain = url.hostname; // example.com
const params = url.searchParams; // URLSearchParams对象
const page = params.get('page'); // 1
URLSearchParams已经处理好了?page=1&size=20这类格式的解析,不需要额外引第三方库,多个相同参数名时getAll('key')可以拿到数组形式的全部值。
Q:输入的URL没有协议前缀,直接用new URL()报错了怎么办
new URL()对协议前缀要求严格,缺了http:或https:就会抛TypeError,处理方案是自动补全,但要判断是否存在协议:
function safeParse(raw) {
if (!/^[a-zA-Z][a-zA-Z0-9+.-]:/.test(raw)) {
raw = 'https://' + raw;
}
return new URL(raw);
}
该方案对example.com/path这类格式有效,同时不会误伤那些已经带有协议头的输入,如果输入是ftp://、ws://这类非HTTP协议,严禁擅自补全为https,应保留原协议做解析。
结尾说一句:拿不准时就用new URL(),它把边界情况处理得足够好;需要正则出手的场合建议搭配URL对象做二次校验,两者配合才能在js截取url域名的各种场景里都输出稳定结果。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/636940.html





