1、分析路由自己实现需要满足的是:
(1)改变URL,但是页面不要进行强制刷新(a标签会进行页面的自动刷新)
(2)自己来监听URL的改变,并且改变之后自己改变页面的内容
2、监听hash来实现
hase的特点是再url后加上#
设置a标签如下:
<div id="app">
<a href="#/home">首页</a>
<a href="#/about">关于</a>
<div class="router-view"></div>
</div>
js代码监听url如下:通过location.hash来获取url#所带的值。
// 获取router-view的DOM
const routerViewEl = document.getElementsByClassName("router-view")[0];
// 监听URL的改变
window.addEventListener("hashchange", () => {
switch (location.hash) {
case "#/home":
routerViewEl.innerHTML = "首页";
break;
case "#/about":
routerViewEl.innerHTML = "关于";
break;
default:
routerViewEl.innerHTML = "";
}
})
3、监听history来实现
给a标签绑定点击事件,并且在其中阻止默认行为。每一次点击就获取a标签的href属性,再将src属性放到history之中,再用location.pathname监听url的改变。将监听到url的部分封装一个函数,放入到a点击事件之中,可以在a标签的每一次进行点击的时候都调用监听函数。需要注意的是,进行点击之后,返回之后div内的内容不会改变,所以要添加一个监听函数当返回时也调用监听函数。
<body>
<div id="app">
<a href="/home">首页</a>
<a href="/about">关于</a>
<div class="router-view"></div>
</div>
<script>
// 1.获取router-view的DOM
const routerViewEl = document.getElementsByClassName("router-view")[0];
// 获取所有的a元素, 自己来监听a元素的改变
const aEls = document.getElementsByTagName("a");
for (let el of aEls) {
el.addEventListener("click", e => {
e.preventDefault();
const href = el.getAttribute("href");
history.pushState({}, "", href);
urlChange();
})
}
// 执行返回操作时, 依然来到urlChange
window.addEventListener('popstate',urlChange);
// 监听URL的改变
function urlChange() {
switch (location.pathname) {
case "/home":
routerViewEl.innerHTML = "首页";
break;
case "/about":
routerViewEl.innerHTML = "关于";
break;
default:
routerViewEl.innerHTML = "";
}
}
</script>
</body>
4、router具体运用
(1)安装:
npm install react-router-dom
(2)
监听hash来实现=>对应BrowserRouter
监听history来实现=>对应HashRouter
Link:对应a标签,Route匹配Link展示组件。
<BrowserRouter>
<Link to="/">FIRST</Link>
<Link to="/about">about</Link>
<Link to="/home">home</Link>
<Route path="/" component={Profile}/>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</BrowserRouter>
(3)模糊匹配:
点击Link的时候去匹配Route,to="/about"被模糊匹配所以path="/“也会被匹配到。也就是点击to=”/about"、to="/home"都会显示 path="/"匹配的组件。要实现精准匹配如下:加上exact
<Route exact path="/" component={Profile}/>
(4)NavLink
NavLink相比Link可以有activeStyle属性来设置选中时的css样式
<NavLink to="/about" activeStyle={{color: "red", fontSize: "30px"}}>
也可以自己定义NavLink的选中样式名,在css之中指定选中的样式
<NavLink exact to="/" activeClassName="link-active">首页</NavLink>
NavLink的样式也会存在模糊匹配
所以需要在NavLink中加入exact
(4)排他操作
Link匹配Rout的过程是匹配到合适的不会停止,会将所有能匹配的都显示出来。如下:
如果点击to="/about"的Link,匹配到的会有About和User,Route之中不写path时任何Link都可以匹配,算是一个兜底的存在。但是如果前面已经有匹配但是不会停止还会一直匹配,所以也会匹配大User.要进行排他的匹配可以使用到Switch
<NavLink exact to="/" activeClassName="link-active">首页</NavLink>
<NavLink to="/about" activeClassName="link-active">关于</NavLink>
<NavLink to="/profile" activeClassName="link-active">我的</NavLink>
<NavLink to="/abc" activeClassName="link-active">abc</NavLink>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route component={User} />
</Switch>
(5)Redirect使用
前面的Link都需要交互点击才能跳转,Redirect可以自动进行跳转,使用场景是用户页面需要先判断是否已经登录,如果没有登录则就跳转到相关的loading的组件
user组件:
import React, { PureComponent } from 'react'
import { Redirect } from 'react-router-dom';
export default class User extends PureComponent {
constructor(props) {
super(props);
this.state = {
isLogin: true
}
}
render() {
return this.state.isLogin ? (
<div>
<h2>User</h2>
<h2>用户名: coderwhy</h2>
</div>
): <Redirect to="/login"/>
}
}
user父组件app.js之中包括所有的Route
<NavLink exact to="/" activeClassName="link-active">首页</NavLink>
<NavLink to="/about" activeClassName="link-active">关于</NavLink>
<NavLink to="/profile" activeClassName="link-active">我的</NavLink>
<NavLink to="/abc" activeClassName="link-active">abc</NavLink>
<NavLink to="/user" activeClassName="link-active">用户</NavLink>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route path="/user" component={User} />
<Route path="/login" component={Login} />
<Route component={NoMatch} />
</Switch>
(6)路由的嵌套:
在about之中再嵌套路由,让路径是这种格式/about/culture。嵌套的路由需要写在这个组件里而不是写在父组件内。而且由于Switch只匹配一次,所以Rout path="/about"需要是exact。并且NavLink也需要设置exact,因为模糊匹配/about,都可以匹配上。
<NavLink exact to="/about" activeClassName="about-active">企业历史</NavLink>
<NavLink exact to="/about/culture" activeClassName="about-active">企业文化</NavLink>
<NavLink exact to="/about/contact" activeClassName="about-active">联系我们</NavLink>
<Switch>
<Route exact path="/about" component={AboutHisotry}/>
<Route path="/about/culture" component={AboutCulture}/>
<Route path="/about/contact" component={AboutContact}/>
</Switch>
(7)手动实现跳转
上面的Link都是a标签,如果是图片点击或者按钮点击进行跳转就没办法运用Link,Rout是检测的URL的变化,可以自己实现一个点击函数在其中修改url就可以进行跳转。
下面这个Route操作对About组件有进行相关的操作,就例如传递封装过的history
<Route path="/about" component={About} />
再About之中可以手动实现跳转(props传递了history)
import React, { PureComponent } from 'react'
import { Route } from 'react-router-dom';
export default class About extends PureComponent {
render() {
return (
<div>
<button onClick={e => this.jumpToJoin()}>加入我们</button>
<Route exact path="/about" component={AboutHisotry}/>
<Route path="/about/join" component={AboutJoin}/>
</div>
)
}
jumpToJoin() {
// console.log(this.props.history);
// console.log(this.props.location);
// console.log(this.props.match);
this.props.history.push("/about/join");
}
}
需要注意的是需要Route之中处理的组件才有传入封装的history,如果在App.js使用手动跳转就没有这个history。此时需要使用到react-router-dom之中的withRouter高阶组件。
export default withRouter(App);
(8)路由传参
①通过url进行传参:
<NavLink to={`/detail2?name=why&age=18`} activeClassName="link-active">详情2</NavLink>
②更推荐如下传:
const info = {name: "why", age: 18, height: 1.88};
<NavLink to={{
pathname: "/detail3",
search: "name=abc",
state: info
}}
activeClassName="link-active">
详情3
</NavLink>
(9)react-router-config的使用
在前面需要有大段的Route来进行路由组件映射,使用react-router-config可以更加简便。
yarn add react-router-config
创建一个router文件夹下面只创建一个index.js文件编写如下:
import Home from '../pages/home';
import About, { AboutHisotry, AboutCulture, AboutContact, AboutJoin } from '../pages/about';
import Profile from '../pages/profile';
import User from '../pages/user';
const routes = [
{
path: "/",
exact: true,
component: Home
},
{
path: "/about",
component: About
},
{
path: "/profile",
component: Profile
},
{
path: "/user",
component: User
}
]
export default routes;
原本在组件之中写Route的地方改为如下:
{renderRoutes(routes)}
但是如果是About之下的二级路由在index.js之中这样如下表示:
import Home from '../pages/home';
import About, { AboutHisotry, AboutCulture, AboutContact, AboutJoin } from '../pages/about';
import Profile from '../pages/profile';
import User from '../pages/user';
const routes = [
{
path: "/",
exact: true,
component: Home
},
{
path: "/about",
component: About,
routes: [
{
path: "/about",
exact: true,
component: AboutHisotry
},
{
path: "/about/culture",
component: AboutCulture
},
{
path: "/about/contact",
component: AboutContact
},
{
path: "/about/join",
component: AboutJoin
},
]
},
{
path: "/profile",
component: Profile
},
{
path: "/user",
component: User
}
]
export default routes;
需要注意的是二级路由的Rout处如下表示:
下方的this.props.route是index.js文件之中的如下对象
{
path: "/about",
component: About,
routes: [
{
path: "/about",
exact: true,
component: AboutHisotry
},
{
path: "/about/culture",
component: AboutCulture
},
{
path: "/about/contact",
component: AboutContact
},
{
path: "/about/join",
component: AboutJoin
},
]
},
所以如下就可以获取到二级路由信息
renderRoutes(this.props.route.routes)