1 React中使用路由
1.1 安装
yarn add react-router-dom
import { BrowserHistory, Route} from 'react-router-dom';
1.2 路由设置和使用
打开我们的APP.js,如下是设置的代码
代码解释:
import { BrowserRouter, Route} from 'react-router-dom';
导入模块,外面加一层div 是有要去的 render只包含一个child
添加路由:
<BrowserRouter>
<Route path='/' exact render= {() => <div>home</div>}></Route>
<Route path='/detail' exact render= {() => <div>detail</div>}></Route>
</BrowserRouter>
path: 路径
其中exact表示只有路径完全匹配才能访问(如果不设置的话,/detail的路径两个都可以访问到)
src/App.js
import React, { Component} from 'react';
import Header from './common/header'
import { BrowserRouter, Route} from 'react-router-dom';
import store from './store'
import { Provider } from 'react-redux';
class App extends Component {
render() {
return (
<Provider store={store}>
<div>
<Header />
<BrowserRouter>
<Route path='/' exact render= {() => <div>home</div>}></Route>
<Route path='/detail' exact render= {() => <div>detail</div>}></Route>
</BrowserRouter>
</div>
</Provider>
);
}
}
export default App;
2 首页组件的拆分
组件拆分原则:
组件拆分是为了我们更加容易的管理各个组件。因此在拆分的时候我们要进行一个合理拆分,使得我们的代码更简洁,更的容易维护
我们将上图分为 5 个区域,其中图片的显示很简单,我们就将它放在首页,index中,其它的放在conponents中进行管理
代码模板:
import React, {Component} from 'react';
class Lsit extends Component {
render() {
return (
<div>Lsit</div>
)
}
}
export default Lsit;
App.js
import React, { Component} from 'react';
import Header from './common/header'
import { BrowserRouter, Route} from 'react-router-dom';
import Home from './pages/home';
import Detail from './pages/detail';
import store from './store'
import { Provider } from 'react-redux';
class App extends Component {
render() {
return (
<Provider store={store}>
<div>
<Header />
<BrowserRouter>
<Route path='/' exact component = { Home }></Route>
<Route path='/detail' exact component = { Detail }></Route>
</BrowserRouter>
</div>
</Provider>
);
}
}
export default App;
index.js
import React, {Component} from 'react';
import Topic from './components/Topic';
import List from './components/List';
import Writer from './components/Writer';
import Recommend from './components/Recommend';
import {
HomeWrapper,
HomeLeft,
HomeRight
} from './style';
class Home extends Component {
render() {
return (
<HomeWrapper>
<HomeLeft>
<img className='banner-img' alt='' src="//upload.jianshu.io/admin_banners/web_images/4318/60781ff21df1d1b03f5f8459e4a1983c009175a5.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540" />
<Topic />
<List />
</HomeLeft>
<HomeRight>
<Recommend />
<Writer />
</HomeRight>
</HomeWrapper>
)
}
}
export default Home;
3 首页专题布局及reducer设计
3.1 布局
我们设置布局,同时调整样式,样式都在style.css进行
import React, {Component} from 'react';
import {
TopicWrapper,
TopicItem
} from '../style';
class Topic extends Component {
render() {
return (
<TopicWrapper>
<TopicItem>
<img
className='topic-pic'
src='https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1074417518,1198280004&fm=26&gp=0.jpg' alt='tu'
/>
社会热点
</TopicItem>
</TopicWrapper>
)
}
}
export default Topic;
Topic的样式
export const TopicItem = styled.div`
float: left;
height: 32px;
line-height: 32px;
margin-left: 18px;
margin-bottom: 18px;
padding-right: 10px;
font-size: 14px;
background: #f7f7f7;
color: #000;
border: 1px solid #dcdcdc;
border-radius: 4px;
.topic-pic {
margin-right: 10px;
display: block;
float: left;
width: 32px;
height:32px;
}
`
3.2 reducer设计
首先我们在home目录下,创建一个reducer和index,用index导出reducer,另外在reducer中存放我们需要的数据
如下:
(1)home/reducer
存放数据
import { fromJS } from 'immutable';
//将其变为immutable
const defaultState = fromJS({
topicList: [{
id: 1,
title: '社会热点',
imgUrl: '//upload.jianshu.io/collections/images/261938/man-hands-reading-boy-large.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/64/h/64'
},{
id:2,
title: '手绘',
imgUrl: '//upload.jianshu.io/collections/images/21/20120316041115481.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/64/h/64'
}]
});
export default (state = defaultState, action) => {
switch(action.type) {
default:
return state;
}
}
(2)home/index
方便管理与导出
import reducer from './reducer';
export {reducer};
(3)分析是否可以使用
我们的Provider包裹了我们的home,而home又包裹了Topic,完全可以从中获取数据
import React, { Component} from 'react';
import { BrowserRouter, Route} from 'react-router-dom';
import Header from './common/header';
import Home from './pages/home';
import Detail from './pages/detail'
import store from './store';
import { Provider } from 'react-redux';
class App extends Component {
render() {
return (
<Provider store={store}>
<div>
<Header />
<BrowserRouter>
<Route path='/' exact component={Home}></Route>
<Route path='/detail' exact component={Detail}></Route>
</BrowserRouter>
</div>
</Provider>
);
}
}
export default App;
(4)与src/reducr建立联系
import { reducer as headerReducer } from '../common/header/store';
import {reducer as homeReducer} from '../pages/home/store';
//import { combineReducers } from 'redux';
import { combineReducers } from 'redux-immutable';
const reducer = combineReducers({
header: headerReducer,
home: homeReducer
});
export default reducer;
(5)使用数据
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
TopicWrapper,
TopicItem
} from '../style';
class Topic extends Component {
render() {
const {list} = this.props;
return (
<TopicWrapper>
{
list.map((item) => (
<TopicItem key={item.get('id')}>
<img
//immutable获取要使用get
className='topic-pic'
src={ item.get('imgUrl')} alt='tu'
/>
{item.get('title')}
</TopicItem>
))
}
</TopicWrapper>
)
}
}
const mapState = (state) => ({
list: state.get('home').get('topicList')
})
export default connect(mapState, null)(Topic);
4 首页文章列表制作
4.1 样式布局
布局的方式基本上就和专题布局的方式差不多,先进行样式的布局,因为内容分为两类:
1.有图片
2.无图片
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
ListItem,
ListInfo
} from '../style'
class Lsit extends Component {
render() {
const {list} = this.props;
return (
<div>
<ListItem>
<img className='pic' src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598593565605&di=7e03a7f64bab11b3da0a67f9a6b5ff41&imgtype=0&src=http%3A%2F%2Fn.sinaimg.cn%2Fsinacn%2Fw480h339%2F20180226%2F0d93-fyrwsqi3956378.jpg" alt="sabeining"/>
<ListInfo>
<h3 className='title'>撒贝宁说:我对自己的婚姻非常失望</h3>
<p className='desc'>在参加一档节目中,谈到婚姻,撒贝宁公开说道:“我觉得自己的婚姻非常的无望,也没有继续维持下去的必要。”惹得现场观众大吃一惊。 事实上当时马东故意...</p>
</ListInfo>
</ListItem>
<ListItem>
<ListInfo>
<h3 className='title'>我摆地摊,最惨痛的教训</h3>
<p className='noimgdesc'>曾经,我摆过一次摊,在龙眼丰收的7月里,进了30斤新鲜龙眼,进货价3元一斤,打算以5元单价出售。 找了个人流量大的道路旁,铺开一张垫子,挑选一些...</p>
</ListInfo>
</ListItem>
</div>
)
}
}
export default Lsit;
style.js
export const TopicWrapper = styled.div`
overflow: hidden;
padding: 20px 0 10px 0;
margin-left: -18px;
border-bottom: 1px solid #dcdcdc; //专题下加一个底线
`
export const ListItem = styled.div`
overflow: hidden;
padding: 15px 0;
border-bottom: 1px solid #dcdcdc;
.pic {
position: relative;
top: 30px;
float: right;
width: 150px;
height: 100px;
border: 1px solid #f0f0f0;
border-radius: 4px;
}
`
export const ListInfo = styled.div`
width: 460px;
float: left;
.title {
line-height: 18px;
font-size: 18px;
font-weight: bold;
color: #333;
margin-block-start: 10px;
margin-block-end: 10px;
}
.desc {
font-size: 13px;
line-height: 24px;
color: #999;
}
.noimgdesc {
width: 640px;
font-size: 13px;
line-height: 24px;
color: #999;
}
`
结果如图所示:
4.2 数据管理
我们在reducer中增加我们需要的数据
import { fromJS } from 'immutable';
//将其变为immutable
const defaultState = fromJS({
topicList: [{
id: 1,
title: '社会热点',
imgUrl: '//upload.jianshu.io/collections/images/261938/man-hands-reading-boy-large.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/64/h/64'
},{
id:2,
title: '手绘',
imgUrl: '//upload.jianshu.io/collections/images/21/20120316041115481.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/64/h/64'
}],
articleLists: [{
id: 1,
title: '撒贝宁说:我对自己的婚姻非常失望',
desc: '在参加一档节目中,谈到婚姻,撒贝宁公开说道:“我觉得自己的婚姻非常的无望,也没有继续维持下去的必要。”惹得现场观众大吃一惊。 事实上当时马东故意...',
imgUrl: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598593565605&di=7e03a7f64bab11b3da0a67f9a6b5ff41&imgtype=0&src=http%3A%2F%2Fn.sinaimg.cn%2Fsinacn%2Fw480h339%2F20180226%2F0d93-fyrwsqi3956378.jpg',
imgAlt: '撒贝宁'
},{
id: 2,
title: '我摆地摊,最惨痛的教训',
desc: '曾经,我摆过一次摊,在龙眼丰收的7月里,进了30斤新鲜龙眼,进货价3元一斤,打算以5元单价出售。 找了个人流量大的道路旁,铺开一张垫子,挑选一些...',
imgUrl: null
},{
id: 3,
title: '撒贝宁说:我对自己的婚姻非常失望',
desc: '在参加一档节目中,谈到婚姻,撒贝宁公开说道:“我觉得自己的婚姻非常的无望,也没有继续维持下去的必要。”惹得现场观众大吃一惊。 事实上当时马东故意...',
imgUrl: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598593565605&di=7e03a7f64bab11b3da0a67f9a6b5ff41&imgtype=0&src=http%3A%2F%2Fn.sinaimg.cn%2Fsinacn%2Fw480h339%2F20180226%2F0d93-fyrwsqi3956378.jpg',
imgAlt: '撒贝宁'
},{
id: 4,
title: '我摆地摊,最惨痛的教训',
desc: '曾经,我摆过一次摊,在龙眼丰收的7月里,进了30斤新鲜龙眼,进货价3元一斤,打算以5元单价出售。 找了个人流量大的道路旁,铺开一张垫子,挑选一些...',
imgUrl: null
}]
});
export default (state = defaultState, action) => {
switch(action.type) {
default:
return state;
}
}
在list中显示,注意不同的类型返回不同的样式。根据是否有imgUrl进行判断。
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
ListItem,
ListInfo
} from '../style'
class Lsit extends Component {
render() {
const {list} = this.props;
return (
<div>
{
list.map((item) => {
if(item.get('imgUrl')){
return (
<ListItem key={item.get('id')}>
<img className='pic' src={item.get('imgUrl')} alt={item.get('imgAlt')}/>
<ListInfo>
<h3 className='title'>{item.get('title')}</h3>
<p className='desc'>{item.get('desc')}</p>
</ListInfo>
</ListItem>
)
}else{
return (
<ListItem key={item.get('id')}>
<ListInfo>
<h3 className='title'>{item.get('title')}</h3>
<p className='noimgdesc'>{item.get('desc')}</p>
</ListInfo>
</ListItem>
)
}
})
}
</div>
)
}
}
const mapState = (state) => ({
list: state.getIn(['home', 'articleLists'])
})
export default connect(mapState, null)(Lsit);
4.3 其它更改
这里的搜索的背景变为了透明,我们返回header/style中找到SearchInfo,将背景变为白色 background: #fff;
export const SearchInfo = styled.div`
position: absolute;
left: 0;
top: 56px;
width:240px;
padding: 0 20px;
box-shadow: 0 0 8px rgba(0, 0, 0, .2);
background: #fff;
`
5 首页推荐部分代码编写
5.1 样式布局
这个布局相对比较简单
Recommend.js
注意 组件必须大写!!!!!!
import React, {Component} from 'react';
import {
RcommendItem
} from '../style'
class Recommend extends Component {
render() {
return (
<RcommendItem>
<img className="pic" src= "https://cdn2.jianshu.io/assets/web/banner-s-club-aa8bdf19f8cf729a759da42e4a96f366.png" alt="" />
<img className="pic" src= "https://cdn2.jianshu.io/assets/web/banner-s-club-aa8bdf19f8cf729a759da42e4a96f366.png" alt="" />
</RcommendItem>
)
}
}
export default Recommend;
style.js
export const RcommendItem = styled.div`
width: 280px;
height: 225px;
padding: 6px 0;
.pic {
width: 280px;
height: 50px;
}
`
//我们把右边调低了 20px 为了使得页面更美观
export const HomeRight = styled.div`
position: relative;
top: 20px;
width: 280px;
float: right;
`
结果:
5.2 数据管理
reducer中存储的数据
recommendPic: [{
id: '1',
imgUrl:"https://cdn2.jianshu.io/assets/web/banner-s-club-aa8bdf19f8cf729a759da42e4a96f366.png"
},{
id: '2',
imgUrl: "https://cdn2.jianshu.io/assets/web/banner-s-7-1a0222c91694a1f38e610be4bf9669be.png"
},{
id: '3',
imgUrl:"https://cdn2.jianshu.io/assets/web/banner-s-5-4ba25cf5041931a0ed2062828b4064cb.png"
},{
id: '4',
imgUrl: "https://cdn2.jianshu.io/assets/web/banner-s-6-c4d6335bfd688f2ca1115b42b04c28a7.png"
}]
数据的加载与处理
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {
RcommendItem
} from '../style'
class Recommend extends Component {
render() {
const {list} = this.props;
return (
<RcommendItem>
{
list.map((item) => {
return (
<img className="pic" src= {item.get('imgUrl')} alt="" />
)
})
}
</RcommendItem>
)
}
}
const mapState = (state) => ({
list: state.getIn(['home', 'recommendPic'])
})
export default connect(mapState, null)(Recommend);
当前页面:
补充
视频中的语法,传递到样式中的值的使用
6 作者部分代码编写
注:我们实现样式不实现换一换的动作,实现如下图的样式
6.1 样式布局
writer.js
import React, {Component} from 'react';
import {
WriterHeader,
WriterSwitch,
WriterItem,
WriterName,
WriterDesc,
WriterFlower,
WriterInfo,
Allwriters
} from '../style'
class Writer extends Component {
render() {
return (
<div>
<WriterHeader>
推荐作者
<WriterSwitch>
<svg className="spin" aria-hidden="true">
<use xlinkHref="#iconspin"></use>
</svg>
换一批
</WriterSwitch>
</WriterHeader>
<WriterItem>
<img className="head" src="https://upload.jianshu.io/users/upload_avatars/5303650/33f2887c-e6e2-43b3-bd2c-b8516e5ee646.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96/format/webp" alt="" />
<WriterInfo>
<WriterFlower>+关注</WriterFlower>
<WriterName>Rose的肉丝儿</WriterName>
<WriterDesc>写了151.5k字 · 13k喜欢</WriterDesc>
</WriterInfo>
</WriterItem>
<WriterItem>
<img className="head" src="https://upload.jianshu.io/users/upload_avatars/5303650/33f2887c-e6e2-43b3-bd2c-b8516e5ee646.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96/format/webp" alt="" />
<WriterInfo>
<WriterFlower>+关注</WriterFlower>
<WriterName>Rose的肉丝儿</WriterName>
<WriterDesc>写了151.5k字 · 13k喜欢</WriterDesc>
</WriterInfo>
</WriterItem>
<Allwriters>查看全部</Allwriters>
</div>
)
}
}
export default Writer;
style.js
export const WriterItem = styled.div`
width: 285px;
overflow: hidden;
.head {
width: 50px;
height: 50px;
border: 1px solid #ddd;
border-radius: 50%;
float: left;
}
`
export const WriterHeader = styled.div`
font-size: 14px;
color: #969696;
margin-bottom: 18px;
`
export const WriterSwitch = styled.div`
float: right;
font-size: 14px;
color: #969696;
.spin {
display: block;
position: relative;
top:5px;
float: left;
width: 12px;
height: 12px;
color: red;
margin-right: 2px;
}
`
export const WriterInfo = styled.div`
overflow: hidden;
width: 230px;
margin-top: 4px;
margin-left: 60px;
margin-bottom: 20px;
`
export const WriterName = styled.div`
font-size: 14px;
color: #333;
`
export const WriterFlower = styled.span`
float: right;
margin-right: 10px;
font-size: 13px;
color: #42c02e;
`
export const WriterDesc = styled.div`
margin-top: 8px;
font-size: 12px;
color: #969696;
`
export const Allwriters = styled.div`
position: absolute;
width: 258px;
padding: 7px 7px 7px 12px;
left: 0;
font-size: 13px;
color: #787878;
background-color: #f7f7f7;
border: 1px solid #dcdcdc;
border-radius: 4px;
text-align: center;
`
实现效果:
6.2 数据编写
reducer中的数据
WirterList: [
{id:"14715425",nickname:"简书钻首席小管家",avatar_source:"https://upload.jianshu.io/users/upload_avatars/14715425/e0668349-8c75-43db-8a9d-c388e5f00d0d.jpg",total_likes_count:"101307",total_wordage:"462675"},
{id:"6652326",nickname:"没文化的野狐狸",avatar_source:"https://upload.jianshu.io/users/upload_avatars/6652326/498e86b9-7067-4e96-b5f2-6438089e2da1.jpg",total_likes_count:"6174",total_wordage:"319160"},
{id:"7290998",nickname:"念远怀人",avatar_source:"https://upload.jianshu.io/users/upload_avatars/7290998/f64f5ef0-def0-4b26-beb3-b9d88f060ba0.jpg",total_likes_count:"14246",total_wordage:"685673"},
{id:"7663825",nickname:"名贵的考拉熊",avatar_source:"https://upload.jianshu.io/users/upload_avatars/7663825/7c28763e-002b-4e89-8dea-5b8da210ef2c.jpg",total_likes_count:"19585",total_wordage:"267784"},
{id:"278",nickname:"邓哲",avatar_source:"https://upload.jianshu.io/users/upload_avatars/278/0778727c-c557-4ffb-929c-6ee182a58145.png",total_likes_count:"1494",total_wordage:"434008"}
]
writer.js
注意:显示的时候将数字单位转换为k,并取一位小数
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {
WriterHeader,
WriterSwitch,
WriterItem,
WriterName,
WriterDesc,
WriterFlower,
WriterInfo,
Allwriters
} from '../style'
class Writer extends Component {
render() {
const {list} = this.props;
const switchNum = (num) => {
if(num > 1000){
num = parseFloat(num / 1000);
num = num.toFixed(1);
const final = num + "k";
return final;
}else{
return num;
}
}
return (
<div>
<WriterHeader>
推荐作者
<WriterSwitch>
<svg className="spin" aria-hidden="true">
<use xlinkHref="#iconspin"></use>
</svg>
换一批
</WriterSwitch>
</WriterHeader>
{
list.map((item) => {
return (
<WriterItem key={item.get('id')}>
<img className="head" src={item.get('avatar_source')} alt="" />
<WriterInfo>
<WriterFlower>+关注</WriterFlower>
<WriterName>{item.get('nickname')}</WriterName>
<WriterDesc>写了{switchNum(item.get('total_wordage'))}字 · {switchNum(item.get('total_likes_count'))}喜欢</WriterDesc>
</WriterInfo>
</WriterItem>
)
})
}
<Allwriters>查看全部</Allwriters>
</div>
)
}
}
//注意方括号
const mapState = (state) => ({
list: state.getIn(['home', 'WirterList'])
})
export default connect(mapState,null)(Writer);