首页开发(1)

注意

  • 涉及浮动时要将浮动元素的父元素设为BFC元素,防止浮动元素溢出。
  • 被包裹在BFC中的浮动不会影响BFC外部其他元素的布局(也就是可以使用BFC来清除浮动
  • <img>标签必须设置alt属性。
  • 从App.js中可知Provider提供给Home组件的store是总store,而src-reducer.js中我们知道总store由header的store和home的store组成,所以我们需要先使用.get("home")获取到home的store才能继续获取她下面的数据。

路由

  • 路由指根据url的不同显示不同的内容

在React中使用路由功能

  • 需要借助第三方模块react-router-dom

安装react-router-dom

1
yarn add react-router-dom

使用react-router-dom

  • **引入react-router-dom中的BrowserRouterRoute**。
    • Route组件:定义路由组件。
      • path属性:(字符串)定义路由路径
      • render属性:(函数)在该路径下渲染的内容
      • component属性:(组件)在该路径下渲染的组件
      • exact属性:(可省略属性值)准确匹配路径。如不使用该属性,则默认是模糊匹配path
    • BrowserRouter组件:包含各个路由组件。

在App.js中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { Component } from 'react';
import { Provider } from "react-redux";
import { BrowserRouter, Route } from "react-router-dom";
import Header from "./common/header";
import store from "./store";
import { Globalstyle } from './style';
import { GlobalIconFontStyle } from "./statics/iconfont/iconfont";

class App extends Component {
render() {
return (
<Provider store={store}>
<Globalstyle />
<GlobalIconFontStyle />
<Header />
<BrowserRouter>
<Route path="/" render={() => <div>home</div>}></Route>
<Route path="/detail" render={() => <div>detail</div>}></Route>
</BrowserRouter>
</Provider>
);
}
}

export default App;

可以看到访问/detail时同时出现了//detail的内容
可以看到访问`/detail`时同时出现了`/`和`/detail`的内容

加上exact属性:

1
2
<Route path="/" exact render={() => <div>home</div>}></Route>
<Route path="/detail" exact render={() => <div>detail</div>}></Route>

解决问题


首页组件的拆分

  1. src下新建pages文件夹,表示项目有多少个页面
    1. pages下新建detail文件夹和home文件夹,表示有home和detail两个页面
    2. 在这两个文件夹下分别新建index.js文件。在index.js中放置需要被渲染的组件
  2. App.js中引入两个组件,分别渲染在两个Route组件中

Home组件

src-pages-home-index.js:

1
2
3
4
5
6
7
8
9
10
11
import React, { Component } from 'react'

class Home extends Component {
render() {
return (
<div>Home~</div>
)
}
}

export default Home;

Detail组件

src-pages-detail-index.js:

1
2
3
4
5
6
7
8
9
10
11
import React, { Component } from 'react'

class Detail extends Component {
render() {
return (
<div>Detail~</div>
)
}
}

export default Detail;

App中引入并显示在路由上

App.js中,引入Home、Home两个组件:

1
2
import Home from "./pages/home";
import Detail from "./pages/detail";

使用component属性使路由分别显示对应的组件:

1
2
<Route path="/" exact component={Home}></Route>
<Route path="/detail" exact component={Detail}></Route>

效果

完善Home组件

刚刚只是写了最简单的一个样子,现在来完善一下。
Home组件
在src-pages-home下新建style.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import styled from "styled-components";

export const HomeWrapper = styled.div`
overFlow: hidden;
width: 960px;
margin: 0 auto;
`;

export const HomeLeft = styled.div`
float: left;
width: 625px;
margin-left: 15px;
padding-top: 30px;
.banner-img{
width: 625px;
height: 270px;
}
`;

export const HomeRight = styled.div`
float: right;
width: 240px;
`;

在Home组件中引入并使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React, { Component } from 'react';
import {
HomeWrapper,
HomeLeft,
HomeRight,
} from "./style";

class Home extends Component {
render() {
return (
<HomeWrapper>
<HomeLeft>
<img className="banner-img" src="https://upload.jianshu.io/admin_banners/web_images/4894/23ecc55accf5c6a6c9910be966c125853d1f04a5.png?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540" alt="banner图片" />
</HomeLeft>
<HomeRight>right</HomeRight>
</HomeWrapper>
)
}
}

export default Home;

注意:涉及浮动时要将浮动元素的父元素设为BFC元素,防止浮动元素溢出。
效果

Home组件再拆分

Home组件下还要拆分一些组件:
Home组件下的一些组件

  • home文件夹下新建components文件夹用于存放其他组件。
    • Topic.js
    • List.js
    • Recommend.js
    • Writer.js
  • components下的组件都先简单设置以后放入Home组件来展示一下位置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    import React, { Component } from 'react';
    import {
    HomeWrapper,
    HomeLeft,
    HomeRight,
    } from "./style";
    import List from "./components/List";
    import Recommend from "./components/Recommend";
    import Topic from "./components/Topic";
    import Writer from "./components/Writer";

    class Home extends Component {
    render() {
    return (
    <HomeWrapper>
    <HomeLeft>
    <img className="banner-img" src="https://upload.jianshu.io/admin_banners/web_images/4894/23ecc55accf5c6a6c9910be966c125853d1f04a5.png?imageMogr2/auto-orient/strip|imageView2/1/w/1250/h/540" alt="banner图片" />
    <Topic></Topic>
    <List></List>
    </HomeLeft>
    <HomeRight>
    <Recommend></Recommend>
    <Writer></Writer>
    </HomeRight>
    </HomeWrapper>
    )
    }
    }

    export default Home;
    效果

Topic区域布局

  • 之前每个组件都放在一个文件夹中,样式就在style.js下。
  • 但是为了防止过度开发,Home组件下的所有小的组件的样式我们统一放在Home组件的style.js去定义,包括Topic组件。

pages-home-style.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export const TopicWrapper = styled.div`
display:flow-root;
padding: 20px 0 10px 0;
margin-left: -18px;
`;

export const TopicItem = styled.div`
float: left;
line-height: 32px;
background: #f7f7f7;
font-size: 14px;
color: #000;
border: 1px solid #dcdcdc;
border-radius: 4px;
padding-right: 10px;
margin-left: 18px;
margin-bottom: 18px;
.topic-pic{
width: 32px;
height: 32px;
margin-right: 10px;
float: left;
}
`;

topic.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import React, { Component } from 'react'
import {
TopicWrapper,
TopicItem,
} from "../style"

class Topic extends Component {
render() {
return (
<TopicWrapper>
<TopicItem>
<img
className="topic-pic"
src="https://upload.jianshu.io/users/upload_avatars/3136195/484e32c3504a.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96/format/webp"
alt="社会热点"
/>
社会热点
</TopicItem>
</TopicWrapper>
)
}
}

export default Topic;

效果显示

Topic的reducer设计

  • 我们现在只写了一个TopicItem组件,实际上应该从数据中读取生成多个TopicItem组件。数据是保存在redux的store中的,整个项目的reducer在src的store中,它是由各个小的reducer合并而成的
  • 所以我们在Home组件下也新建一个store文件夹,用于管理home以及其下组件的数据
    • 新建reducer.js:管理home页面的数据(可将header的reducer复制一个框架过来)。
    • 新建index.js:管理store中所有文件的输出,他是整个store中所有文件的出口文件。
  • 项目的总reducer中引入home的reducer并将其合并
  • Topic组件中循环读取state的数据、输出对应TopicItem组件
    • Topic组件在Home组件内部,Home组件又在Provider组件内部,所以Topic组件有使用store中数据的能力,只需借助connect方法即可使用数据。

新建store-reducer.js管理数据

home-store-reducer.js中,将header的reducer复制一个框架过来,放入一些模拟数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { fromJS } from "immutable";

// immutable对象 是不可改变的,注意list是immutable数组
const defaultState = fromJS({
topicList:[{
id:1,
title:"社会热点",
imgUrl: "https://upload.jianshu.io/users/upload_avatars/3136195/484e32c3504a.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96/format/webp"
},{
id:2,
title:"手绘",
imgUrl: "https://upload.jianshu.io/users/upload_avatars/3136195/484e32c3504a.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96/format/webp"
}]
});

export default (state = defaultState, action) => {
switch (action.type) {

default:
return state;
}
}

新建store-index.js管理文件输出

home-store-index.js:管理store中所有文件的输出,他是整个store中所有文件的出口文件:

1
2
3
import reducer from "./reducer";

export { reducer };

合并入项目总reducer

src-store-reducer.js,项目的总reducer中引入home的reducer并将其合并

1
2
3
4
5
6
7
8
9
10
import { combineReducers } from "redux-immutable";
import { reducer as headerReducer } from "../common/header/store";
import { reducer as homeReducer } from "../pages/home/store";

const reducer = combineReducers({
header: headerReducer,
home: homeReducer
})

export default reducer;

Topic中循环读取state中数据

  • Topic组件中循环读取state的数据、输出对应TopicItem组件
  • 此时Topic组件在Home组件内部,Home组件又在Provider组件内部,所以Topic组件有使用store中数据的能力,只需借助connect方法即可使用数据
  • 由于我们只需要读取state中数据,不需要去改变数据,所以此时connect方法的参数2可以先使用null。
  • 注意
    1. state中的TopicList是immutable数组,需要使用get()获取数据
    2. 从App.js中可知Provider提供给Home组件的store是总store,而src-reducer.js中我们知道总store由header的store和home的store组成,所以我们需要先使用.get("home")获取到home的store才能继续获取她下面的数据。

home-components-Topic.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React, { Component } from 'react';
import { connect } from "react-redux";
import {
TopicWrapper,
TopicItem,
} from "../style"

class Topic extends Component {
render() {
return (
<TopicWrapper>
{
this.props.list.map((item) => {
return (
<TopicItem key={item.get("id")}>
<img
className="topic-pic"
src={item.get("imgUrl")}
alt={item.get("title")}
/>
{item.get("title")}
</TopicItem>
)
})
}
</TopicWrapper>
)
}
}

const mapState = (state) => ({
list: state.get("home").get("topicList")
})

export default connect(mapState, null)(Topic);

效果

,