项目实战:简书之详情页

src/pages/detail/
store文件夹:actionCreator.js / constants.js /index.js / reducer.js
index.js
style.js

style.js
import styled from 'styled-components';

export const DetailWrapper = styled.div`
	overflow:hidden;
	width:620px;
	margin:0 auto;
	padding-bottom:100px;
	
`;

export const Header = styled.div`
	margin:50px 0 20px 0;
	line-height:44px;
	font-size:34px;
	color:#333;
	font-weight:bold;
`;

export const Content = styled.div`
	color:#2f2f2f;
	img {//img标签
		width:100%;
	}
	p{ //P标签
    	margin:25px 0;
    	font-size:16px;
    	line-height:30px;
	}
`;
import React,{ Component }from 'reat';
import { DetailWrapper,Header }from './style';
import { connect }from 'react-redux';
import { actionCreators }from './store';
class Detail extends Component {
	render(){
		return (
			<DetailWrapper>
				<Header>{this.props.title}</Header>
				<Content dangerouslySetInnerHTML={{__html:this.props.content}} />
					//{this.props.content}发现展示的仅仅是一个字符串 为了显示成标签我们需要dangerouslySetInnerHTML 写完以后把此行删掉
				
			</DetailWrapper>
		)
	}
	componentDidMount(){
	this.props.getDetail(this.props.match.params.id);
	}
}

const mapStateToProps =(state) => ({
	title:state.getIn(['detail','title']),
	content:state.getIn(['detail','content'])
});  //({})返回一个对象

const mapDispatchToProps= (dispatch) => ({
	getDetail(id){
		dispatch(actionCreators.getDetail(id));
	}
})
export default connect(mapStateToProps,mapDispatchToProps)(Detail);

store/reducer.js
import { fromJS }from 'immutable';
import * as constants from './constants';

const defaultState = fromJS({
	title:''
	content:‘’
});

export default (state = defaultState ,action)=> {
	switch(action.type){
		case constants.CHANGE_DETAIL:
			return state.merge({
				title:action.title
				content:action.content
			})
		default:
			return state;
		}
	}

index.js
import reducer from './reducer';
import * as actionCreators from './actionCreators';
import * as constants from './constants';
export { reducer,actionCreators,constants };


<img src=''><p><b></b></p><p></p>//形成字符串,中间没有空格,用单引号包裹
//复制粘贴到content

/api/detail.json
{
	"success":true,
	"data":{
		"title":"标题"
		"content":"

"
} } constants.js export const CHANGE_DETAIL ='detail/CHANGE_DETAIL'; actionCreators.js import axios from 'axios'; import * as constants from './constants'; const changeDetail =(title,content) => ({ type:constants.CHANGE_DETAIL, title, content }) export const getDetail =(id) => { return (dispatch)=>{ axios.get('/api/detail.json?id=' + id).then((res) => { const result =res.data.data; dispatch(changeDetail(result.title,result.content)); }) } }

控制台里Network的XHR里显示的是接口里的数据

动态路由:
第一种:
这个id我们从this.props返回的对象里
在match.params.id

<link key={index} to={'/detail/' +item.get('id')}>

App.js
<Route path='/detail/:id' exact component={...}>

第二种:

<link key={index} to={'/detail?id=' +item.get('id')}>

App.js
<Route path='/detail' exact component={...}>
但是这种this.props.match.params里没有id了
在location.search里  解析一下 比较麻烦 建议用第一种

异步组件
打开Network里的JS,发现各个页面点击只加载一个bundle.js,如果这个js文件很大会导致时间很长,此时我们需要点击一个页面只加载那个页面的js,需要异步组件

react-loadable github

detail里创建loadable.js
import Loadable from 'react-loadable';
import React from 'react';//使用JSX必须引入
const LoadableComponent =loadable({
	loader:() => import ('./'),//import这里指异步加载
	loading(){
		return <div>正在加载</div>
	}//加载页面 显示临时的 
});

export default () => <loadableComponent />

App.js
这样引用就可以让它成为异步组件
import Detail from './pages/detail/loadable.js';

但是这样组件index.js里获取不到路由里的信息了 this.props.match.param.id就找不到了 也没有params
如何解决?
import { withRouter } from 'react-router-dom';
导出那一行改为 connect(...,...)(withRouter(Detail));

项目上线流程

XAMPP软件:
后端开发目录在htdocs里
api文件夹
然后我们在浏览器里可以找到这些接口
localhost/api/…

然后我们在简书(你的前端文件)目录下运行npm run build
然后发现多了build的文件夹 这些就是上线需要的文件

把build里的文件全部放到htdocs里 完成了上线

你可能感兴趣的:(项目实战:简书之详情页)