如果尝试简要描述Web应用程序前端的路由功能由什么组成,则可以得出结论,每个流行的框架对此都有完全不同的想法。 即使比较相同框架的版本,我们也可以得出结论,功能和路由API最容易发生更改(通常没有向后兼容性)。 例如,React中路由的第4版进行了彻底的重新设计,以至于githab.com上一些受欢迎的项目都没有切换到该版本。
在所有这些背后,有一个普遍的趋势,在我看来,这是许多流行的前端框架中路由功能的重载。 在这方面,它与可能与路由隔离的其他组件(例如,导航,历史记录,链接等)紧密相连。 因此,可能很多人对使用路由感到不舒服时的感觉很熟悉,并且根本不可能扩展路由。 与灵活和可扩展的组件相比,流行的前端框架中的路由看上去不那么方便,而且根本无法扩展。 在React的第一个版本(直到第4个版本)中尤其如此。
在这篇文章中,我将探讨一些导致路由问题的历史点,以及使用通用路由器库和React的情况。
布线是否必要?
从技术上讲,一页Web应用程序无需路由即可工作。 例如,由于桌面应用程序中没有路由。 如果一页Web应用程序不保持相同的Web浏览器应用程序,那么一切都会正常运行。 也就是说,用户可以随时按F5键或单击浏览器的“重新加载”图标来刷新页面。 或者,用户可以随时通过单击“左箭头”和“右箭头”图标或按“退格”键来向前或向后滚动故事。
因此,对于单页面应用程序,组件的更改和应用程序内部状态的更改应始终伴随着url的更改。
为什么要这样路由?
我认为,流行的前端Web应用程序框架中的路由功能受其与经典Web应用程序(带有服务器渲染)中路由的历史联系的影响。
最初,url是静态Web文档的网址,它非常简单。 接下来,开始将MVC架构适应Web:模型1和模型2。最后一个包括前端控制器,该控制器后来分为两部分:路由(用于选择所需的控制器)和控制器本身,它们与模型一起工作,以及渲染视图。 如您所见,在传统的Web应用程序中,路由确定操作(控制器),并间接(通过控制器)确定应在服务器上呈现的视图。
也就是说,桌面架构一次可以适应服务器上的经典Web应用程序,然后以路由的形式返回到Web应用程序的前端,该路由具有服务器端所需的功能。
通用路由器库提供什么?
通用路由器库可丢弃所有多余的内容,并且仅在客户端和Web服务器端(在通用/同构Web应用程序中)呈现时,保留可以使用或不使用任何框架的部分。
抛弃了所有的时间层之后,通用路由器仅提供一种明确定义的功能。 基于该行(我再次强调该行,而不是历史对象,位置等),调用异步函数,该函数将解析的url字符串作为实际参数传递。 仅此而已。 在React中的样子:
import React from 'react'; import UniversalRouter from 'universal-router'; import App from './App'; import Link from './Link'; const routes = { path: '/', async action({next}) { const children = await next(); return ( <App> {children} </App> ); }, children: [ { path: '', async action() { return ( <div>Root route go to <Link href='/test'>Test</Link></div> ); }, }, { path: '/test', async action({next}) { const children = await next(); return ( <App> {children} </App> ); }, children: [ { path: '', async action() { return ( <div>Test route return to <Link href='/'>Root</Link></div> ); }, }, ] }, ], }; export const basename = ''; const router = new UniversalRouter(routes, { baseUrl: basename }); export default router;
还支持嵌套路由。 它们在children字段中定义,您可以通过调用next()异步函数来获取它们。
以及如何与React一起使用?
定义历史记录的navigation()方法,尽管在许多情况下使用本机push()方法就足够了
import { createBrowserHistory } from 'history' import parse from 'url-parse' import deepEqual from 'deep-equal' const isNode = new Function('try {return this===global;}catch(e){return false;}')
我们还创建了将触发导航的Link组件:
import React from 'react'; import {basename} from './router'; import history from './history'; function noOp(){}; const createOnClickAnchor = (callback) => { return (e) => { e.preventDefault(); history.navigate(e.currentTarget.getAttribute('href')); callback(e); }; }; export default ({href, onClick = noOp, children, ...rest}) => ( <a href={basename + href} onClick={createOnClickAnchor(onClick)} {...rest} > {children} </a> );
现在您可以渲染组件了:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; import history from './history'; import router from './router'; const render = async (location) => { const element = await router.resolve(location); ReactDOM.render( element, document.getElementById('root'), ); }; render(history.location); history.listen(render);
项目代码有用的链接
1.
medium.com/@ippei.tanaka/universal-router-history-react-97ec79464573