转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 “路由”跳转
就“写文章页”的布局而言,我依然不会倾注太多精力,重点依然在——拿到一个新组件,我们该如何一步步思考,以及后边的整个“逻辑”该如何编码。
1️⃣打开 src 目录下的 pages 文件夹,在其中新增一个 write 文件夹。用于表示可跳转的 write 页面:
🔗前置知识:《首页开发——① 如何在 React 中使用“路由”》
2️⃣在 write 文件夹下,新增一个 index.js
文件,并写入以下代码:
import React, {Component} from "react";
class Write extends Component {
render() {
return(
<div>写文章页~</div>
)
}
}
export default Write;
3️⃣打开 src 目录下的 App.js
文件:
import React, { Component } from "react";
import {GlobalStyle} from "./style";
import {GlobalIconStyle} from "./statics/iconfont/iconfont";
import {BrowserRouter, Route} from "react-router-dom";
import Header from "./common/header";
import Home from "./pages/home";
import Detail from "./pages/detail";
import Login from "./pages/login";
// 3️⃣-①:引入 Write 组件;
import Write from "./pages/write";
import { Provider } from "react-redux";
import store from "./store";
class App extends Component {
render() {
return (
<div>
<GlobalStyle />
<GlobalIconStyle />
<Provider store={store}>
<BrowserRouter>
<div>
<Header />
<Route path="/" exact component={Home}></Route>
<Route path="/detail/:id" exact component={Detail}></Route>
<Route path="/login" exact component={Login}></Route>
{/*
3️⃣-②:继续用 Route 包裹可以跳转的 write 页面;
❗️Route 表示“一条条的路由规则”!
*/}
<Route path="/write" exact component={Write}></Route>
{/*
❗️❗️❗️3️⃣-③:其中,path 指页面跳转的“路径”;
exact 指“路径”必须“精确”地匹配才“跳转”;
component 指将“渲染”的内容替换为“组件”。
*/}
</div>
</BrowserRouter>
</Provider>
</div>
);
}
}
export default App;
返回页面查看(可以正常进入 write 页面):
2 “写文章页”布局
1️⃣打开 pages 目录下 write 文件夹中的 index.js
文件:
🔗前置知识:《HTML——③ HTML 表单详解》
import React, {Component} from "react";
// ❗️从当前目录下的 style.js 中,引入“样式组件”!
import {
WriteWrapper,
Title,
Editor
} from "./style";
class Write extends Component {
render() {
return(
<WriteWrapper>
<Title placeholder="输入标题" />
<Editor>
<textarea placeholder="输入内容"></textarea>
</Editor>
</WriteWrapper>
)
}
}
export default Write;
2️⃣在 pages 目录下的 write 文件夹中新建一个 style.js
文件,用于放置“写文章页”的样式:
import styled from "styled-components";
export const WriteWrapper = styled.div`
width: 620px;
margin: 0 auto;
padding-bottom: 100px;
`;
export const Title = styled.input`
display: inline-block;
width: 100%;
padding: 10px 20px;
outline: none;
font-size: 18px;
`;
export const Editor = styled.div`
textarea {
position: absolute;
width: 620px;
height: 100%;
padding: 20px;
background: #eee;
border: none;
resize: none;
outline: none;
}
`;
返回页面查看(“写文章页”正常显示):
3 “写文章页”登录鉴权
❓需求,当点击页面右上角的“写文章”时:
- 只有在登录状态时,才会跳转至“写文章页”;
- 若未登录,则跳转至“登录页”。
✔️需求分析:
只要在 Write 组件里获取到 login
的值,我们就可以用这个“数据”来做相应的路由跳转。
1️⃣本文一开始就让“Write 组件”拥有了获取 store 中数据的“能力”——我们把 Write 组件放到了 Provider
组件之中。
打开 src 目录下的 App.js
文件查看:
import React, { Component } from "react";
import {GlobalStyle} from "./style";
import {GlobalIconStyle} from "./statics/iconfont/iconfont";
import {BrowserRouter, Route} from "react-router-dom";
import Header from "./common/header";
import Home from "./pages/home";
import Detail from "./pages/detail";
import Login from "./pages/login";
import Write from "./pages/write";
import { Provider } from "react-redux";
import store from "./store";
class App extends Component {
render() {
return (
<div>
<GlobalStyle />
<GlobalIconStyle />
<Provider store={store}>
<BrowserRouter>
<div>
<Header />
<Route path="/" exact component={Home}></Route>
<Route path="/detail/:id" exact component={Detail}></Route>
<Route path="/login" exact component={Login}></Route>
<Route path="/write" exact component={Write}></Route> {/* ❗️❗️❗️ */}
</div>
</BrowserRouter>
</Provider>
</div>
);
}
}
export default App;
2️⃣既然有了“能力”,Write 组件就可以“连接”store,然后从 store 中取得“数据项” login
的值。
2️⃣-①:打开 write 目录下的 index.js
文件;
import React, {Component} from "react";
import {
WriteWrapper,
Title,
Editor
} from "./style";
/*
2️⃣-②:从 react-redux 中引入 connect 方法(它也是 React-redux 的核心 API 之一),
connect 的目的很明确——就是“连接”的意思!
*/
import {connect} from "react-redux";
/*
❗️❗️❗️2️⃣-⑭:幸运的是,react-reouter-dom 为我们提供了一个“组件”——Redirect,
它可以直接“重定向”至目的页;
*/
import {Redirect} from "react-router-dom";
class Write extends Component {
render() {
/*
❗️❗️❗️2️⃣-⑨:“映射”上了过后,我们就可以通过调用 this.props.login 来“调用”
store 中的 login 了!
注释掉下边的代码~
return(
<WriteWrapper>
<Title placeholder="输入标题" />
<Editor>
<textarea placeholder="输入内容"></textarea>
</Editor>
</WriteWrapper>
)
*/
// ❗️❗️❗️2️⃣-⑩:取而代之,我们用一个“条件判断”来决定“页面渲染”和“路由跳转”;
if(this.props.login) { // 2️⃣-⑪:如果为“已登录”状态,则“渲染”出“写文章页”;
return(
<WriteWrapper>
<Title placeholder="输入标题" />
<Editor>
<textarea placeholder="输入内容"></textarea>
</Editor>
</WriteWrapper>
)
}else { // 2️⃣-⑫:否则(为“未登录”状态),则“重定向”至“登录页”;
// ❓2️⃣-⑬:怎样“重定向”至“登录页”呢?
// 2️⃣-⑮:我们用“Redirect 组件”来“重定向”至“登录页”;
return <Redirect to="/login" />; // ❗️❗️❗️注意标签的闭合!
}
}
}
// 2️⃣-⑥:接下来,我们先定义“连接”的“规则”;
const mapStateToProps = (state) => { /*
2️⃣-⑦:把 store 里的“数据 state”作为“参数”
传递给 mapStateToProps;
*/
return { // ❗️这个规则会返回一个“对象”出去!
login: state.getIn(["login", "login"]) /*
❗️❗️❗️2️⃣-⑧:“规则”的具体做法为——将 store
里的 login 映射到“Write 组件”里的 props 的
login 中去;
*/
}
}
/*
2️⃣-③:之前我们直接导出的是 Write,可用了 React-redux 后,就不能这样写了!
export default Write;
*/
/*
2️⃣-④:取而代之,我们是导出 connect 方法(
❗️注意看我们给 connect 方法传递了哪些参数!)
*/
export default connect(mapStateToProps, null)(Write); /*
2️⃣-⑤:我们一共要给 connect 方法传递 3 个参数!
Write 表示:connect 会让“Write 组件”和 store 进行
“连接”;
mapStateToProps 表示:“Write 组件”和 store 进行
“连接”是需要“规则”的,而具体的“规则”就在这个
mapStateToProps 里边(❗️直译为:把 store 里边的
“数据 state”映射到“Write 组件”的 props 里);
null 表示:这里还会接收一个名叫 mapDispatchToProps
的参数,由于这里不涉及到“修改数据”,故用 null 占位。
*/
2️⃣-⑯:打开 header 目录下的 index.js
文件;
import React, {Component} from "react";
import {Link} from "react-router-dom";
import {
HeaderWrapper,
Logo,
Navbar,
ItemList,
LinkList,
SearchArea,
SearchInput,
SearchPanel,
PanelTitle,
PanelChange,
PanelLabels,
LabelLink,
Extra,
ExtraLink
} from "./style";
import { connect } from "react-redux";
import {actionCreators} from "./store";
import {actionCreators as loginActionCreators} from "../../pages/login/store";
class Header extends Component {
getPanels() {
const newList = this.props.list.toJS();
const pageLabels = [];
if(newList.length) {
for(let i=(this.props.page - 1)*10; i<this.props.page*10; i++) {
pageLabels.push(
<Link to="/" key={newList[i]}>
<LabelLink>
{newList[i]}
</LabelLink>
</Link>
)
}
return pageLabels;
}
}
render() {
return (
<HeaderWrapper>
<Link to="/">
<Logo>
<img src="https://qdywxs.github.io/jianshu-images/logo.png" alt="logo" />
</Logo>
</Link>
<Navbar className="clearfix">
<ItemList className="active">
<Link to="/">
<LinkList href="/">
首页
</LinkList>
</Link>
</ItemList>
<ItemList>
<Link to="/">
<LinkList>
下载APP
</LinkList>
</Link>
</ItemList>
</Navbar>
<SearchArea>
<SearchInput
onFocus={() => this.props.handleInputFocus(this.props.list)}
/>
<span className="iconfont icon-search"></span>
<SearchPanel>
<PanelTitle>
热门搜索
<PanelChange
onMouseDown={this.props.handleMouseDown}
onMouseUp={this.props.handleMouseUp}
onClick={() => this.props.handleChangePage(this.props.page, this.props.totalPage)}
>
<span className={this.props.refresh ? "iconfont refresh" : "iconfont"}></span>
换一批
</PanelChange>
</PanelTitle>
<PanelLabels className="clearfix">
{this.getPanels()}
</PanelLabels>
</SearchPanel>
</SearchArea>
<Extra>
<span className="iconfont icon-textsize" ></span>
{this.props.login ? <ExtraLink className="login" onClick={this.props.logout}>退出</ExtraLink> :
<Link to="/login">
<ExtraLink className="login">
登录
</ExtraLink>
</Link>
}
<Link to="/">
<ExtraLink className="register">
注册
</ExtraLink>
</Link>
{/*
❗️❗️❗️2️⃣-⑰:将“写文章”的 Link 标签的跳转“路径”改为 /write;
先注释掉下面的写法~
<Link to="/">
*/}
<Link to="/write">
<ExtraLink className="writing">
<span className="iconfont icon-pen"></span>
写文章
</ExtraLink>
</Link>
</Extra>
</HeaderWrapper>
)
}
}
const mapStateToProps = (state) => {
return {
refresh: state.getIn(["header", "refresh"]),
list: state.getIn(["header", "list"]),
page: state.getIn(["header", "page"]),
totalPage: state.getIn(["header", "totalPage"]),
login: state.getIn(["login", "login"])
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleMouseDown() {
const action = actionCreators.changeClassNameAction();
dispatch(action)
},
handleMouseUp() {
const action = actionCreators.resumeClassNameAction();
dispatch(action)
},
handleInputFocus(list) {
if(list.size === 0) {
const action = actionCreators.initLabelAction();
dispatch(action)
}
},
handleChangePage(page, totalPage) {
if(page < totalPage) {
dispatch(actionCreators.changePageAction(page + 1))
}else {
dispatch(actionCreators.changePageAction(1))
}
},
logout() {
const action = loginActionCreators.logout();
dispatch(action);
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);
返回页面查看(需求全部正确实现):
下一篇,我们对整个项目作性能的优化和上线,也是很重要的一篇。
祝好,qdywxs ♥ you!