内容简介:安装目的当你点击不同路径,就渲染不同组件实现方式
安装 http-server
, react-router-dom
, path-to-regexp
将路径转换为正则
目的当你点击不同路径,就渲染不同组件
实现方式
- hashrouter
- BrowserRouter
hashrouter
最早客户端实现路由靠锚点,实现切换页面不刷新,
应用方法,对 hashchange
进行监听,这个是浏览器自带的哦~!
//浏览器自带 window.addEventListener('hashchange',(event)=>{ console.log(event); }) 复制代码
BrowserRouter
利用h5 APi 实现 history对象,它提供啦操作浏览器绘画历史的接口,追踪一组location,并且保存索引周,指向当前作用的location
跑通路由
从 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只能拥有一个子组件
component对应组件的props属性有
- history
- location {hash,pathname,search,state}
- match {path,url,isExact,params }
这些属性是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组件
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 源码
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.jshashrouter是个返回容器的组件
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> ) } } 复制代码
Consumer,provider原理
组件复用两大策略
- 高阶组件
- 函数组委子组件
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')); 大功告成长城,不懂得留言哦~复制代码
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- libmodbus源码分析(3)从机(服务端)功能源码分析
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。