现在我们来进入项目基础搭建的环节。
本节代码对应 GitHub 分支: chapter2
初始项目的搭建主要分为三个部分进行:
- 路由的配置和应用部分
- 公共组件的开发
- redux 的 store 创建和引入
现在让我们开始吧。
# 一、路由配置
# 路由文件编写
要开发一个复杂应用的时候,首先要做的并不是上来就开始写具体功能,要想清楚整个应用的结构,从路由开始入手编写是一个比较好的方式,也建议大家拿到别人的项目的时候从路由开始着手,可以很好的整理我们的思路。
应用的骨架其实非常简单,顶部有固定的内容及 tab 栏,下面对应不同的功能组件。
首先安装依赖。
npm install react-router react-router-dom react-router-config --save
现在我们在 routes 目录下新建 index.js 文件,利用 react-router-config 来对路由进行配置。
//routes/index.js
import React from 'react';
import { Redirect } from "react-router-dom";
import Home from '../application/Home';
import Recommend from '../application/Recommend';
import Singers from '../application/Singers';
import Rank from '../application/Rank';
export default [
{
path: "/",
component: Home,
routes: [
{
path: "/",
exact: true,
render: () => (
<Redirect to={"/recommend"}/>
)
},
{
path: "/recommend",
component: Recommend
},
{
path: "/singers",
component: Singers
},
{
path: "/rank",
component: Rank
}
]
}
]
Home 组件对应公共组件,下面的推荐组件、歌手列表组件和排行榜组件为具体的功能组件。
为了让路由文件生效,必须在 App 根组件下面导入路由配置,现在在 App.js 中:
import React from 'react';
import { GlobalStyle } from './style';
import { renderRoutes } from 'react-router-config';//renderRoutes 读取路由配置转化为 Route 标签
import { IconStyle } from './assets/iconfont/iconfont';
import routes from './routes/index.js';
import { HashRouter } from 'react-router-dom';
function App () {
return (
<HashRouter>
<GlobalStyle></GlobalStyle>
<IconStyle></IconStyle>
{ renderRoutes (routes) }
</HashRouter>
)
}
export default App;
# 新建组件文件
现在你的项目应该是无法启动的,因为这些组件你都没有定义和引入。
现在, 在 application 目录下,新建 Home 文件夹,然后新建 index.js 文件,
//src/appliction/Home/index.js
import React from 'react';
function Home (props) {
return (
<div>Home</div>
)
}
export default React.memo (Home);
然后类似的,创建 Recommend、Singers 和 Rank 组件。
启动项目,打开页面,你可以看到 "home" 已经显示到屏幕,但是这还不够,我们需要展示下面的功能组件,但是你在地址后面加上 /recommend,却并没有显示 Recommend 组件相应的内容,因为 renderRoutes 这个方法只渲染一层路由,之前 Home 处于数组第一层,后面的功能组件在第二层,当然不能正常渲染啦。其实要解决这个问题也非常简单,只需在 Home 中再次调用 renderRoutes 即可。
//src/appliction/Home/index.js
import React from 'react';
import { renderRoutes } from "react-router-config";
function Home (props) {
const { route } = props;
return (
<div>
<div>Home</div>
{ renderRoutes (route.routes) }
</div>
)
}
export default React.memo (Home);
好,现在你可以访问 /recommend 路由,应该可以看到 Recommend 中的内容。同理,现在也可以正常访问其它的路由啦。
# 二、公共组件开发
路由折腾清楚后,我们来着手开发项目的第一个组件: Home 组件。
# 全局样式准备
现在要真正开始写样式了,为了统一风格,需要一些全局样式配置,在 assets 目录下新建 global-style.js, 内容如下:
// 扩大可点击区域
const extendClick = () => {
return `
position: relative;
&:before {
content: '';
position: absolute;
top: -10px; bottom: -10px; left: -10px; right: -10px;
};
`
}
// 一行文字溢出部分用... 代替
const noWrap = () => {
return `
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
`
}
export default {
'theme-color': '#d44439',
'theme-color-shadow': 'rgba (212, 68, 57, .5)',
'font-color-light': '#f1f1f1',
'font-color-desc': '#2E3030',
'font-color-desc-v2': '#bba8a8',// 略淡
'font-size-ss': '10px',
'font-size-s': '12px',
'font-size-m': '14px',
'font-size-l': '16px',
'font-size-ll': '18px',
"border-color": '#e4e4e4',
'background-color': '#f2f3f4',
'background-color-shadow': 'rgba (0, 0, 0, 0.3)',
'highlight-background-color': '#fff',
extendClick,
noWrap
}
# 顶部栏开发
首先,在 Home 目录下新建 style.js,创建 CSS 样式组件
import styled from'styled-components';
import style from '../../assets/global-style';
export const Top = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 5px 10px;
background: ${style ["theme-color"]};
&>span {
line-height: 40px;
color: #f1f1f1;
font-size: 20px;
&.iconfont {
font-size: 25px;
}
}
`
很简单的布局和样式,就不过多解释了。接下来在 Home 组件应用这些样式,
//src/appliction/Home/index.js
import React from 'react';
import { renderRoutes } from "react-router-config";
import { Top } from './style';
function Home (props) {
const { route } = props;
return (
<div>
<Top>
<span className="iconfont menu"></span>
<span className="title">WebApp</span>
<span className="iconfont search"></span>
</Top>
{ renderRoutes (route.routes) }
</div>
)
}
export default React.memo (Home);
接着来编写上面的 tab 栏,先定义样式:
export const Tab = styled.div`
height: 44px;
display: flex;
flex-direction: row;
justify-content: space-around;
background: ${style ["theme-color"]};
a {
flex: 1;
padding: 2px 0;
font-size: 14px;
color: #e4e4e4;
&.selected {
span {
padding: 3px 0;
font-weight: 700;
color: #f1f1f1;
border-bottom: 2px solid #f1f1f1;
}
}
}
`
export const TabItem = styled.div`
height: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
`
在 Home 组件中使用:
import React from 'react';
import { renderRoutes } from "react-router-config";
import {
Top,
Tab,
TabItem,
} from './style';
import { NavLink } from 'react-router-dom';// 利用 NavLink 组件进行路由跳转
function Home (props){
const { route } = props;
return (
<div>
<Top>
<span className="iconfont menu"></span>
<span className="title">Web App</span>
<span className="iconfont search"></span>
</Top>
<Tab>
<NavLink to="/recommend" activeClassName="selected"><TabItem><span > 推荐 </span></TabItem></NavLink>
<NavLink to="/singers" activeClassName="selected"><TabItem><span > 歌手 </span></TabItem></NavLink>
<NavLink to="/rank" activeClassName="selected"><TabItem><span > 排行榜 </span></TabItem></NavLink>
</Tab>
{ renderRoutes (route.routes) }
</div>
);
}
export default React.memo (Home);
打开页面,现在一个像样的 WebApp 头部就出来了,并且点击不同的 tab 会显示不同的功能组件。

# 三、redux 准备
本项目开发的一大核心理念就是用 Redux 这一成熟的状态管理库实现单一数据源。因此,在后面的具体功能开发之前,有必要准备一些关于 Redux 的工作。
# 安装相应依赖
npm install redux redux-thunk redux-immutable react-redux immutable --save
其中 redux-immutable 大家可能比较陌生,因为项目中需要用到 immutable.js 中的数据结构,所以合并不同模块 reducer 的时候需要用到 redux-immutable 中的方法。
# 创建 store
在 store 文件夹下面新建 index.js 和 reducer.js 文件:
//reducer.js
import { combineReducers } from 'redux-immutable';
export default combineReducers ({
// 之后开发具体功能模块的时候添加 reducer
});
//index.js
import { createStore, compose, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducer from './reducer'
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore (reducer, composeEnhancers (
applyMiddleware (thunk)
));
export default store;
# 项目中注入 store
现在 App.js 中代码如下:
import React from 'react'
import { Provider } from 'react-redux'
import { GlobalStyle } from './style'
import { renderRoutes } from 'react-router-config'
import { IconStyle } from './assets/iconfont/iconfont'
import store from './store/index'
import routes from './routes/index.js'
import { HashRouter } from 'react-router-dom';
function App () {
return (
<Provider store={store}>
<HashRouter>
<GlobalStyle></GlobalStyle>
<IconStyle></IconStyle>
{ renderRoutes (routes) }
</HashRouter>
</Provider>
)
}
export default App;
现在功能依旧能用,但是打开控制台会有这样一段报错:

因为现在没有开发出具体的 reducer 函数,没关系,随着之后的开发,这个错误会自动消失。
← 初始化项目 推荐模块1 打造酷炫轮播及列表 →