使用React構建精簡版本掘金(三)

rocky191發表於2019-03-22

抽了兩天工作中閒暇時間,文章詳情頁終於寫完了,先上圖哈,截圖少了下面一部分哈,見諒!

使用React構建精簡版本掘金(三)
本篇文章為使用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包裹內容了,實際開發中個人覺得應該需要讀取對應各層標題的樣式、正文的樣式,分段落顯示,需要去分型別解析各種內容,包括圖片、超連結等內容。

評論區域

使用React構建精簡版本掘金(三)
佈局問題就不說了,重點說一下輸入框評論功能實現,其實類似大家常說的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點贊哦,多謝!

相關文章