抽了兩天工作中閒暇時間,文章詳情頁終於寫完了,先上圖哈,截圖少了下面一部分哈,見諒!
本篇文章為使用React構建精簡版本掘金系列第三篇,想看前兩篇的話,請檢視第一篇,第二篇。整理一下詳情頁用到的一些知識點:
- redux儲存、取值
- React-router路由跳轉傳值
- 父子元件傳值
實現過程
左側部分
主要包含點贊數量顯示、評論數量、收藏按鈕,分享連結等。由於要常駐在左側,且頁面滾動過程中位置不變,故使用fixed定位方式,並且使用ant-design中的Avatar,Badge,Icon,Popover元件。
import { Avatar,Badge,Icon,Popover } from 'antd';
...
<div style={articleSuspendedPanel}>
<Badge count={this.props.starCount} overflowCount={this.state.overflowCount} style={badgeSy}>
<div style={panelCircleSy}>
<Icon type="like" theme="filled" style={IconSy}/>
</div>
</Badge>
<Badge count={this.props.commentsCount} overflowCount={this.state.overflowCount} style={badgeSy}>
<div style={panelCircleSy}>
<Icon type="message" theme="filled" style={IconSy}/>
</div>
</Badge>
<div style={panelCircleSy}>
<Icon type="star" theme="filled" style={IconSy}/>
</div>
<label>分享</label>
<Avatar style={avatarSy1}>
<img alt="" src="//b-gold-cdn.xitu.io/v3/static/img/weibo.2076a57.svg" />
</Avatar>
<Avatar style={avatarSy1}>
<img alt="" src="//b-gold-cdn.xitu.io/v3/static/img/qq.0834411.svg" />
</Avatar>
<Popover content={<QRCode value={window.location.href+'/post/'+this.props.wxShareAddr} />} placement="bottom">
<Avatar style={avatarSy1}>
<img alt="" src="//b-gold-cdn.xitu.io/v3/static/img/wechat.63e1ce0.svg" />
</Avatar>
</Popover>
</div>
複製程式碼
render中的程式碼結構如上,starCount和commentsCount資料是從父級元件傳入的,微信分享掃一掃利用了QRCode元件,關於該元件的介紹可檢視上一篇文章,有詳細的說明。
中間部分(重點)
仔細看的話,其實中間部分也可以分為頂部的作者介紹,包括文章編輯時間,閱讀量,登入使用者是否關注了作者。第二部分才是真正的文章詳情。第三部分有關於該文章的評論區域,最下面是一系列的相關推薦文章。接下來分別介紹下:
頂部
<List
itemLayout="horizontal"
dataSource={this.state.articleInfo.articleList}
renderItem={item => (
<List.Item actions={[item.isFocus?<button className='focusedBtn'>已關注</button>:<button className='focusBtn'>關注</button>]}>
<List.Item.Meta
avatar={<Avatar src={item.authorImage} />}
title={item.author}
description={<div><span>{item.editDate}</span><span style={{marginLeft:'10px'}}>閱讀{item.readNum}</span></div>}
/>
</List.Item>
)}
/>
複製程式碼
很簡單的一部分,應該能看明白。
中間部分
中間部分我這裡就簡單的用section包裹內容了,實際開發中個人覺得應該需要讀取對應各層標題的樣式、正文的樣式,分段落顯示,需要去分型別解析各種內容,包括圖片、超連結等內容。
評論區域
佈局問題就不說了,重點說一下輸入框評論功能實現,其實類似大家常說的todolist功能。輸入內容,點選評論按鈕或者直接回車,評論內容新增到評論列表,輸入框內容清空。<div style={{marginLeft:'10px',flex:'1'}}>
<div>
<Input value={this.state.value} placeholder="輸入評論..." onFocus={this.handleFocus.bind(this)} onChange={this.handleChange.bind(this)} onPressEnter={this.handlePressEnter.bind(this)}/>
</div>
{
this.state.showIconAndBtn?
<div style={{display:'flex',justifyContent:'space-between',marginTop:'10px'}}>
<span style={{color:'#027fff',cursor: 'pointer',display:'flex',alignItems:'center'}} onClick={info}>
<img alt='' src='//b-gold-cdn.xitu.io/v3/static/img/emoji.5594dbb.svg' />
表情
</span>
<p>
<label style={{color:'#c2c2c2',marginRight:'8px'}}>Enter</label>
<Button type="primary" onClick={this.handlePressEnter.bind(this)}>評論</Button>
</p>
</div>:''
}
</div>
複製程式碼
showIconAndBtn是控制輸入框下面那一行是否顯示的,初始進入頁面的時候是隻能看到輸入框,下面的表情和評論按鈕只能等輸入框獲取焦點後才能顯示。 輸入框上繫結了三個事件,分別用來處理獲取焦點事件、輸入內容改變事件、按下回車事件。
- 獲取焦點事件
handleFocus=()=>{
this.setState({
showIconAndBtn:true
})
}
複製程式碼
- 輸入內容改變事件
handleChange=(e)=>{
this.setState({
value:e.target.value
});
}
複製程式碼
- 回車事件(後面的評論按鈕功能相同)
handlePressEnter=()=>{
if(this.state.value!==''){
this.props.submitComment(this.state.value);//呼叫父級元件並傳值
this.setState({
value:'',
showIconAndBtn:false
})
}else{
message.warning('還未填寫評論哦!');
}
}
複製程式碼
這裡有用到子元件向父元件傳值的方法,簡單介紹下:
父向子傳值
父向子傳值起始比較簡單,重點要考慮向下傳值是否會使得父元件中的狀態過於繁雜,是否會影響頁面效能。
假設存在一個元件ComponentA,在父元件中呼叫ComponentA,並且傳遞引數data
...
this.state={
text:'hello world'
}
...
<ComponentA data={this.state.text} /
複製程式碼
在元件ComponentA中就可以通過this.props.data獲取到父元件傳入的data值了。
子向父傳值
由子元件的事件觸發,在觸發的函式體中呼叫父元件傳入的方法,將子元件裡的值傳入即可。
假設存在父元件ComponentParent,子元件ComponentChild,父元件中呼叫ComponentChild
父元件:
...
handleChange=(val)=>{
console.log(`資訊:`+val);//子元件傳入的資料
}
...
<ComponentChild change={this.handleChange.bind(this)} />
複製程式碼
子元件:
//假設有個方法叫handleClick
handleClick=()=>{
this.props.change('hello');
}
複製程式碼
在子元件中執行handleClick方法的時候就會觸發父元件中的方法handleChange,並在控制檯輸出hello。
兄弟元件傳值
可以將資料提升到共同的父元件中,進行傳值,之後在利用父子元件傳值即可。
多層級元件或者稱為不相鄰元件傳值
可以利用redux管理全域性狀態,之後在任何地方都可以取到對應的資料。
這種方式的使用方式在第一篇文章做了說明,可以瀏覽哦!
待實現功能:
- 表情元件
評論列表區域
個人實現的這塊區域和掘金有少許不同。
<List
itemLayout="vertical"
size="large"
dataSource={this.props.commentList}
renderItem={item => (
<List.Item
key={item.userId}
actions={[<span>{moment().subtract(item.editDate, 'days').fromNow()}</span>,<IconText type="like-o" text={item.starNum} />, <IconText type="message" text={item.commentNum} />]}
>
<List.Item.Meta
avatar={<a href={'/user/'+item.userId}><Avatar src={item.userImage} /></a>}
title={<div>{item.authorName}</div>}
description={item.userDesc}
/>
{item.commentText}
</List.Item>
)}
/>
複製程式碼
採用了ant-design中的List元件進行顯示
待實現
- 巢狀評論列表
相關推薦列表
該部分結構採用了首頁列表元件,只需按照文章型別,推薦不同內容即可,這裡不再贅述。
右側部分
右側整體結構和首頁右側內容類似,分6塊:
- 關於作者
<Card
title="關於作者"
style={{ width: '100%' }}
hoverable={'true'}
headStyle={{fontSize:'14px',color:'#333'}}
bodyStyle={{padding:'0 16px'}}
>
<List
itemLayout="vertical"
dataSource={this.props.author}
renderItem={item => (
<List.Item onClick={()=>window.location.href='/user/'+item.id}>
<List.Item.Meta
avatar={<Avatar size={46} src={item.authorImage} />}
title={item.author}
/>
{item.isGroup?<div>
<Avatar style={{ backgroundColor: '#e1efff',color:'#7bb9ff' }} icon="user" />
<label style={{color:'#000',marginLeft:'10px',fontSize:'16px'}}>聯合編輯</label>
</div>:''}
<div style={{marginTop:'10px'}}>
<Avatar style={{ backgroundColor: '#e1efff' }}>
<Icon type="like" theme="filled" style={{color:'#7bb9ff'}} />
</Avatar>
<label style={{color:'#000',marginLeft:'10px',fontSize:'16px'}}>獲得贊數{item.allStarNum}</label>
</div>
<div style={{marginTop:'10px'}}>
<Avatar style={{ backgroundColor: '#e1efff' }}>
<Icon type="eye" theme="filled" style={{color:'#7bb9ff'}} />
</Avatar>
<label style={{color:'#000',marginLeft:'10px',fontSize:'16px'}}>獲得閱讀數{item.allReadNum<99999?item.allReadNum:'99999+'}</label>
</div>
</List.Item>
)}
/>
</Card>
複製程式碼
- 感興趣的小冊推薦
<Card
title="你可能感興趣的小冊"
style={{ width: '100%',marginTop:'20px' }}
hoverable={'true'}
headStyle={{fontSize:'14px',color:'#333'}}
bodyStyle={{padding:'0 16px'}}
>
<List
itemLayout="horizontal"
dataSource={this.props.recommendBooks}
className="bookCard"
renderItem={item => (
<List.Item onClick={()=>window.location.href='/book/'+item.id}>
<List.Item.Meta
avatar={<img alt='' src={item.bookImage} />}
title={item.title}
description={<p className="book-desc"><span>{item.sellNum+'人已購買'}</span><span className="try-read">試讀</span></p>}
/>
</List.Item>
)}
/>
</Card>
複製程式碼
這一塊單獨抽一個元件,別的地方也可能會用到,後期只需傳入小冊內容即可。目前首頁和文章詳情頁用的都是這個元件。
- 掘金客戶端下載二維碼
<Card style={{ width: '100%',marginTop:'20px' }} hoverable='true' className="download-card" bodyStyle={{padding:'15px'}}>
<NavLink to='/app'>
<img alt='qrcode' src='//b-gold-cdn.xitu.io/v3/static/img/timeline.e011f09.png' />
<div>
<div className="headline">下載掘金客戶端</div>
<div className="desc">一個幫助開發者成長的社群</div>
</div>
</NavLink>
</Card>
複製程式碼
- 掘金微信群
<Card
style={{ width: '100%',marginTop:'20px' }}
hoverable={'true'}
bodyStyle={{padding:'0'}}
>
<img alt='' src='//b-gold-cdn.xitu.io/v3/static/img/backend.ba44b94.png' style={{height:'200px',width:'100%'}} />
</Card>
複製程式碼
- 相關文章列表
<Card
title="相關文章"
headStyle={{fontSize:'14px',color:'#333'}}
style={{ width: '100%',marginTop:'20px'}}
hoverable={'true'}
bodyStyle={{padding:'0 16px'}}
>
<List
itemLayout="vertical"
dataSource={this.props.relationArticles}
split={false}
renderItem={item => (
<List.Item onClick={()=>window.location.href='/post/'+item.id}>
<div style={{color:'#333',fontSize:'16px'}}>{item.title}</div>
<div style={{marginTop:'10px',color:'#b2bac2',fontSize:'12px'}}>
<Icon type="like" theme="filled" style={{marginRight:'3px'}} />{item.starNum}
<Icon type="message" theme="filled" style={{marginLeft:'15px',marginRight:'3px'}} />{item.commentNum}
</div>
</List.Item>)}
/>
</Card>
複製程式碼
- 文章目錄
<div className='text-catalogue'>
<Timeline>
{this.props.data.map((item,index)=>{
return <Timeline.Item color='#000' key={item.text} dot={<span className='catalogue-circle'></span>}>{(item.children && item.children.length !== 0)?<div><a href={`#heading-${index}`}>{item.text}</a><div className='secondCatalogue'><Catalogue data={item.children} /></div></div>:<a href={`#heading-${index}`}>{item.text}</a>}</Timeline.Item>
})}
</Timeline>
</div>
複製程式碼
用到了ant-design中的Timeline元件,這個單獨抽離成一個元件,整個系統中都可以複用,這裡用到了遞迴元件實現目錄巢狀的功能。
路由跳轉傳參
從首頁文章列表進入文章詳情頁的時候需要傳遞一些引數,比如文章的id值
方法一(本文采用了該方法)
- 第一步 在路由檔案中設定
<Route path='/post/:articleId' component={Post}/>
複製程式碼
- 第二步 文章列表點選事件中
showArticleInfo=(id)=>{
console.log(`文章id值:${id}`);
window.location.href='/post/'+id;
}
複製程式碼
- 第三步 在進入文章詳情頁的時候就可以獲取到文章id了。
componentDidMount(){
const {match}=this.props;
console.log(`文章id:${match.params.articleId}`);
}
複製程式碼
方法二
- 第一步:定義路由
<Route path='/post' component={Post} />
複製程式碼
- 第二步:傳遞方式
let data = {id:3,name:sam};
let path = {
pathname:'/post',
state:data,
}
複製程式碼
連結跳轉
<Link to={path}>詳情</Link>
複製程式碼
- 第三步:獲取
let data = this.props.location.state;
let {id,name} = data;
複製程式碼
截止到目前為止,首頁和文章詳情頁的結構和基本功能就算完成了,細節後續在優化,剩餘部分陸續更新中。
上述詳細程式碼請見github,不要忘了star和點贊哦,多謝!