在看啦一遍源码后,发现无论出什么问题均可以轻松找到缘由啦,哇咔咔javascript
安装http-server
,react-router-dom
,path-to-regexp
将路径转换为正则css
目的当你点击不一样路径,就渲染不一样组件html
实现方式java
最先客户端实现路由靠锚点,实现切换页面不刷新,react
应用方法,对hashchange
进行监听,这个是浏览器自带的哦~!bootstrap
//浏览器自带
window.addEventListener('hashchange',(event)=>{
console.log(event);
})
复制代码
利用h5 APi 实现 history对象,它提供啦操做浏览器绘画历史的接口,追踪一组location,而且保存索引周,指向当前做用的locationapi
从react-router-dom
引入Route(路由规则)和Router(容器)数组
src/index.js浏览器
import React from 'react';
import ReactDOM from 'react-dom';
import {HashRouter as Router,Route,Link,Switch,Redirect,Forward,Back} from './react-router-dom';
import Home from './components/Home';
import User from './components/User';
import Protected from './components/Protected';
import Profile from './components/Profile';
import Login from './components/Login';
import MenuLink from './components/MenuLink';
import NavHeader from './components/NavHeader';
import 'bootstrap/dist/css/bootstrap.css';
import './components/MenuLink.css';
//一个路径对应一个组件
ReactDOM.render(
<Router>
<div>
<nav>
<div className="navbar navbar-inverse">
<div className="container-fluid">
<NavHeader />
<div>
<ul className="nav navbar-nav">
<MenuLink to='/' exact={true} lable="首页" />
<MenuLink to='/user' lable="用户管理" />
<MenuLink to='/profile' lable="我的设置" />
</ul>
</div>
</div>
</div>
</nav>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/user" component={User}/>
<Route path="/login" component={Login}/>
<Protected path="/profile" component={Profile}/>
<Redirect to="/"/>
</Switch>
</div>
</Router>
, document.getElementById('root'));
复制代码
Route两个属性,path对应路径, component对应组件 exact 是否绝对匹配, Router只能拥有一个子组件react-router
component对应组件的props属性有
这些属性是Router传过来的,靠contxt中的(provider提供者,Consumer消费者)传递(react.6.3)他们是由 React.creatContext()
建立
咱们要实现 Home User Protected Profile Login MenuLink NavHeader 和 react-router-dom里的 HashRouter, Route,Link,Switch,Redirect,Forward,Back的功能
components/Home主页
import React , {Component} from 'react';
export default class Home extends Component{
render(){
return (
<div>Home</div>
)
}
}
复制代码
components/Profile授权限页面
import React,{Component} from 'react';
export default class Profile extends Component{
render() {
return (
<div>Profile</div>
)
}
}
复制代码
Protected受保护路由
import React,{Component} from 'react'
import {Route,Redirect} from '../react-router-dom';
// rest = {path,exact,,xxxxxx}
//返回路由,执行render,返回组件,或者重定向 to里面放对象或者路径
export default function ({component: Component,...rest}) {
return (
<Route {...rest} render={props => (
localStorage.getItem('logined')? <Component {...props} />:<Redirect to={{pathname: '/login',state: {from:props.location.pathname}}}/>
)}/>
)
}
复制代码
components/User用户界面
import React,{Component} from 'react';
import {Link,Route,Back,Forward} from '../react-router-dom';
import UserAdd from './UserAdd';
import UserList from './UserList';
import UserDetail from './UserDetail';
export default class User extends Component{
componentWillMount() {
console.log('User componentWillMount');
}
componentDidMount() {
console.log('User componentDidMount');
}
render() {
return (
<div className="row">
<div className="col-md-2">
<ul className="nav nav-stacked">
<li><Link to="/user/add">新增用户</Link></li>
<li><Link to="/user/list">用户列表</Link></li>
<li><Back /></li>
<li><Forward /></li>
</ul>
</div>
<div className="col-md-10">
<Route path="/user/add" component={UserAdd} />
<Route path="/user/list" component={UserList} />
<Route path="/user/detail/:id" component={UserDetail}/>
</div>
</div>
)
}
}
复制代码
components/UserAdd添加用户界面
有跳转功能
import React , {Component} from 'react';
import api from './api';
import {Prompt} from '../react-router-dom'
export default class UserAdd extends Component{
state={
isBlocking: false
}
componentWillUpdate(nextProps, nextState) {
console.log('Component WILL UPDATE!');
console.log(nextProps, nextState);
}
componentDidUpdate(prevProps, prevState) {
console.log('Component DID UPDATE!');
console.log(prevProps, prevState);
}
handleSubmit=(event) => {
this.setState({isBlocking: false},()=>{
event.preventDefault();
let username=this.username.value;
let email=this.email.value;
let user={username,email};
api.createUser(user);
this.props.history.push('/user/list');
})
}
render(){
// 提交表单,提交到跳页面
return (
<form onSubmit={this.handleSubmit}>
<Prompt
when = {this.state.isBlocking}
message={
loc =>`请问你是否要切换到${loc.pathname}`
}
/>
<div className="form-group">
<label htmlFor="username" className="control-label">用户名</label>
<input
onChange={()=> this.setState({isBlocking:true})}
ref={input=>this.username = input} type="text" className="form-control"/>
</div>
<div className="form-group">
<label htmlFor="email" className="control-label">邮箱</label>
<input
onChange={()=> this.setState({isBlocking:true})}
ref={input=>this.email = input} type="text" className="form-control"/>
</div>
<div className="form-group">
<input type="submit" className="btn btn-primary"/>
</div>
</form>
)
}
}
复制代码
components/UserList用户列表展现页
import React , {Component} from 'react';
import api from './api';
import {Link} from '../react-router-dom';
import 'bootstrap/dist/css/bootstrap.css';
export default class UserList extends Component{
state={
users:[]
}
componentWillMount(){
//读取用户
let users = api.getUsers();
this.setState({users});
}
handleDelete =(id)=>{
let users = api.delUser(id);
this.setState({users});
}
render(){
return (
<table className="table table-bordered"> <thead> <tr> <th>ID</th> <th>用户名</th> <th>操做</th> </tr> </thead> <tbody> { this.state.users.map(user => ( <tr key={user.id}> {/* <td><Link to={`/user/detail/${user.id}`}>{user.id}</Link></td> */} <td> <Link to={{pathname:`/user/detail/${user.id}`,state:user}}> {user.id} </Link> </td> <td>{user.username}</td> <td> <button onClick={()=>this.handleDelete(user.id)} className="btn btn-danger">删除</button> </td> </tr> )) } </tbody> </table>
)
}
}
复制代码
components/menuLink菜单
import React,{Component} from 'react'
import {Route,Link} from '../react-router-dom';
import './MenuLink.css'
//渲染Route有三种方式 component render children
//何时用函数组件,若是不须要state尽可能用函数组件,简洁,可控
export default ({to,exact=false,label}) => (
<Route path={to} exact={exact} children={ ({match}) => <li className={match? 'active':''}><Link to={to}>{label}</Link></li> }/> ) 复制代码
components/navHeader导航
import React,{Component} from 'react'
import {withRouter} from '../react-router-dom';
class NavHeader extends Component{
render() {
return (
<div className="navbar-header"> <a onClick={()=>this.props.history.push('/')} className="navbar-brand">管理系统</a> </div>
)
}
}
//NavHeader原本是一个普通的组件,跟Route没有关系
export default withRouter(NavHeader);
复制代码
components/UserDetail 用户详细信息页
import React , {Component} from 'react';
import api from './api';
export default class UserDetail extends Component{
state={
user:{}
}
componentDidMount(){
let user = this.props.location.state.user;
console.log(user);
if(!user){
let id = this.props.match.params.id;
user = api.getUser(id);
}
this.setState({user});
}
render(){
let user = this.state.user;
return (
<div> <p>ID:{user.id}</p> <p>用户名:{user.username}</p> <p>邮箱:{user.email}</p> </div>
)
}
}
复制代码
components/api.js存储添加用户使用的方法
let userApi = {
//获取全部用户
getUsers(){
let usersStr = localStorage.getItem('users');
return usersStr? JSON.parse(usersStr) :[];
},
//建立用户
createUser(user){
let users = userApi.getUsers();
user.id = users.length>0? users[users.length-1].id + 1:1;
users.push(user);
localStorage.setItem('users',JSON.stringify(users));
},
getUser(id){
return userApi.getUsers().find(user => user.id == id)
},
delUser(id){
let users = userApi.getUsers().filter(user => user.id != id)
localStorage.setItem('users',JSON.stringify(users));
return users;
}
}
export default userApi;
复制代码
react-router-dom/index.js
import HashRouter from './HashRouter';
import Route from './Route';
import Link from './Link';
import Switch from './Switch';
import Redirect from './Redirect';
import Forward from './Forward';
import Back from './Back';
import withRoter from './withRoter';
import Prompt from './Prompt';
export {HashRouter,Route,Link,Switch,Redirect,Back,Forward,withRoter,Prompt}
复制代码
hashrouter.js hashrouter是个返回容器的组件
import React , {Component} from 'react';
import { Provider } from './context';
//每当地址栏的锚点发生变化的时候,都须要从新配置
export default class HashRouter extends Component{
state={
location:{
pathname:window.location.hash?window.location.hash.slice(1):'/'
}
}
componentDidMount(){
window.addEventListener('hashchange',()=>{
this.setState({
location:{
...this.state.location,
pathname:window.location.hash?window.location.hash.slice(1):'/'
}
})
})
}
render(){
let that=this;
let value = {
location:that.state.location,
history:{
//value会用Provider传递给Consumer,从而使用用这里定义方法
push(to){
if(that.block){
//保证to是对象
let ok = window.confirm(that.block(typeof to === 'object' ? to:{pathname:to}));
if(!ok){
return;
}
}
// if(that.unblock){
// window.confirm(that.block)
// }
if(typeof to === 'object'){
let {pathname,state} = to;
that.setState({
...that.state,
location:{
...that.state.location,
pathname,
state
}
},()=>{
window.location.hash = pathname;
})
}else{
window.location.hash = to;
}
},
goback(){
window.history.go(-1);
},
forward(){
window.history.go(1);
},
//弹窗方法
block(message){
that.block = message;
},
unblock(message){
that.block = null;
}
}
}
return(
// 想传递数据用Provider,接收数据用Consumer
<Provider value={value}>
{/* children就是Route */}
{this.props.children}
</Provider>
)
}
}
复制代码
react-router-dom/Route.js
import React,{Component} from 'react';
import {Consumer} from './context';
import pathToRegexp from 'path-to-regexp';
//每当地址栏的锚点发生变化的时候,都须要从新配置
//pathToRegexp('要转换的路径',参数,是否结束)
export default class Route extends Component{
render() {
return (
<Consumer> { // 接收属性 value => { let {location: {pathname}}=value;// /user let {path="/",component: Component,exact=false,render,children}=this.props; let keys=[];//[id] //匹配路径 let regexp=pathToRegexp(path,keys,{end: exact}); let result=pathname.match(regexp); let props={ location: value.location, history:value.history } if(result){ let [,...values]=result; keys=keys.map(key => key.name); let params = keys.reduce((memo,name,index) => { memo[name]=values[index]; return memo; },{}); //matchv {path,url,isExact,params } let match={ url:pathname, path, params } props.match=match; //prototype组件,若是render存在,则执行 //渲染Route有三种方式 component render children if (Component) { return <Component {...props}/>; } else if (render) { return render(props); } else if (children) { return children(props); } else { return null; } } else { if (children) { return children(props); } else { return null; } } } } </Consumer> ) } } 复制代码
/Link.js
import React , {Component} from 'react';
import {Consumer} from './context';
//每当地址栏的锚点发生变化的时候,都须要从新配置
export default class Link extends Component{
render(){
return(
<Consumer> { value => { let {history: {push}} = value; return <a onClick={()=>push(this.props.to)}>{this.props.children}</a> } } </Consumer>
)
}
}
复制代码
switch.js匹配优化
import React,{Component} from 'react';
import {Consumer} from './context';
import pathToRegexp from 'path-to-regexp';
export default class Switch extends Component{
render(){
return (
<Consumer> { value =>{ let {location:{pathname}} = value; let children = this.props.children; for(let i=0;i<children.length;i++){ let child=children[i]; //path的默认值为/ exact默认值为false,非精确匹配 /user/1 let {path="/",exact=false}=child.props;//:id let reg=pathToRegexp(path,[],{end: exact}); if(reg.test(pathname)){ return child; } } return null; } } </Consumer> ) } } 复制代码
Redirect.js重定向
import React , {Component} from 'react';
import {Consumer} from './context';
export default class Redirect extends Component{
render(){
return(
<Consumer> { value=>{ value.history.push(this.props.to); return null; } } </Consumer>
)
}
}
复制代码
Back.js
import React , {Component} from 'react';
import {Consumer} from './context';
export default class Back extends Component{
render(){
return (
<Consumer> { value => { return <a onClick={() => value.history.goback()}>返回</a>; } } </Consumer>
)
}
}
复制代码
Forward.js
import React , {Component} from 'react';
import {Consumer} from './context';
export default class Back extends Component{
render(){
return (
<Consumer> { value => { return <a onClick={()=>value.history.forward()}>前进</a>; } } </Consumer>
)
}
}
复制代码
withRoter 将Component 和router创建连接
import React from 'react';
import { Route } from '../react-router-dom';
//<Route component = {Component} />是实例,不能被直接返回
export default (Component) => () => <Route component = {Component} />
复制代码
Prompt.js防止条状弹出框
import React,{Component} from 'react'
import {Consumer} from './context';
export default class Prompt extends Component{
componentWillUnmount() {
this.history.unblock();
}
render() {
return (
<Consumer> { value => { this.history=value.history; let {when,message}=this.props; if (when) { value.history.block(message); } else { value.history.unblock(); } } } </Consumer>
)
}
}
复制代码
import React,{Component} from 'react'
import ReactDOM from 'react-dom';
class Panel extends Component{
render() {
return (
<div className="panel panel-default"> <div className="panel-heading">头部</div> <div className="panel-body"> { this.props.children('面板') } </div> </div>
)
}
}
ReactDOM.render(<div> <Panel> {(text) => <div style={{color:'red'}}>{`我是${text}`}</div>} </Panel> <Panel> {(text) => <div style={{color:'green'}}>{`我是${text}`}</div>} </Panel> </div>,document.querySelector('#root'));
复制代码
同理
import React,{Component} from 'react'
import ReactDOM from 'react-dom';
//let {Provider,Consumer}=React.createContext();
class Provider extends Component{
render() {
let value=this.props.value;
//拿到consumer
let children=this.props.children;
children = children.map(child => {
//判断consumer类型
if (child.type.toString().includes('Consumer'))
//cloneElement能够返回就属性的props,也能够继承新的props
return React.cloneElement(child,{value});
else
return child;
});
return <div>{children}</div>;
}
}
class Consumer extends Component{
render() {
return this.props.children(this.props.value);
}
}
ReactDOM.render((
<Provider value={1}> <Consumer> { value => <div>{value}</div> } </Consumer> <div>2</div> </Provider>
),document.querySelector('#root'));
大功告成长城,不懂得留言哦~复制代码