URL的构成
URL,全称为Uniform Resource Locator,即标准资源定位器,是通过互联网上唯一的地址来定位各种网络资源的机制。在JavaScript对应的类为URL。下面是创建指向当前页面的URL的一个实例的例子:
let url = new URL('docs/javascript/url/index.php', 'https://www.sarkuya.com');
pc.log(url);
在其构造器中,第一个参数为以相对路径的方式来指定,第二个参数是基本路径 (base url)。在所创建的url中,这两部分将自动组合为一个完整的路径。
URL有众多与网络地址相关的属性。下面列出了一些经常出现的属性:
let url = new URL('docs/javascript/url/index.php?username=Mike#sect-1', 'https://www.sarkuya.com:80');
function display(propName) {
pc.log(`${propName}: ${url[propName]}`);
}
let propNames = [
'href',
'pathname',
'protocol',
'hostname',
'port',
'host',
'origin',
'searchParams',
'hash'
];
propNames.forEach(display);
其中,href是最全的地址。pathname是剥离了其他辅助元素之后、比较干净的的当前网页地址的相对路径,根据此特点,我们经常使用该属性来动态地构建不同的URL。
protocol是网络协议,hostname是网站名称,port的端口号,默认为80(另一个也较常见的端口号为8080)。
host = hostname + port
origin = protocol + host
因此,origin是成分较为齐备的网站名称。
searchParams是请求参数。hash指向当前网页中的特定锚点(anchor)。
在URL构造器的参数中,hash应是放在最后的一个属性,须排在searchParams属性之后。如果hash排在searchParams之前,则searchParams的值将为空字符串,而hash的值将为#sect-1?username=Mike
,从而导致出现一个不易觉察的错误。
window.location
构建URL
可通过location对象,根据当前请求页面地址,来快捷地构建一个URL。
let location = window.location;
pc.log(location);
pc.log(location.href === document.baseURI);
let url = new URL('./test.html', location);
pc.log(url);
尽管document的baseURI属性值与location的href属性值一样,但前者是一个字符串,无法解析出第一节所示的各个成分,因此前者缺乏一定的灵活性。
注意到location与url这两个变量分别属于Location及URL不同的类型。这两种类型有较多相同的属性名称,但各自都有自己独有的属性名称(确切来说,Location的属性都是自身属性,而URL的属性都是prototype属性)。下面是它们的属性名称两相比较的结果。
const {PrototypeUtils} = await import('/js/esm/PrototypeUtils.js');
let location = window.location;
let url = new URL('./test.html', location);
let locationPropNames = PrototypeUtils.GetPropNamesInPrototypeChain(location);
let urlPropNames = PrototypeUtils.GetPropNamesInPrototypeChain(url);
let comparedObj = PrototypeUtils.GetComparedArrays(locationPropNames, urlPropNames);
pc.log('Properties that exists in both:');
pc.log(comparedObj.both);
pc.log('\n');
pc.log('Properties that exists only in location:');
pc.log(comparedObj.onlyA);
pc.log('\n');
pc.log('Properties that exists only in url:');
pc.log(comparedObj.onlyB);
pc.log('\n');
Location能用作URL的构造参数,是因为前者实现了一个toString方法,该方法返回一个表示请求地址的字符串:
pc.log(window.location.toString());
递归构建URL
利用URL构造函数中可以访问上级目录、且可以同时指定基础路径的特点,我们可以编写一个递归函数,在每次遍历中,只需将当前工作路径设置为上一次遍历时的父路径即可。
let currURL = window.location;
pc.log(`current path: ${currURL.pathname}`);
let fileName = 'serial-parts-struct.json';
async function findInParent() {
let serialURL = new URL(`../${fileName}`, currURL);
let response = await fetch(serialURL);
if (response.ok) {
pc.log(`find: ${serialURL.pathname}`);
return await response.json();
}
if (serialURL.pathname === `/${fileName}`) {
return;
} else {
currURL = serialURL;
return findInParent();
}
}
let jsonFile = await findInParent();
这种技巧在自动构建多层级的动态网页时比较实用。
但这种方法无法捕获Response抛出的404异常,且在一些浏览器中,无法捕获的异常可能会影响后续代码。因此这种方式只适合应用于特定场合且需经调试。
而如果要查找的文件位置有默认的约定,如总存在于二级子路径下面,则可通过正则表达式快速地加载。
let partStruct = await (async () => {
let currURL = window.location;
let fileName = 'serial-parts-struct.json';
let serialJSONLocation = /(^\/.+?\/.+?\/).+/.exec(currURL.pathname);
return await fetch(`${serialJSONLocation[1]}${fileName}`)
.then(response => response.json());
}
)();
pc.log(partStruct);
本站的技术文档的结构与存储均大量借鉴了DocBook 5的众多长处,因此serial-parts-struct.json文件在本站中有举足轻重的作用,例如,若将基础篇中的概述
调整至高级篇中,只需修改此文件即可。在用户浏览网页时,相应的JavaScript文件则依据此文件自动即时编排文章结构,自动生成左边、右边及下边的导航栏,以及正文中的章节目录编号,非常方便。