路由
定义路由
首先,当我们初始化 Framework7 应用时,应该使用数组参数传递默认路由:routes
在应用初始化时定义的路由是默认路由,它们将对应用中的任何视图/路由可用。
var app = new Framework7({
routes: [
{
name: 'about',
path: '/about/',
url: './pages/about.html',
},
{
name: 'news',
path: '/news/',
url: './pages/news.html',
options: {
animate: false,
},
},
{
name: 'users',
path: '/users/',
componentUrl: './pages/users.html',
options: {
props: {
users: ['John Doe', 'Vladimir Kharlampidi', 'Timo Ernst'],
},
},
on: {
pageAfterIn: function test (e, page) {
// do something after page gets into the view
},
pageInit: function (e, page) {
// do something when page initialized
},
}
},
// Default route, match to all pages (e.g. 404 page)
{
path: '(.*)',
url: './pages/404.html',
},
],
});
在应用初始化时定义的路由是默认路由,它们将对应用中的任何视图/路由可用。
如果你有一个多视图/路由应用,并且你想让某些视图/路由拥有自己的严格路由并且不希望默认路由在这个视图中可用routes
,那么你可以在视图初始化时指定
var view1 = app.views.create('.view-1', {
routes: [
{
path: '/users/',
url: './pages/users.html',
},
{
path: '/user/',
url: './pages/user.html',
},
],
});
如果你有一个多视图/路由应用,并且你想让某些视图/路由拥有额外路由并且不希望这些额外路由在其他视图中可用routesAdd
,那么你可以在视图初始化时指定
// This view will support all global routes + own additional routes
var view2 = app.views.create('.view-2', {
// These routes are only available in this view
routesAdd: [
{
path: '/blog/',
url: './pages/blog.html',
},
{
path: '/post/',
url: './pages/post.html',
},
],
})
路由属性
好的,现在我们将看看每个路由属性的含义:
参数 | 类型 | 描述 | |
---|---|---|---|
name | 字符串 | 路由名称,例如home | |
路径 | 字符串 | 路由路径。这意味着当点击匹配此路径的链接时,将加载此路由,或者可以使用此路径通过 API 加载 | |
选项 | 对象 | 包含附加路由选项的对象(可选) | |
路由 | 数组 | 包含嵌套路由的数组 | |
视图名称 | 字符串 | 此路由将在其中强制加载的视图名称 | |
主从 | |||
master | 布尔值 function(app, router) | 将此路由启用为主路由。它也可以是一个接收app 和路由实例的方法,在这个方法中,你应该返回true 或false :
| |
从路由 | 数组 | 包含从路由的数组 | |
懒加载模块 | |||
模块 | 数组 | 包含懒加载模块在路由加载之前要加载的模块 | |
内容相关属性 以下路由属性定义了内容应该如何加载(从哪里/什么) | |||
内容 | 字符串 | 从指定的内容字符串创建动态页面 | |
url | 字符串 | 通过 Ajax 加载页面内容。 也支持从路径使用
| |
组件上传递单独的属性 | 对象 | 从传递的 Framework7路由组件 | |
组件Url | 字符串 | 通过 Ajax 作为组件加载页面 也支持从路径使用 | |
异步 | 函数(context) | 执行必要的异步操作,并返回所需的路由内容和选项。它接收路由回调上下文对象的静态属性访问。 | |
异步组件 | function() | 方法应该返回解析为组件或包含 它主要设计为
| |
可路由选项卡 | |||
tabs | 数组 | 包含选项卡路由的数组 | |
可路由模态框 | |||
动作 | 对象 | 动作表路由 | |
弹出 | 对象 | 弹出路由 | |
loginScreen | 对象 | 登录屏幕路由 | |
弹出 | 对象 | 弹出路由 | |
表格 | 对象 | 表格路由 | |
可路由面板 | |||
面板 | 对象 | 面板路由 | |
事件 | |||
on | 对象 | 包含事件处理器的对象 | |
别名和重定向 | |||
alias | 字符串 数组 | 路由别名,或包含路由别名的数组。我们需要在这里指定别名路径 | |
redirect | 字符串 function(context) | 路由重定向。我们需要在这里指定重定向url(不是路径)。如果是方法,则作为参数接收路由回调上下文对象的静态属性访问。 | |
进入/离开之前 | |||
beforeEnter | function(context) 数组 | 将在路由加载/进入之前执行的函数(或函数数组)。要继续路由加载resolve 必须被调用。如果是array ,则数组中的每个函数都必须解析以继续。如果是方法,则作为参数接收路由回调上下文对象的静态属性访问。 | |
beforeLeave | function(context) 数组 | 将在路由卸载/离开之前执行的函数(或函数数组)。要继续导航resolve 必须被调用。如果是array ,则数组中的每个函数都必须解析以继续。如果是方法,则作为参数接收路由回调上下文对象的静态属性访问。 | |
keepAlive | |||
keepAlive | 布尔值 | 启用所谓的keepAlive 路由。启用后,一旦加载页面及其组件(Vue、React 或路由组件)将永远不会被销毁。相反,它将从 DOM 中分离出来,并在需要时再次重用。 |
以下是大多数可能选项的示例:
routes: [
// Load via Ajax
{
path: '/about/',
url: './pages/about.html',
},
// Dynamic page from content
{
path: '/news/',
content: `
<div class="page">
<div class="page-content">
<div class="block">
<p>This page created dynamically</p>
</div>
</div>
</div>
`,
},
// By page name (data-name="services") presented in DOM
{
path: '/services/',
pageName: 'services',
},
// By page HTMLElement
{
path: '/contacts/',
el: document.querySelector('.page[data-name="contacts"]'),
},
// By component
{
path: '/posts/',
component: {
// look below
},
},
// By component url
{
path: '/post/:id/',
componentUrl: './pages/component.html',
},
// Async
{
path: '/something/',
async: function ({ app, to, resolve }) {
// Requested route
console.log(to);
// Get external data and return page content
fetch('http://some-endpoint/')
.then((res) => res.json())
.then(function (data) {
resolve(
// How and what to load
{
content: `<div class="page">${data.users}</div>`
},
);
});
}
}
],
路由路径
如上所述,路由的path
属性表示当以下路由通过 api 或点击具有相同路径的链接加载时,将在浏览器窗口地址栏(如果browserHistory
启用)显示的路径/URL。
还支持动态路径。因此,如果你的路由中有以下路径/blog/users/:userId/posts/:postId/
并且点击具有/blog/users/12/posts/25
href 的链接,然后在加载的页面上我们访问route.params
包含{ userId: 12, postId: 25 }
路径匹配由Path To Regexp库处理,因此那里支持的所有内容在 Framework7 中也支持。例如,如果你想添加一个匹配所有路径的默认路由,我们可以使用正则表达式:
// Default route, match to all pages (e.g. 404 page)
{
path: '(.*)',
url: './pages/404.html',
},
路由选项
让我们看看可以传递在options
派发动作
参数 | 类型 | 描述 |
---|---|---|
animate | 布尔值 | 中的附加路由选项,是否应该对页面进行动画处理(覆盖默认路由器设置) |
history | 布尔值 | 是否应该将页面保存到路由历史中 |
browserHistory | 布尔值 | 是否应该将页面保存到浏览器状态中。如果你使用browserHistory ,则可以在这里传递false 以防止路由进入浏览器历史 |
reloadCurrent | 布尔值 | 用路由中的新页面替换当前页面,此情况下没有动画 |
reloadPrevious | 布尔值 | 用路由中的新页面替换历史中的上一页 |
reloadAll | 布尔值 | 加载新页面并从历史记录和 DOM 中删除所有上一页 |
clearPreviousHistory | 布尔值 | 重新加载/导航到指定路由后,上一页的历史记录将被清除 |
ignoreCache | 布尔值 | 如果设置为true ,则它将忽略缓存中的此类 URL,并再次使用 XHR 重新加载它 |
force | 布尔值 | 如果设置为true ,则它将忽略历史中的上一页,并加载指定的页面 |
props | 对象 | 将作为 Vue/React 页面组件属性传递的属性 |
transition | 字符串 | 自定义页面过渡名称 |
openIn | 字符串 | 允许将页面路由作为模态或面板打开。所以它可以是以下之一:popup , popover , loginScreen , sheet , panel |
路由回调上下文
在async
, redirect
, beforeEnter
和beforeLeave
路由属性中使用的路由上下文回调格式:
属性 | |
---|---|
app | 链接到全局应用实例 |
到 | 请求的路由 |
from | 当前激活的路由 |
路由器 | 当前路由器实例 |
resolve | 调用以解析/继续路由的方法 |
reject | 调用以阻止/拒绝路由的方法 |
方向 | 导航方向,可以是forward 或backward |
异步路由
async
路由属性是一个非常强大的工具,设计用来返回动态路由属性。它是一个具有以下参数的函数:
async(context)
- context - 路由回调上下文
resolve
路由回调的方法具有以下格式:
resolve(参数,选项)
- 参数 对象- 包含解析路由内容的对象。必须包含以下之一
url
,content
,component
或componentUrl
属性 - 选项 对象- 包含路由选项
reject
回调函数没有参数:
reject()
注意,直到你调用resolve
或reject
在异步方法中,路由将被阻止!
例如:
routes = [
{
path: '/foo/',
async({ resolve, reject }) {
if (userIsLoggedIn) {
resolve({ url: 'secured.html' })
} else {
resolve({ url: 'login.html' })
}
}
}
]
路由事件
可以使用on
路由属性在页面中添加所有页面事件。例如:
var app = new Framework7({
routes: [
// ...
{
path: '/users/',
url: './pages/users.html',
on: {
pageBeforeIn: function (event, page) {
// do something before page gets into the view
},
pageAfterIn: function (event, page) {
// do something after page gets into the view
},
pageInit: function (event, page) {
// do something when page initialized
},
pageBeforeRemove: function (event, page) {
// do something before page gets removed from DOM
},
}
},
// ...
],
});
请注意,此类路由事件实际上是 DOM 事件,因此每个此类处理程序都将接受event
作为第一个参数的事件本身和page
作为第二个参数的页面数据.
此外,此类事件处理程序的上下文 (this
) 将指向相关的路由器实例.
嵌套路由
还可以嵌套路由(路由中的路由):
routes = [
{
path: '/faq/',
url: './pages/faq.html',
},
{
path: '/catalog/',
url: './pages/catalog.html',
routes: [
{
path: 'computers/',
url: './pages/computers.html',
},
{
path: 'monitors/',
url: './pages/monitors.html',
},
...
],
}
];
这意味着什么?为了更好地理解,实际上(在底层)此类路由将被合并到以下路由中:
routes = [
{
path: '/faq/',
url: './pages/faq.html',
},
{
path: '/catalog/',
url: './pages/catalog.html',
}
{
path: '/catalog/computers/',
url: './pages/computers.html',
},
{
path: '/catalog/monitors/',
url: './pages/monitors.html',
},
];
所以假设我们在一个/catalog/
页面上,并且有以下链接:
<a href="computers/">Computers</a>
- 将按预期工作。链接将与当前路由 (/catalog/
+computers/
) 合并,我们将得到/catalog/computers/
我们在路由中有的。<a href="./computers/">Computers</a>
- 将与情况 1 相同地工作,因为./
在路径的开头意味着同一子级别。<a href="/catalog/computers/">Computers</a>
- 也将按预期工作,与情况 1 相同,因为/
(斜杠)在开头意味着根。我们在合并路由中有一个这样的根路由。<a href="/computers/">Computers</a>
- 将不会按预期工作,因为(斜杠)在开头意味着根。我们没有这样的/
(slash) in the beginning means root. And we don't have such/computers/
根路由在我们的路由中。
详细路由
对于主从视图,也可以在主路由上指定detailRoutes
除了master: true
在主路由上。
当detailRoutes
指定时,导航到详细路由也会预加载其主路由。
但与在routes
参数中指定的嵌套路由不同,详细路由path
s 不会与主路由合并path
.
routes = [
{
path: '/blog/',
url: './news.html',
master: true,
detailRoutes: [
{
/* We need to specify detail route path from root */
path: '/blog/:postId/',
url: './post.html',
},
],
},
// ...
]
可路由选项卡
可路由的标签是什么意思,为什么它很好?
- 首先,它提供了通过普通链接而不是所谓的特殊标签导航到标签的机会。
- 其次,当导航到此路由时,可以加载带有所需标签打开的页面。
- 第三,启用浏览器历史记录后,当你向后和向前导航历史记录时,将打开相同的标签。
- 最后但同样重要的是,当使用可路由标签时,可以像页面一样以相同的方式加载标签内容,即使用
url
,content
,component
或componentUrl
首先,我们需要在应用路由中指定标签路由。假设我们有一个在 /tabs/ 路由上的可路由标签页面:
routes = [
{
path: '/about-me/',
url: './pages/about-me/index.html',
// Pass "tabs" property to route
tabs: [
// First (default) tab has the same url as the page itself
{
path: '/',
id: 'about',
// Fill this tab content from content string
content: `
<div class="block">
<h3>About Me</h3>
<p>...</p>
</div>
`
},
// Second tab
{
path: '/contacts/',
id: 'contacts',
// Fill this tab content via Ajax request
url: './pages/about-me/contacts.html',
},
// Third tab
{
path: '/cv/',
id: 'cv',
// Load this tab content as a component via Ajax request
componentUrl: './pages/about-me/cv.html',
},
],
}
]
在/about-me/
页面上,我们可能有以下结构,例如:
<div class="page">
<div class="navbar">
<div class="navbar-bg"></div>
<div class="navbar-inner">
<div class="title">About Me</div>
</div>
</div>
<div class="toolbar tabbar toolbar-bottom">
<div class="toolbar-inner">
<a href="./" class="tab-link" data-route-tab-id="about">About</a>
<a href="./contacts/" class="tab-link" data-route-tab-id="contacts">>Contacts</a>
<a href="./cv/" class="tab-link" data-route-tab-id="cv">>CV</a>
</div>
</div>
<div class="tabs tabs-routable">
<div class="tab page-content" id="about"></div>
<div class="tab page-content" id="contacts"></div>
<div class="tab page-content" id="cv"></div>
</div>
</div>
与普通标签页几乎相同,但区别在于没有更多的tab-link-active
和tab-active
类别在标签链接和标签上。这些类和标签将由路由器切换。还有一个新的data-route-tab-id
属性,它对于标签切换器来说了解哪个链接与选定的路由相关是必需的。
您可以在标签页组件页面的适当部分了解更多关于可路由标签及其附加事件的信息。
可路由模态框
模态也可以路由。这里的模态指的是以下组件:弹出窗口, 弹出框, 操作表单, 登录屏幕, 表格模态。可能弹出和登录屏幕在这里有更多用例。
与可路由标签和页面相同的功能:
- 它提供了通过普通链接而不是所谓的特殊链接或 API 打开模态的机会,
- 启用浏览器历史记录后,当你刷新浏览器,向后和向前导航历史记录时,将打开相同的模态,
- 使用可路由模态,您可以像页面一样加载模态本身及其内容,即使用
url
,content
,component
或componentUrl
routes = [
...
// Creates popup from passed HTML string
{
path: '/popup-content/',
popup: {
content: `
<div class="popup">
<div class="view">
<div class="page">
...
</div>
</div>
</div>
`
}
},
// Load Login Screen from file via Ajax
{
path: '/login-screen-ajax/',
loginScreen: {
url: './login-screen.html',
/* login-screen.html contains:
<div class="login-screen">
<div class="view">
<div class="page">
...
</div>
</div>
</div>
*/
},
},
// Load Popup from component file
{
path: '/popup-component/',
loginScreen: {
componentUrl: './popup-component.html',
/* popup-component.html contains:
<template>
<div class="popup-screen">
<div class="view">
<div class="page">
...
</div>
</div>
</div>
</template>
<style>...</style>
<script>...</script>
*/
},
},
// Use async route to check if the user is logged in:
{
path: '/secured-content/',
async({ resolve }) {
if (userIsLoggedIn) {
resolve({
url: 'secured-page.html',
});
} else {
resolve({
loginScreen: {
url: 'login-screen.html'
} ,
});
}
},
}
]
根据上面的示例:
- 当你点击具有
/popup-content/
href 属性的链接时,它将打开指定字符串内容的弹出窗口, - 当你点击具有
/login-screen-ajax/
href 属性的链接时,它将执行到login-screen.html
文件的 Ajax 请求并将其作为登录屏幕打开, - 当你点击具有
/popup-component/
href 属性的链接时,它将执行到popup-component.html
文件的 Ajax 请求,将其解析为路由组件并将其作为弹出窗口打开, - 当你点击具有
/secured-content/
href 属性的链接时,如果用户已登录,它将加载来自secured-page.html
的页面,如果用户未登录,则从login-screen.html
文件打开登录屏幕。
可路由面板
可路由的面板从 Framework7 版本 3.2.0 开始可用。
面板(侧面板)也可以像可路由模态和页面一样可路由:
- 它提供了通过普通链接而不是所谓的特殊链接或 API 打开面板的机会,
- 启用浏览器历史记录后,当你刷新浏览器,向后和向前导航历史记录时,将打开相同的面板,
- 使用可路由面板,您可以像页面和模态一样加载面板本身及其内容,即使用
url
,content
,component
或componentUrl
routes = [
...
// Creates Panel from passed HTML string
{
path: '/left-panel/',
panel: {
content: `
<div class="panel panel-left panel-cover">
<div class="view">
<div class="page">
...
</div>
</div>
</div>
`
}
},
// Load Panel from file via Ajax
{
path: '/right-panel-ajax/',
panel: {
url: './right-panel.html',
/* right-panel.html contains:
<div class="panel panel-right panel-reveal">
<div class="view">
<div class="page">
...
</div>
</div>
</div>
*/
},
},
// Load Panel from component file
{
path: '/panel-component/',
panel: {
componentUrl: './panel-component.html',
/* panel-component.html contains:
<template>
<div class="panel panel-left panel-cover">
<div class="view">
<div class="page">
...
</div>
</div>
</div>
</template>
<style>...</style>
<script>...</script>
*/
},
},
]
根据上面的示例:
- 当你点击具有
/left-panel/
href 属性的链接时,它将打开指定字符串内容的面板, - 当你点击具有
/right-panel-ajax/
href 属性的链接时,它将执行到right-panel.html
文件的 Ajax 请求并将其作为右面板打开, - 当你点击具有
/panel-component/
href 属性的链接时,它将执行到panel-component.html
文件的 Ajax 请求,将其解析为路由组件并将其作为面板打开,
注意可路由的面板不能与静态面板混合。所以如果你在应用中有一个静态左面板,那么只有右面板可以作为可路由面板加载。
进入/离开前路由
beforeEnter
和beforeLeave
如果您需要在路由加载(进入)之前和卸载(离开)之后进行额外的检查、执行额外的操作或加载/发送某些内容,路由钩子会非常有用。它可以是一个方法或一个要执行的方法数组。例如:
routes = [
{
path: 'profile',
url: 'profile.html',
beforeEnter: function ({ resolve, reject }) {
if (/* some condition to check user is logged in */) {
resolve();
} else {
// don't allow to visit this page for unauthenticated users
reject();
}
},
},
{
path: 'profile-edit',
url: 'profile-edit.html',
beforeLeave: function ({ resolve, reject }) {
if (/* user didn't save edited form */) {
app.dialog.confirm(
'Are you sure you want to leave this page without saving data?',
function () {
// proceed navigation
resolve();
},
function () {
// stay on page
reject();
}
)
} else {
resolve();
}
}
}
]
当然,当作为函数数组传递时,支持多个钩子:
function checkAuth({ to, from, resolve, reject }) {
if (/* some condition to check user is logged in */) {
resolve();
} else {
reject();
}
}
function checkPermission({ to, from, resolve, reject }) {
if (/* some condition to check user edit permission */) {
resolve();
} else {
reject();
}
}
routes = [
{
path: '/profile/',
url: 'profile.html',
// check if the user is logged in
beforeEnter: checkAuth,
},
{
path: '/profile-edit/',
url: 'profile-edit.html',
// check if the user is logged in and has required permission
beforeEnter: [checkAuth, checkPermission],
}
]
注意beforeEnter
如果页面使用component
属性加载,则无法正常工作,在这种情况下,您应该使用async
或asyncComponent
属性在beforeEnter
触发后动态加载页面
重定向 & 别名
别名
我们可以使用路由alias
属性传递路由别名。在这种情况下,别名基本上意味着同一个路由可以有多个路径来访问:
routes = [
{
path: '/foo/',
url: 'somepage.html',
alias: '/bar/',
},
{
path: '/foo2/',
url: 'anotherpage.html',
alias: ['/bar2/', '/baz/', '/baz2/'],
}
]
根据上面的示例:
- 如果我们通过
/foo/
或/bar/
URL 请求页面,则第一个路由将匹配,我们将从somepage.html
- 如果我们通过
/foo2/
,/bar2/
,/baz/
,/baz2/
URL 请求页面,则第二个路由将匹配,我们将从anotherpage.html
重定向
我们可以使用redirect
派发动作
- 如果我们把
redirect
作为string
传递,我们必须在这里传递直接的重定向 URL - 如果我们把
redirect
作为function
传递,我们需要调用函数的 resolve 参数并传递重定向 URL
例如:
routes = [
{
path: '/foo/',
url: 'somepage.html',
},
{
path: '/bar/',
redirect: '/foo/',
},
{
path: '/baz/',
redirect: function ({to, resolve, reject}) {
// if we have "user" query parameter
if (to.query.user) {
// redirect to such url
resolve('/foo/?user=' + to.query.user);
}
// otherwise do nothing
else reject();
}
}
]
上述示例意味着:
- 当我们请求
/bar/
URL 时,路由器将重定向到/foo/
URL,然后搜索匹配此新 URL 的路由。在这种情况下,它将匹配具有路径/foo/
的第一个路由,并从somepage.html
- 当我们请求
/baz/?user=john
时,我们将重定向到 URL/foo/?user=john
该 URL 将匹配第一个路由 - 当我们请求
/baz/
(不带查询)时什么都不会发生
注意,在重定向中我们传递 URL,而不是像别名中那样传递路由路径
Keep Alive
keepAlive 路由从 Framework7 版本 3.6.0 开始可用。
当keepAlive
启用时,一旦路由加载此类页面,页面及其组件(Vue、React 或 Router 组件)将永远不会被销毁。相反,它将被从 DOM 中分离出来,并在需要时再次重用。
对于不经常更新的“重型”页面启用它可能很有用。例如,带有地图的页面,或带有重型画布或其他计算的页面。使用常规逻辑,所有这些重型计算都会在每次访问此页面时发生。但使用 keepAlive,它将只执行一次,并在所有后续访问中,路由器将重用已经渲染的页面 DOM 元素。
如果您确实需要保留页面状态,启用 keepAlive 也很有用。在启用 keepAlive 的情况下,下次加载页面时,所有 DOM 修改和表单元素状态都将被保留。
但要注意以下几点:
- 不支持
async
路由 - 仅支持页面(不支持面板和模态)
- 如果您有动态路由路径(如
/some-page/:foo/:bar
)或依赖页面路由查询 (?foo=bar
),则初始加载后查询和路由参数将不会更改。 page:beforeremove
,pageBeforeRemove
此类页面的页面事件将永远不会触发- 如果您将其用作 Vue 组件,则
beforeDestroy
,destroyed
钩子将永远不会触发此类组件。created
,mounted
钩子将只触发一次。 - 如果您将其用作 React 组件,则
componentWillUnmount
方法将永远不会触发此类组件。componentWillMount
,componentDidMount
方法将只触发一次。 - 如果您将其用作 F7 的 Router 组件,则
beforeDestroy
,destroyed
钩子将永远不会触发此类组件。created
,mounted
钩子将只触发一次。
为了避免组件和页面生命周期中的陷阱,建议依赖以下页面事件:
page:mounted
- 将始终在 keepAlive 路由页面的 DOM 元素附加或重新附加时调用。page:beforeunmount
- 将始终在 keepAlive 路由页面的 DOM 元素将要从 DOM 中分离时调用。
要创建 keepAlive 路由,我们只需要将其参数传递给keepAlive: true
to its parameters:
import SomPageComponent from './some-page.js';
var routes = [
/* Usual route */
{
path: '/',
url: './pages/home.html',
},
/* Alive route. Will be loaded from file first time, and then will reuse rendered DOM element */
{
path: '/map/',
url: './pages/map.html',
keepAlive: true,
},
/* Alive route. Will be created as component, and then will reuse rendered DOM element */
{
path: '/some-page/',
component: SomPageComponent,
keepAlive: true,
},
];