单页Web应用(single page web application,SPA),就是只有一张Web页面的应用。是只加载单个HTML页面,并在用户与应用程序交互时动态更新该页面的Web应用程序。而React
、Vue
就是构建单页应用的前端主流框架。
对比 | 单页面应用 | 多页面应用 |
---|---|---|
组成 | 一个外壳页面和多个页面组件(片段)构成 | 多个完整的页面组成 |
资源(css,js) | 可共用,只需要在外壳部分加载 | 需要时向服务器请求,资源独立 |
首次加载 | 首屏加载慢 | 每次加载区别不大 |
用户体验 | 用户体验好,内容改变不需要重新加载整个页面,后续服务器压力小 | 页面加载慢,每次加载需要对服务器进行请求 |
转场动画 | 可以实现 | 无法实现 |
搜索引擎优化(SEO) | 效果较差,可通过SSR服务端渲染,成本较高 | 效果好 |
通过监听路由的变化,去匹配路由所对应的组件,并将组件映射到路由上。当路由改变时框架能有效地更新并正确地渲染组件。
这里我介绍HashRouter的实现原理、过程
React Router中有三类组件:
对应路由的两种模式hash和history
控制路径对应的显示组件
路由切换,跳转
以一个demo为例
export default class App extends Component { render() { return ( <HashRouter> <Route path="/home" component={Home}></Route> <Route path="/user" component={User}></Route> </HashRouter> ) } } ReactDOM.render(<App />, document.getElementById('root') ); 复制代码
这里Route组件获得了对应的路径和所要展示的组件,并嵌套在Router组件中。
首先我们要知道BOM的一些特性
BOM
是一套操作浏览器的API,而window是BOM中的一个顶级的对象,我们可以通过this.props打印挂载在window下的一些信息,以掘金为例
而HashRouter的实现就依赖于这些Api,我们可以通过window.location.href
拿到我们所在的url值。
当然通过这个就可以了吗?等等,Route作为HashRouter的嵌套组件是怎么拿到url路径去匹配path的呢?
这里React-Router采用了React16.3版本提出的api React.createContext
可以解决父组件向子组件、孙子孙子...传值,为多组件嵌套数据传递提供解决方案。
//我们通过一个context.js方法引入这个api import React from 'react'; let { Provider, Consumer } = React.createContext() export { Provider, Consumer } 复制代码
而HashRouter便充当了这个生产者的角色,通过window.addEventListener('hashChange',callback)
监听hash值的变化,并传递给其嵌套的组件。
具体代码如下:
import React, { Component } from 'react'; import { Provider } from './context' // 该组件下Api提供给子组件使用 class HashRouter extends Component { constructor() { super() this.state = { location: { pathname: window.location.hash.slice(1) || '/' } } } // url路径变化 改变location componentDidMount() { window.location.hash = window.location.hash || '/' window.addEventListener('hashchange', () => { this.setState({ location: { ...this.state.location, pathname: window.location.hash.slice(1) || '/' } }, () => console.log(this.state.location)) }) } render() { let value = { location: this.state.location } return ( <Provider value={value}> { this.props.children } </Provider> ); } } export default HashRouter; 复制代码
Route组件则充当的消费者的角色,通过一个回调接收到HashRouter传递过来的url路径值,并进行后面的匹配渲染组件
import React, { Component } from 'react'; import { Consumer } from './context' const { pathToRegexp } = require("path-to-regexp"); class Route extends Component { render() { return ( <Consumer> { state => { console.log(state) let {path, component: Component} = this.props let pathname = state.location.pathname let reg = pathToRegexp(path, [], {end: false}) // 判断当前path是否包含pathname if(pathname.match(reg)) { return <Component></Component> } return null } } </Consumer> ); } } export default Route; 复制代码
由于官方实现的正则表达式较为复杂,这里我借助了path-to-regexp
这个插件去进行正则匹配的处理。
实现效果:
本次只是简单的模拟了下HashRouter的实现过程,对React-Router的实现原理也有了一定的认识。最后,学习的过程,重在总结,乐在分享,具体的代码大家可以看我的github 欢迎大家留言和我交流分享😀。