然而这样确实有他的道理。假使上课的有1000人,那应该通宵开发出来。而在没有那么多人的情况下,也许还能在用几年。
事实上真正的技术驱动公司还是少之又少,即便有这样的岗位,也是为大牛准备的。而前端缺乏改进产品的核心竞争力,在工作中出于一个更加弱势的地位。
当这个职业的红利期结束,行业开始回归沉淀,其实你会发现,所谓的大前端思维,其实是非常局限的。
最新版本已经是5.0了。不过本文用例均可跑通。
安装:
npm install --save react-router-dom
npm install --save react-router
先引入最常用的三个依赖 BrowserRouter,Link,Route
,然后用 <BrowserRouter>
把页面包裹起来。这样页面就能拥有 router
的一系列属性。
再加个Link导航
import {BrowserRouter,Link,Route} from 'react-router-dom'
<BrowserRouter>
{/*导航*/}
<nav>
<Link to="/">水果列表</Link>| <Link to="/add">添加水果</Link>
</nav>
content...
</BrowserRouter>
exact表示独占页面的路由。
function HooksTest({fruits,addFruit,asyncFetch}) {
const [fruit, setFruit] = useState("草莓");
// 全局属性
useEffect(() => {
asyncFetch(["草莓", "香蕉"] )
}, []);
return (
<BrowserRouter>
<nav>
<Link to="/">水果列表</Link>| <Link to="/add">添加水果</Link>
</nav>
<div>
{/* <p>{fruit === "" ? "请选择喜爱的水果:" : `您的选择是:${fruit}`}</p> */}
<Route path="/add" component={AddFruit}/>
<Route exact path="/" render={()=>{
return (fruits.length==0?<div>loading...</div>:<FruitList setFruit={setFruit} fruits={fruits}></FruitList>)
}}/>
</div>
</BrowserRouter>
);
}
现在需要加一个详情页面,势必用到传参。
首先在页面定义一个路由组件,使用 /detail/:fruit
跳转:
<Route path="/detail/:fruit" component={FruiteDetail}/>
然后开始写 FruiteDetail
组件,
通过传参,可以用 match.params.fruit(占位符,fruit)
拿到水果字段。
function FruiteDetail({match,history,location}){
console.log(match,history,location)
return (
<div>
{match.params.fruit}详情
</div>
)
}
解构出来的参数是什么信息呢?可以看一下:
有了 history
这个api,可以像react原生api一样,做返回操作:
function FruiteDetail({match,history,location}){
console.log(match,history,location)
return (
<div>
{match.params.fruit}详情
<div>
<button onClick={()=>{history.goBack()}}>返回</button>
</div>
</div>
)
}
location则可以获取当前的地址。
Route组件嵌套在其他页面组件中就产生了嵌套关系 。
假设存在这样的需求,我点击详情,不出现详情页面,而是直接在FruitList中展示。
于是改写FruitList
// 水果列表
function FruitList({ fruits, setFruit }) {
let result = fruits.map(f =>
<li onClick={() => { setFruit(f) }} key={f}>
<Link to={`/list/detail/${f}`}>{f}</Link>
</li>)
return <div>
<ul>
{result}
</ul>
<Route path="/list/detail/:fruit" component={FruiteDetail}/>
</div>;
}
去掉exact。
<Route path="/list" render={()=>{
return (fruits.length==0?<div>loading...</div>:<FruitList setFruit={setFruit} fruits={fruits}></FruitList>)
}}/>
route不加path即可匹配全部页面。
<Route component={() => <h3>页面不存在</h3>}></Route>
但是匹配的所有。需要引入switch。
{/* 添加Switch表示仅匹配一个 */}
<Switch>
<Route path="/list" render={()=>{
return (fruits.length==0?<div>loading...</div>:<FruitList setFruit={setFruit} fruits={fruits}></FruitList>)
}}/>
<Route component={() => <h3>页面不存在</h3>}></Route>
</Switch>
我想让add页面变为私有页面,怎么做?
react-router已有的特性可实现类似vue中路由守卫的功能
你可以创建高阶组件包装route使其具有权限判断。
事实上几乎所有守卫的高阶函数都可以这么用:
// 创建高阶组件,使其可以验证登录态。
function privateRoute(props){
// console.log(props)
const { component: Component, isLogin, ...rest }=props;
const redirect=props.path;
return (
<Route
{...rest}
render={props=>
(isLogin)?<Component {...props}/>:<Redirect to={{
pathname:'/login',
state: { referrer: redirect }
}}/>}
></Route>
)
}
接下来完成登录逻辑:点击直接登录:
//登录,需要拿到isLogin和login操作
function getLogin(props){
const {location,isLogin,login}=props;
// 获取重定向地址
const redirect=location.state.referrer||'/';
if(isLogin){
return <Redirect to={{pathname:location.state.referrer}} />
}
return (
<div>
<div>
<input type="text" placeholder="username"/>
</div>
<div>
<input type="password" placeholder="password"/>
</div>
<div>
<button onClick={login}>login</button>
</div>
</div>
)
}
isLogin应该跟redux拿。新建一个userReducer.js
// uerReducer.js
export const login= (payload) => {
return dispatch => {
setTimeout(() => {
dispatch({ type: 'login', payload});
}, 1000);
};
};
export default function(state = {
isLogin: false
}, action) {
switch (action.type) {
case "login": // 登录
return {isLogin:true}
case "logout":
return {isLogin:false}
default:
return state;
}
}
//Hook.js
import {login} from "../store/userReducer"
// isLogin中,两个组件都需要
const Login=connect(mapStateToProps,mapDispatchToProps)(getLogin)
const PrivateRoute= connect(mapStateToProps,mapDispatchToProps)(privateRoute)
那么add现在就不能直接用原生的路由了。而应该用包装过的路由:
<PrivateRoute path="/add" component={AddFruit}/>
功能实现。