寫在前面
7 月 11 日 -8 月 27 日,一個半月過去了,以周為單位的時間過得特別快
每天都忙,卻記不清在忙些什麼。期間
-
沒有再翻過新書(寫部落格時偶爾翻過 Node 和 JS,因為內容有些淡忘了)
-
看了 1.5 篇散文
-
Kindle 充電 2 次,但從未使用過
-
日語學習到第七課結束
-
週一 FEX 週刊(有一次是週二發的),週五奇舞週刊(這週來自凹凸實驗室的 mock 淘寶造物節全景很不錯)
最近在看什麼書,有推薦的嗎?
日語書,沒有大片時間看書,對日常工作熟練了就有時間了
真是這樣嗎?為什麼沒有大片時間?
一。問題簡述
請用 URL 規範的辦法取出 host,然後判斷 host,不要按照單個字元判斷
場景就是這樣,當前頁的 query string 攜帶了一個 url 參數,我們需要從中解析出 hostname
location.hostname 能夠取出當前頁的主機名,那對於任意一個 URL 串呢?有非正則方案嗎?
之前筆者認為是沒有的,記憶中JS API 沒有提供 之類的,以為必須通過正則解析new URL()
出去搓的時候提出了這個問題,三位老司機瞬間給出了 3 種方案
二。解決方案
標準 URL 格式為:scheme://domain:port/path?query_string#fragment_id,簡單的正則捕獲分分鐘解析好
也有一些奇怪的(精心構造的)URL:
http://www.example.com/public/page/2015/index.html?url=http://12.23.34.45/hack.html?http://www.example.com//check.htm
http://www.example.com/public/page/2015/index.html?url=http://www.example.com @12.23.34.45/hack.html
P.S.為了看清楚,url 參數部分沒有進行 encode,這樣的參數看著就不懷好意
甚至還有一些不合規範的 URL,使用一般的正則很難應對,比如:
// 這個東西作何解?
http://www.example.com/what??key=val?&{http:// @www.abc.com?query=2#45
// 那這個呢?
http://www.example.com:8899 @www.abc.com/what??key=val?&{http://?query=2#45
// 這個?
http://www.example.com:$88;9,9 @www.abc.com$/what??key=val?&{http://?query=2#45
//...
1.a 標籤自動解析 URL
var a = document.createElement('a');
a.href = 'http://www.example.com/news.php?id=10#footer';
var div = document.createElement('div');
for (var key in a) {
!(key in div) && console.log(`${key} = ${a[key]}`);
}
輸出結果是這樣:
// 說明在哪裡顯示指向的資源,當前窗體、新標籤頁等等
target =
// 通知 UA 下載指向的資源
download =
// 點擊連結時非同步 POST 指定地址,用於廣告統計
ping =
// 表明指向的資源與當前資源的關係,備胎、書籤等等
rel =
// 指向的資源的 language
hreflang =
// 指向的資源的 MIME type
type =
// 請求頭 referer 字段策略,用於保護使用者隱私
referrerpolicy =
//
text =
// 已廢棄。支援自定義形狀,傳入一系列座標點
coords =
// 已廢棄。指向的資源的字元編碼
charset =
// 已廢棄。跳轉到指定 name 的標籤
name =
// 已廢棄。反向關係,rel 的反義詞
rev =
// 已廢棄。用於指定自定義形狀熱區
shape =
// 指向的資源的 URL,或者 URL 的#fragment_id 部分
href = http://www.example.com/news.php?id=10#footer
origin = http://www.example.com
protocol = http:
username =
password =
host = www.example.com
hostname = www.example.com
port =
pathname = /news.php
search = ?id=10
hash = #footer
這些都是 a 標籤特有的屬性,裡面就有我們想要的 hostname,也就是說,a 標籤自動完成了 URL 解析,對於前端來說,這曾經是解析 URL 最廉價的方式:
var getHostname = function(url) {
var a = document.createElement('a');
a.href = url;
return a.hostname;
};
100% 可靠,再複雜的 URL 也騙不了瀏覽器
2.JS URL API
var url = new URL('http://www.example.com:$88;9,9 @www.abc.com$/what??key=val?&{http://?query=2#45');
for (var key in url) {
console.log(`${key} = ${url[key]}`);
}
Chrome 下輸出結果:
searchParams = %3Fkey=val%3F
href = http://www.example.com:$88%3B9,9 @www.abc.com%24/what?%3Fkey=val%3F#123http://?query=2#45
origin = http://www.abc.com%24
protocol = http:
username = www.example.com
password = $88%3B9,9
host = www.abc.com%24
hostname = www.abc.com%24
port =
pathname = /what
search = ?%3Fkey=val%3F
hash = #123http://?query=2#45
因為這個 URL 太不合標準,UA 處理細節有差異,FF 下的結果不同:
"href = http://www%2Eexample%2Ecom:$88%3B9,9 @www.abc.com$/what??key=val?&{http://?query=2#45"
"origin = http://www.abc.com$"
"protocol = http:"
username = www%2Eexample%2Ecom
password = $88%3B9,9
host = www.abc.com$
hostname = www.abc.com$
port =
pathname = /what
search = ??key=val?&&
searchParams = %3Fkey=val%3F
hash = #123http://?query=2#45
瀏覽器確實偷偷提供了 URL 類,不是 ES5 也不是 ES6、7 標準,目前只是實驗性的特性,相容性如下:
Android4.0 webkitURL
Android4.4 URL
Safari6.0 webkitURL
Chrome32 URL
FF19 URL
IE10 URL
移動端基本可以放心使用,更多相容性資訊請查看 URL - Web APIs | MDN
var getHostname = function(url) {
return new URL(url).hostname;
};
3.正則解析
var parseUrl = function(url) {
var urlParseRE = /^\s*(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^: @\/#\?]+)(?:\:([^: @\/#\?]+))?) @)?(([^:\/#\?\]\[]+|\[[^\/\] @#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/;
var matches = urlParseRE.exec(url || "") || [];
return {
href: matches[0] || "",
hrefNoHash: matches[1] || "",
hrefNoSearch: matches[2] || "",
domain: matches[3] || "",
protocol: matches[4] || "",
doubleSlash: matches[5] || "",
authority: matches[6] || "",
username: matches[8] || "",
password: matches[9] || "",
host: matches[10] || "",
hostname: matches[11] || "",
port: matches[12] || "",
pathname: matches[13] || "",
directory: matches[14] || "",
filename: matches[15] || "",
search: matches[16] || "",
hash: matches[17] || ""
};
};
被嚇哭了,試一試夠不夠健壯:
var url = parseUrl('http://www.example.com:$88;9,9 @www.abc.com$/what??key=val?&{http://?query=2#45');
for (var key in url) {
console.log(`${key} = ${url[key]}`);
}
輸出結果:
href = http://www.example.com:$88;9,9 @www.abc.com$/what??key=val?&{http://?query=2#45
hrefNoHash = http://www.example.com:$88;9,9 @www.abc.com$/what??key=val?&&
hrefNoSearch = http://www.example.com:$88;9,9 @www.abc.com$/what
domain = http://www.example.com:$88;9,9 @www.abc.com$
protocol = http:
doubleSlash = //
authority = www.example.com:$88;9,9 @www.abc.com$
username = www.example.com
password = $88;9,9
host = www.abc.com$
hostname = www.abc.com$
port =
pathname = /what
directory = /
filename = what
search = ??key=val?&&
hash = #123http://?query=2#45
與 FF 完全一致,結果可信度很高。那麼我們嘗試解讀一下這個無敵的正則:
/^ #href
\s*
( #hrefNoHash
( #hrefNoSearch
( #domain
([^:\/#\?]+:)? #protocol
(?:
(\/\/) #doubleSlash
( #authority
(?:
( #取結果時$7 被跳過了,應該也用非捕獲型括號 (?:
([^: @\/#\?]+) #username
(?:
\:
([^: @\/#\?]+) #password
)?
)
@
)?
( #host
([^:\/#\?\]\[]+|\[[^\/\] @#?]+\]) #hostname
(?:
\:
([0-9]+) #port
)?
)
)?
)?
)?
( #pathname
(\/?(?:[^\/\?#]+\/+)*) #directory
([^\?#]*) #filename
)
)?
(\?[^#]+)? #search
)
(#.*)? #hash
/
根據上面的分析,第 9 個左括號應該用非捕獲型括號 (?:,取值的時就不用跳過 $7 了,如下:
var getHostname = function(url) {
// 第 9 個小括號改掉了
var urlParseRE = /^\s*(((([^:\/#\?]+:)?(?:(\/\/)((?:(?:([^: @\/#\?]+)(?:\:([^: @\/#\?]+))?) @)?(([^:\/#\?\]\[]+|\[[^\/\] @#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/;
var matches = urlParseRE.exec(url || "") || [];
return matches[10] || "";
};
看瞎了,正則再見。
三。方案分析
a 標籤
老司機 1 前端經驗豐富,冷門技巧瞬間解決問題
相容性沒問題(很多年前的技巧了),純前端方案,簡單粗暴療效確切,a 標籤竟然這麼強大
更多冷門技巧請查看 前端不為人知的一面--前端冷知識集錦,昨天新發現的另一位老前輩,追隨之
URL 類
老司機 2 眼界開闊,細節紮實
非標準的 URL 類都知道,我天天用 console 也沒主意這個,不細心就會少經驗,就像超級瑪麗
無敵正則
老司機 3 解決問題經驗豐富,資源積累很多
這正則,嚇哭了,orz
四。總結
早出晚歸,不漲經驗,我在忙些什麼?
時間碎片化,沒有明確的當前時段 task,一抬頭,又半小時過去了,一轉眼,又該開周會了……一個半月過去了,經驗條一動不動
長此以往,泯然碼農矣(3 年工作經歷,1 年工作經驗)
暫無評論,快來發表你的看法吧