<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
作為資料驅動的領導者react/vue等MVVM框架的出現,幫我們減少了工作中大量的冗餘程式碼, 一切皆元件的思想深得人心。元件就是對一些具有相同業務場景和互動模式程式碼的抽象,這就需要我們對元件進行規範的封裝,掌握高質量元件設計的思路和方法可以幫助我們提高日常的開發效率。筆者將會通過實戰抖音訂單元件詳細的介紹元件的設計思路和方法,對新手特別友好,希望對前端新手們和有一定工作經驗的朋友有一定幫助~
在元件設計之前,希望你對css、js具有一定的基礎。在我們的元件設計時需要用到的開源元件庫有:
(有不瞭解的小夥伴可以自行查閱資料學習一下,在後面用到的時候我也會說明的)
axios
它是一個基於 promise 的網路請求庫,用於獲取後端資料,是前端常用的資料請求工具;
react-weui
、weui
weui 是微信官方製作的一個基礎樣式UI庫,我們可以通過閱讀官方檔案直接使用裡面的樣式,而 react-weui 就是將這些樣式封裝成我們可以直接使用的元件;
styled-components
稱之為css in js,現在正在成為在 React 中設計元件樣式的新方法。
另外,我們還用到線上介面工具 faskmock
模擬ajax請求。它更加真實的模擬了前端開發中後端提供資料的方式。
在這我們先來看看元件實現後的元件效果:
在這個元件中我們需要實現的業務有:
(目前我們就暫時實現以下效果,該頁面的其他功能筆者將會在後期慢慢完善~)
tab
,該tab
新增上紅色下劃線樣式,並將該tab
狀態下的訂單展示在下方。loading
圖示tab
下搜尋商品標題含有輸入內容的訂單。fastmock
中請求得到,因此刪除只相對於前端。根據我們的需求,可以劃分出5個元件模組組成整個頁面:
<Myorder/>
,它是其他元件的父元件;<OrderList/>
,單個資料元件<OrderNote/>
;<EmptyItem/>
;<RecommendList/>
。<Myoeder/>
元件中請求資料,將對應的陣列資料通過props
傳給<OrderList/>
元件和<RecommendList/>
元件;<OrderList/>
元件再將單個資料傳給<OrderNote/>
元件。這樣就規範的完成了父元件請求資料,子元件搭建樣式的分工合作了。分析完元件組成接下來完成元件目錄的搭建:
首先我們先根據需求將元件框架寫好,這樣後面寫業務邏輯會更清晰:
這個頁面級別元件包括固定在頂部的搜尋方塊+導航欄,以及OrderList
和RecommendList
元件,因此可以寫出如下元件框架:
import React from 'react' import OrderList from '../OrderList' import RecommendList from '../RecommendList' import { OrderWrapper } from './style' import fanhui from '../../assets/images/fanhui.svg' import gengduo from '../../assets/images/gengduo.svg' import sousuo from '../../assets/images/sousuo.svg' export default function Myorder() { return ( <OrderWrapper> // 搜尋 + 導航欄 部分 <div className="head"> <div className="searchOrder"> <img src={fanhui} alt="返回"/> <div className='searchgroup'> <input placeholder="搜尋訂單" /> <img className="searchimg" src={sousuo} alt="搜尋"/> </div> <img src={gengduo} alt="更多"/> </div> <ul> <li>全部</li> <li>待支付</li> <li>待發貨</li> <li>待收貨/使用</li> <li>評價</li> <li>退款</li> </ul> </div> // 訂單列表元件 <OrderList/> // 推薦列表元件 <RecommendList/> </OrderWrapper> ) }
有了這個框架,我們來一步步往裡面實現內容吧。
首先來完成第一個需求:當點選某個tab
時,如'待支付',這個tab
要有紅色下劃線效果。實現原理其實很簡單,就是當我們觸發該tab
的點選事件時,就將我們事先寫好的active
樣式加到該tab
上。
這裡有兩種方案:
tab
來控制每個<li>
的className
的內容:import React,{ useState} from 'react' import { OrderWrapper } from './style' export default function Myorder() { const [tab,setTab] = useState('全部'); const changeTab= (target) => { setTab(target); } return ( <OrderWrapper> ... <ul> <li className={tab=='全部'?'active':''} onClick={changeTab.bind(null,'全部')}>全部</li> <li className={tab=='待支付'?'active':''} onClick={changeTab.bind(null,'待支付')}>待支付</li> <li className={tab=='待發貨'?'active':''} onClick={changeTab.bind(null,'待發貨')}>待發貨</li> <li className={tab=='待收貨/使用'?'active':''} onClick={changeTab.bind(null,'待收貨/使用')}>待收貨/使用</li> <li className={tab=='評價'?'active':''} onClick={changeTab.bind(null,'評價')}>評價</li> <li className={tab=='退款'?'active':''} onClick={changeTab.bind(null,'退款')}>退款</li> </ul> ... </OrderWrapper> ) }
這種方法有一個明顯的缺點,就是隻能為其新增一個樣式名,當有多個樣式類名時,就會出問題了,因此可以採用第二種方法。
classnames
了,也是比較推薦的方法,寫法也比較簡單。import classnames from 'classnames' import { OrderWrapper } from './style' export default function Myorder() { const [tab,setTab] = useState('全部'); const changeTab= (target) => { setTab(target); } return ( <OrderWrapper> ... <ul> <li className={classnames({active:tab==="全部"})} onClick={changeTab.bind(null,'全部')}>全部</li> <li className={classnames({active:tab==="待支付"})} onClick={changeTab.bind(null,'待支付')}>待支付</li> <li className={classnames({active:tab==="待發貨"})} onClick={changeTab.bind(null,'待發貨')}>待發貨</li> <li className={classnames({active:tab==="待收貨/使用"})} onClick={changeTab.bind(null,'待收貨/使用')}>待收貨/使用</li> <li className={classnames({active:tab==="評價"})} onClick={changeTab.bind(null,'評價')}>評價</li> <li className={classnames({active:tab==="退款"})} onClick={changeTab.bind(null,'退款')}>退款</li> </ul> ... </OrderWrapper> ) }
當有多個類名時,這樣新增:
<li className={classnames('test',{active:tab==="全部"})} onClick={changeTab.bind(null,'全部')}>全部</li>
實現效果如圖:
這裡準備了兩個介面,用於獲取訂單資料和推薦商品資料。
為了便於管理,我們將資料請求封裝在api檔案中:
tab
狀態篩選獲取的資料,這一步我們也寫在介面檔案中:import axios from 'axios' // 請求訂單資料 export const getOrder = ({tab}) => axios .get('https://www.fastmock.site/mock/759aba4bef0b02794e330cccc1c88555/beers/order') .then ( res => { let result=res.data; if(tab){ switch(tab) { case "待支付": result=result.filter(item => item.state=="待支付"); break; case "待發貨": result=result.filter(item => item.state=="待發貨"); break; case "待收貨/使用": result=result.filter(item => item.state=="待收貨/使用"); break; case "評價": result=result.filter(item => item.state=="評價"); break; case "退款": result=result.filter(item => item.state=="退款"); break; default: break; } } return Promise.resolve({ result }); } )
import axios from 'axios' // 請求推薦商品資料 export const getCommend = () => axios.get('https://www.fastmock.site/mock/759aba4bef0b02794e330cccc1c88555/beers/goods')
介面準備好了,接下來我們將資料分配給子元件,接下來資料如何在頁面上顯示的任務就交給子元件<OrderList/>
和<Recommend/>
完成
import React,{useEffect, useState} from 'react' import { OrderWrapper } from './style' import OrderList from './OrderList' import RecommendList from './RecommendList' export default function Myorder() { const [list,setList] =useState([]); const [recommend,setRecommend] = useState([]); // 從介面中獲取推薦商品資料 useEffect(()=> { (async()=> { const {data} = await getCommend(); setRecommend([...data]); })() }) // 從介面中獲取訂單資料,每次tab切換都重新拉取 useEffect(()=>{ (async()=>{ const {result} = await getOrder({tab}); setList([ ...result ]) })() },[tab]) return ( <OrderWrapper> ... {list.length>0 && <OrderList list={list}/>} {recommend.length>0 && <RecommendList recommend={recommend}/>} </OrderWrapper> ) }
搜尋功能應該在對應的tab
下進行,因此我們可以將輸入的內容設定為一個狀態
,每次改變就根據tab
內容和輸入內容重新獲取資料:
api介面對訂單資料的請求的封裝中增加一個query
限制:
export const getOrder = ({tab,query}) => axios .get('https://www.fastmock.site/mock/759aba4bef0b02794e330cccc1c88555/beers/order') .then ( res => { let result=res.data; if(tab){ switch(tab) { case "待支付": result=result.filter(item => item.state=="待支付"); break; case "待發貨": result=result.filter(item => item.state=="待發貨"); break; case "待收貨/使用": result=result.filter(item => item.state=="待收貨/使用"); break; case "評價": result=result.filter(item => item.state=="評價"); break; case "退款": result=result.filter(item => item.state=="退款"); break; default: break; } } if(query) { result = result.filter(item => item.title.includes(query)); } return Promise.resolve({ result }); } )
而在元件的實現上,由於頁面沒有新增點選搜尋的按鈕,如果將input
中的value
直接和query
狀態繫結的話,每次使用者輸入一個字就會進行一次查詢,觸發太頻繁,效能不夠好,使用者體驗也不好。
所以這裡我的想法是每次輸入完按下enter
才進行搜尋
但是React中無法直接對input
的enter
事件進行處理。於是我在網上查閱到兩種處理方式,第一種是通過 e.nativeEvent
來獲取keyCode
判斷是否為 13 ,第二中方法是通過addEventListener
註冊事件來處理,要慎用。
這裡採用第一種方法來實現:
import React,{useState} from 'react' import { OrderWrapper } from './style' export default function Myorder() { const [query,setQuery] = useState(''); const handleEnterKey = (e) => { if(e.nativeEvent.keyCode === 13){ setQuery(e.target.value); } } return ( <OrderWrapper> ... <input placeholder="搜尋訂單" onKeyPress={handleEnterKey} /> ... </div> </OrderWrapper> ) }
在資料請求過程之,頁面會空白,為了提升視覺上的效果,在這個時間段我們就設定一個loading
樣式,這個樣式元件我們直接使用reacct-weui
的Toast
元件。
我們增加一個loading
狀態來來控制Toast
的顯示。
import React,{useEffect, useState} from 'react' import { OrderWrapper } from './style' import WeUI from 'react-weui' const { Toast } = WeUI; export default function Myorder() { const [loading,setLoading]=useState(false); useEffect(()=>{ setLoading(true); (async()=>{ const {result} = await getOrder({tab}); setList([ ...result ]) setLoading(false); })() },[tab]) return ( <OrderWrapper> ... <Toast show={loading} icon="loading">載入中...</Toast> { list.length>0 && <OrderList list={list}} ... <OrderWrapper> ) }
實現效果如圖:
空狀態 元件,顧名思義就是當請求到的資料為空或者是資料長度為 0 時,就顯示該元件。這個元件實現起來比較簡單,因此這裡我們直接寫在myorder
元件中,用styled-components
實現效果。
import React,{useEffect, useState} from 'react' import { OrderWrapper,EmptyItem } from './style' import OrderList from './OrderList' import empty from '../../assets/images/empty.png' export default function Myorder() { const [list,setList] = useState([]); ... return ( <OrderWrapper> ... {list.length>0&&<OrderList list={list} deleteOrder={deleteOrder}/>} {list.length==0&&loading==false&& <EmptyItem> <h3>美好生活 觸手可得</h3> <img src={empty} /> <h2>暫無訂單</h2> <p>你還沒有產生任何訂單</p> </EmptyItem> } ... </OrderWrapper> ) }
完成上面這些業務,myorder
元件就完成的差不多啦~
這個元件只需要將父元件myorder
傳進來的陣列資料通過 map
分配給 OederNote
,另外刪除功能在它的子元件OrderNote
上觸發,需要通過它解構出deleteOrder
函數傳給OrderNote
import React from 'react' import { OrderListWrapper } from './style' export default function OrderList({list,deleteOrder}) { return ( <OrderListWrapper> <h3>美好生活 觸手可得</h3> { list.map(item => ( <OrderNote key={item.id} data={item} deleteOrder={()=>deleteOrder(item.id)}/> )) } </OrderListWrapper> ) }
該元件主要負責實現訂單的展示效果,這裡只展示部分程式碼
import React from 'react' import { NoteWrapper } from './style' const OrderNote = (props) => { const { data } =props; const { deleteOrder } =props return ( <NoteWrapper> ... <div className="btngroup"> <button onClick={deleteOrder}>刪除訂單</button> <button>檢視相似</button> </div> </div> </NoteWrapper> )
在這個元件可以觸發刪除訂單的業務,具體如何刪除我們只需要在父元件myOrder
實現,然後將函數傳遞到OrderNote
觸發
在myOrder
元件新增deleteOrder
函數:
import React from 'react' import OrderList from './OrderList' export default function Myorder() { const deleteOrder = (id) => { setList(list.filter(order => order.id!==id)); } ... return ( <OrderWrapper> ... {list.length>0&&<OrderList list={list} deleteOrder={deleteOrder}/>} ... </OrderWrapper> ) }
該元件也是對從父元件Myorder
獲取來的資料進行展示,主要是做樣式上的功夫。使用多列布局,將頁面分為兩列,並且不固定每個資料盒子的高度。
多列布局注意上面三點就差不多了
以上就是筆者目前完成整個元件設計、封裝的過程啦,後面會去繼續學習下拉重新整理、上拉載入等功能,慢慢完善這個元件。
原始碼地址:cool-g/react-reportPage: 仿抖音我的訂單元件 (github.com)
gitpage地址(直接檢視頁面效果):Vite App (cool-g.github.io)
更多關於React抖音訂單元件設計的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45