系统的整理下肯定会有新的收获
前端路由是什么
路由这个概念最早出现在后端,后端用户请求URL导航到html页面。而前端路由不用服务器解析,可以通过hash函数和history API来实现,本质上是匹配前端组件的一种规则,也就是说整个过程只有一个页面,也就是我们说的单页面应用。
优点
- 不涉及页面跳转,过程服务器没有参与,减少服务器压力
- 前端组件之间切换,跳转流畅,用户体验好
- 页面效果酷炫(可以加页面转场动画)
- 组件化开发
缺点
- 首屏加载(FCP)慢(整个项目都在一起能不慢嘛)
- 不利于seo(spa是通过js加载来创建dom元素,爬虫只是抓去静态资源,不会执行js文件)
- 页面的复杂度会提高(修改单个页面都要发布整个项目)
哈希路由和历史路由
哈希路由(hash)模式
这里的hash就是url尾巴后的#号和后面的字符,本身可以作为页面的定位,使用对应id显示在可是区域内,hash变化不会的导致浏览器发请求,hash 会触发 hashchange事件,前进后退也能进行控制。
widow.addEventListener('hashChange', function() {})
history 模式
history在hash之后出现,相比hash穿参只能基于url,如果需要复杂数据,会有体积的限制,history不仅可以在url放参数,还可以放在一个特定的对象中。
如果不想要很丑的 hash,我们可以用路由的 history 模式
以掘金为例
React-router基础使用
react-router 简介 react包含三个库,react-router,react-router-dom,react-router提供最基础的路由功能,在使用时不直接使用react-router,根据运行的环境使用安装react-router-dom(浏览器中使用),react-router-native(rn中使用)。他们都依赖react-router,会自动安装,创建web应用。
BrowserRouter和HashRouter对比
- HashRouter最简单,不需要服务器段渲染,靠浏览器的#来区分path,BrowserRouter需要服务器对不同的url返回HTML,需要服务器配置。
- BrowserRouter使用HTML5的history API(pushState,replaceState,popState事件),让页面UI同步给URL
- Hash history不需要服务器配置就可以运行,最简单,如果你介意的项目有个丑的url,那么你应该使用browserHistory
常见的BrowserRouter配置:
- Nginx配置
// 这样所有的路径都会指向index.html 浏览器上的path,会自动被React-router处理,进行无刷新跳转。
server {
server_name react.thinktxt.com;
listen 80;
root /Users/txBoy/WEB-Project/React-Demo/dist;
index index.html;
location / {
try_files $uri /index.html;
}
}
- webpack-dev-server运行方式
// 直接在运行时加入参数“–history-api-fallback”
"scripts": {
"build": "webpack",
"dev": "webpack-dev-server --inline --devtool eval --progress --colors --hot --content-base ./build --history-api-fallback"
},
route渲染的三种方式
Route 渲染的优先级:children > component > render,三者都能接受route props,包括match,locaction 和 history,当不匹配时,children的match为null。
三种互斥只能选择一种
component
憨憨,给我啥我渲染啥
render
render的类型是function,Route会渲染这个function的返回值。因此它的作用就是附加一些额外的逻辑
<Route path="/home" render={() => {
console.log('额外的逻辑');
return (<div>Home</div>);
}/>
children
这是最特殊的渲染方式。一、它同render类似,是一个function。不同的地方在于它会被传入一个match参数来告诉你这个Route的path和location匹配上没有。二、第二个特殊的地方在于,即使path没有匹配上,我们也可以将它渲染出来。秘诀就在于前面一点提到的match参数。我们可以根据这个参数来决定在匹配的时候渲染什么,不匹配的时候又渲染什么。
// 在匹配时,容器的calss是light,<Home />会被渲染
// 在不匹配时,容器的calss是dark,<About />会被渲染
<Route path='/home' children={({ match }) => (
<div className={match ? 'light' : 'dark'}>
{match ? <Home/>:<About>}
</div>
)}/>
Route核心渲染代码
<RouterContext.Provider value={props}>
{props.match
? children
? typeof children === "function"
? __DEV__
? evalChildrenDev(children, props, this.props.path)
: children(props)
: children // 这里能看到优先级是children>component>render
: component
? React.createElement(component, props)
: render
? render(props)
: null
: typeof children === "function"
? __DEV__
? evalChildrenDev(children, props, this.props.path)
: children(props)
: null}
</RouterContext.Provider>
注意
如果使用component渲染时,Route指定的组件会使用React.createElement创建一个新的[React element],这意味着如果使用内联函数时,每次render都会创建一个新的组件。导致不再更新现有的组件,而是会重新挂载一个新的组件,这样会造成性能问题,所以如果使用内联函数,请使用render或者children。
404页面
作为路由匹配不上的展示页面
// switch是按照顺序来匹配
<Switch>
<Route path="/" component={homePage}></Route>
<Route path="/user" component={userPage}></Route>
<Route component={_404Page}></Route>
</Switch>
动态路由
使用::id的形式 获取:props.match.params 里面的参数
嵌套路由
Route组件嵌套在其他页面,比如列表跳转详情页面