1、路由
当应用变得复杂的时候,就需要分块的进行处理和展示。
传统方式的处理
: 把整个应用分成了多个页面,然后通过 URL 进行连接。
传统方式的弊端
:
2、单页面应用
单页面应用SPA(Single Page Application)
: 整个应用只加载一个页面(入口页面),
后续在与用户的交互过程中,通过 DOM 操作在这个单页上动态生成结构和内容。
**单页面应用优缺点
**如下:
优点 | 缺点 |
---|---|
有更好的用户体验(减少请求、渲染以及页面跳转等待),页面切换快 | 首次进入页面处理比较慢(请求减少,在同一页面内容就增多了,加载时间随之增加) |
数据和页面内容由异步请求(AJAX)+ DOM 操作来完成,前端处理更多的业务逻辑 | 不利于搜索引擎优化(SEO) |
3、SPA 的页面切换机制
虽然 SPA 的内容都是在一个页面通过 JavaScript 动态处理的,但是还是需要根据需求在不同的情况下分内容展示,如果仅仅只是依靠 JavaScript 内部机制去判断,逻辑会变得过于复杂,通过把 JavaScript 与 URL 进行结合的方式:JavaScript 根据 URL 的变化,来处理不同的逻辑,交互过程中只需要改变 URL 即可。这样把不同 URL 与 JavaScript 对应的逻辑进行关联的方式就是路由,其本质上与后端路由的思想是一样的。
4、前端路由
前端路由只是改变了 URL 或 URL 中的某一部分,但一定不会直接发送请求,可以认为仅仅只是改变了浏览器地址栏上的 URL 而已,JavaScript 通过各种手段处理这种 URL 的变化,然后通过 DOM 操作动态的改变当前页面的结构
前端路由特点
:
目前前端路由主要的模式
:
#
,其他没有什么差别;React Router
基于 web 的 React Router 为:react-router-dom
安装
npm i -S react-router-dom
BrowserRouter 组件
基于 HTML5 History API 的路由组件
HashRouter 组件
基于 URL Hash 的路由组件
示例:简单的tab选项卡
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>hash值的使用-简单tab选项卡</title>
</head>
<body>
<a href="#first">one</a>
<a href="#second">two</a>
<a href="#third">three</a>
<div id="box"></div>
<script>
function getHash(params) {
console.log(window.location.hash);
box.innerHTML = window.location.hash;
}
// 监听hashchange事件,当hash值发生改变时,调用getHash方法
window.addEventListener("hashchange",getHash);
</script>
</body>
</html>
Route 组件
通过该组件来设置应用单个路由信息,Route 组件所在的区域就是就是当 URL 与当前 Route 设置的 path 属性匹配的时候,后面 component 将要显示的区域
示例:
src/view/index.js
src/view/about.js
src/App.js
import React from 'react';
//index.js
export default function Index(){
return (<h1>首页</h1>);
}
import React from 'react';
//about.js
export default function About(){
return (<h1>关于我们</h1>);
}
import React from 'react';
import {Route} from 'react-router-dom';
import Index from './view/index';
import About from './view/about';
//App.js
export default function App(){
return (
<div>
<Route path="/" component={Index} />
<Route path="/about" component={About} />
<Route path="/about/more" component={More}/>
</div>
);
}
运行后,浏览器控制台报错如下:
翻译一下:未捕获错误:不变式失败:你不应该在一个<路由器>之外使用<路由>
上诉问题,
解决第一种方式:可以在入口文件index.js中的外层包裹一层容器HashRouter或者BrowserRouter,
修改如下:
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
// 入口文件引入HashRouter或者BrowserRouter
// import {HashRouter} from 'react-router-dom';
import {BrowserRouter} from 'react-router-dom';
import App from './App';
//index.js入口文件
ReactDOM.render(
// 最顶层需要容器包着
// 使用HashRouter或者BrowserRouter包裹都可以,表明路由的类型。
//
//
// ,
<BrowserRouter>
<App/>
</BrowserRouter>,
document.getElementById('root')
);
或者第二种方式:不在入口文件index.js中修改,
直接在父级组件App.js文件中,给路由外层添加一个容器,如下:
return (
<div>
{/* 最外层需要容器包裹路由,表明路由的类型*/}
<BrowserRouter>
<Route path="/" component={Index} />
<Route path="/about" component={About} />
<Route path="/about/more" component={More}/>
</BrowserRouter>
</div>
);
再次运行后
看截图虽然控制台没报错,但是总感觉哪里不对劲呢?
我们看浏览器地址栏出现的是localhost:3000/about/more,即本应跳转到详情页的,显示详情页内容。
但是"首页"与"关于我们"的内容也一起显示出来了,这是咋回事?
因为在路由path中,匹配url的方式不是相等,而是以指定的url开头
如上例,三者都是以/
开头的,都符合条件,所以导致最后输入详情地址时,三个路由页面内容都会出现。我们假设详情后还有内容,我们要访问它的路由,然而结果是前面路由对应的内容全都出来了,这就很不友好了!如果页面布局内容较多,最后会变成啥样我们也不知道!这该咋办呢?
exact属性
两种匹配模式
精确匹配模式
/
匹配所有以 /
开头的路由。类似于模糊匹配,前面的问题就是这种情况导致的!所以,针对上述问题,我们改为精确匹配模式,如下:
{/* exact精确匹配 在component前加上exact,每一个路由都要加*/}
<Route path="/" exact component={IndexPage} />
<Route path="/about" exact component={AboutPage} />
<Route path="/about/more" exact component={AboutMorePage} />
看看运行效果吧
ok,已经精确匹配到url了,即对应的路由跳转到对应的页面。
首页
localhost:3000/
关于我们
localhost:3000/about
详情
localhost:3000/about/more
Link 组件
Link 组件用来处理 a 链接 类似的功能。它会在页面中生成一个 a 标签。
设置需注意:
to 属性
Link 组件有一个to 属性,它类似 a 标签中的 href
src/component/nav.js
import React from 'react';
import { Link } from 'react-router-dom';
export default function Nav() {
return (
<nav>
<Link to="/">首页</Link>
<span>|</span>
<Link to="/about">关于我们</Link>
<span>|</span>
<Link to="/more">详情</Link>
</nav>
)
}
src/App.js 添加如下
import Nav from './component/nav';
{/* Link组件,只改变url */}
<Nav />
{/* path属性,匹配url
exact精确匹配 在component前加上exact,每一个路由都要加*/}
<Route path="/" exact component={IndexPage} />
<Route path="/about" exact component={AboutPage} />
<Route path="/about/more" exact component={AboutMorePage} />
render属性
如果 Route 使用的是 component 来指定组件,那么就不能使用 props,也就是说无法给组件传参。
别慌!Route有一个render属性来帮忙!
通过 render 属性来指定渲染函数,render 属性值是一个函数,当路由匹配的时候指定该函数进行渲染
src/App.js修改如下
funtion App(){
<div>
let [shop,setShop] = useState("烤烟再起");
return (
{/* Link组件,只改变url */}
<Nav />
{/* render传值 */}
<Route path="/" exact render={()=>{
// return 你要显示的视图
return <IndexPage shop={shop} setShop={setShop}/>
}}/>
<Route path="/about" exact component={AboutPage} />
<Route path="/more" exact component={AboutMorePage} />
</div>
)
view/index.js修改
import React from 'react';
export default function IndexPage(props){
let {shop,setShop} = props;//父组件传过来的参数
console.log(shop);
return (
<h1><a onClick={()=>{setShop('星星点灯')}}
style={{cursor:'pointer'}}>hello
</a>
首页
</h1>
);
}
NavLink组件
NavLink 与 Link 类似,但是它提供了两个特殊属性用来处理页面导航。
src/component/nav.js修改如下:
import React from 'react';
import { NavLink } from 'react-router-dom';
export default function Nav() {
return (
<nav>
{/*有个问题:当点击首页之后,再点击关于或者详情时,
前面给组件添加的类名属性及样式属性还在,需要在添加的属性前加上exact,
使用精确匹配,即只有点击谁,谁才添加,不点击就不添加 */}
<NavLink to="/" exact activeClassName="active-index"
activeStyle={{ color: 'red' }}>首页</NavLink>
<span>|</span>
<NavLink to="/about" exact activeClassName="active-about"
activeStyle={{ color: 'red' }}>关于我们</NavLink>
<span>|</span>
<NavLink to="/more" exact activeClassName="active-more"
activeStyle={{ color: 'red' }}>详情</NavLink>
</nav>
)
}