路徑處理
m.route
和 m.request
各自都有一個稱為路徑(path)的概念。它用於產生您要路由至或從中獲取的 URL。
路徑類型
路徑主要有兩種類型:原始路徑(Raw paths)和參數化路徑(Parameterized paths)。
- 原始路徑(Raw paths)是指直接用作 URL 的字串。不會進行任何替換或分割。它只會被正規化,所有參數都會附加到末尾。
- 參數化路徑(Parameterized paths)讓您可以將值插入路徑中,預設情況下會進行跳脫(escape),以方便使用並防止 URL 注入攻擊。
對於 m.request
來說,這些路徑可以是任何 URL,但對於 路由 來說,這些路徑只能是沒有協定(schemes)或網域(domains)的絕對 URL 路徑名稱。
路徑參數
路徑參數使用起來很簡單,有以下兩種形式:
:foo
- 這會將params.foo
的值注入到 URL 中,並首先對其值進行跳脫處理。:foo...
- 這會將params.foo
的原始路徑注入到 URL 中,而不進行任何跳脫處理。
您可能想知道 params
物件是什麼。它非常簡單:它是 m.route.set(path, params)
或 m.request({url, params})
中的 params
參數。
當透過 m.route(root, defaultRoute, routes)
接收路由時,您可以使用這些參數從路由中_提取_值,提取方式與產生路徑的方式基本相同,只是方向相反。
// 編輯單個項目
m.route(document.body, '/edit/1', {
'/edit/:id': {
view: function () {
return [m(Menu), m('h1', 'Editing user ' + m.route.param('id'))];
},
},
});
// 編輯由路徑識別的項目
m.route(document.body, '/edit/pictures/image.jpg', {
'/edit/:file...': {
view: function () {
return [m(Menu), m('h1', 'Editing file ' + m.route.param('file'))];
},
},
});
在第一個範例中,假設您正在導航到預設路由,m.route.param("id")
會被讀取為 "1"
,而 m.route.param("file")
會被讀取為 pictures/image.jpg
。
路徑參數可以用 /
、-
或 .
分隔。這讓您能夠使用動態路徑段,比單純的路徑名稱更靈活。例如,您可以針對類似 "/edit/:name.:ext"
的路由進行匹配,以根據檔案副檔名進行編輯,或者針對 /:lang-:region/view"
進行本地化的路由。
路徑參數是貪婪的(greedy):給定一個宣告的路由 "/edit/:name.:ext"
,如果您導航到 /edit/file.test.png
,則提取的參數將是 {name: "file.test", ext: "png"}
,而不是 {name: "file", ext: "test.png"}
。 類似地,給定 "/route/:path.../view/:child..."
,如果您前往 /route/foo/view/bar/view/baz
,則提取的參數將是 {path: "foo/view/bar", child: "baz"}
。
參數正規化
為了方便起見,並保持路徑名稱的可讀性,插入到路徑名稱中的路徑參數會從查詢字串中省略。 這會發送一個伺服器請求 GET /api/user/1/connections?sort=name-asc
,並省略 URL 字串中重複的 id=1
。
m.request({
url: 'https://example.com/api/user/:userID/connections',
params: {
userID: 1,
sort: 'name-asc',
},
});
例如,以下範例與上述範例等效:
m.request({
url: 'https://example.com/api/user/:userID/connections?sort=name-asc',
params: {
userID: 1,
},
});
當然,您可以混合和匹配。這會觸發請求 GET /api/user/1/connections?sort=name-asc&first=10
。
m.request({
url: 'https://example.com/api/user/:userID/connections?sort=name-asc',
params: {
userID: 1,
first: 10,
},
});
路由匹配也適用:您可以針對帶有顯式查詢字串的路由進行匹配。它保留了匹配的參數以方便使用,因此您仍然可以透過 vnode 參數或透過 m.route.param
訪問它們。 請注意,雖然這_可行_,但通常不建議這樣做,因為您應該優先使用路徑作為頁面。 如果您需要為特定檔案類型產生略有不同的視圖,但從邏輯上講,它仍然是一個類似查詢的參數,而不是一個完全獨立的頁面,那麼這有時可能很有用。
// 注意:通常*不*建議這樣做 - 您應該首選路徑作為路由
// 宣告,而不是查詢字串。
m.route(document.body, '/edit/1', {
'/edit?type=image': {
view: function () {
return [m(Menu), m('h1', 'Editing photo')];
},
},
'/edit': {
view: function () {
return [m(Menu), m('h1', 'Editing ' + m.route.param('type'))];
},
},
});
查詢參數會被自動處理 - 您不需要命名它們來接受。 您可以根據現有值進行匹配,例如在 "/edit?type=image"
中,但您不需要使用 "/edit?type=:type"
來接受該值。 實際上,Mithril.js 會將其視為您試圖完全匹配 m.route.param("type") === ":type"
,所以您可能不想這樣做。 簡而言之,使用 m.route.param("key")
或路由元件屬性來讀取查詢參數。
路徑正規化
這些小差異通常會造成阻礙,使路由和路徑處理比預期的複雜得多。 Mithril.js 在內部正規化路徑以進行路由,但它不會直接公開當前正規化的路由。(您可以透過 m.parsePathname(m.route.get()).path
計算它。)
在匹配期間,如果參數重複,查詢字串中的參數優先於路徑名稱中的參數,且 URL 末尾附近的參數優先於 URL 開頭附近的參數。
路徑跳脫
有一些字元,如果您想按字面意思使用它們,則需要對它們進行跳脫。 便利的是,encodeURIComponent
會對這些字元(以及更多字元)進行編碼。當您替換參數並新增查詢參數時,Mithril.js 會根據需要使用它進行編碼。 以下是 Mithril.js 會處理的字元:
:
=%3A
/
=%2F
(僅在路徑中需要)%
=%25
?
=%3F
(僅在路徑中需要)#
=%23
當然,還有其他您必須按照 URL 規範進行跳脫的字元。但正如前面提到的,encodeURIComponent
會為您執行此操作,Mithril.js 會在您替換參數時隱式使用它。 因此,您只需要關心是否需要顯式指定參數,例如在 m.request("https://example.com/api/user/User%20Name/:field", {params: {field: ...}})
中。