简书项目实战-main首页开发

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;

img

2 首页组件的拆分

img

组件拆分原则:

组件拆分是为了我们更加容易的管理各个组件。因此在拆分的时候我们要进行一个合理拆分,使得我们的代码更简洁,更的容易维护

我们将上图分为 5 个区域,其中图片的显示很简单,我们就将它放在首页,index中,其它的放在conponents中进行管理

imgimg

代码模板:

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;

img

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;
    }
`

结果如图所示:

img

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 其它更改

img

这里的搜索的背景变为了透明,我们返回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 样式布局

img

这个布局相对比较简单

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;
`

结果:

img

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);

当前页面:

img

补充

视频中的语法,传递到样式中的值的使用

img

6 作者部分代码编写

注:我们实现样式不实现换一换的动作,实现如下图的样式

img

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;
`
实现效果:

img

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);

最终代码:

项目地址:https://github.com/Hokwok/jianshu

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值