摘要:這個庫也很好理解,主要使用了和兩個方法使用就是讓所有的組件有取到里保存的的可能性。我被這個疏忽折磨了一個晚上。后面的是個參數(shù),可以用傳進(jìn)去,或者依靠其他的判斷一下。然后只要用一個三元運(yùn)算符就可以解決要不要這個組件顯示的問題。
這幾天用react+redux+webpack寫了一個簡單的郵箱,這是我第一次用redux寫應(yīng)用,覺得很有必要記錄一下遇到的各種坑~~??
DEMO在這里:
https://yisha0307.github.io/M...
這邊是源碼:
https://github.com/yisha0307/...
(各位用github的旁友路過請隨意幫我點(diǎn)個贊喲!謝謝~)
外觀如圖:
webpack基礎(chǔ)的配置環(huán)境可以先看我這篇文章:
https://segmentfault.com/a/11...
然后再來講講這次特別的地方。
1)CDN考慮到最后bundle.js的大小問題,第三方庫我都用的cdn, 這次用到的有:
react / redux / react-dom / react-redux / font-awesome,
除了最后一個,其他的四個都要在webpack.config.js里用externals注明一下:
//webpack.config.js externals: { "react":"React", "redux":"Redux", "react-dom" :"ReactDOM", "react-redux":"ReactRedux" }
然后在寫的js/jsx文件里開頭引用一下就行:
//類似這樣的格式: import React,{Component} from "react"
font-awesome因?yàn)槭莄ss,本來就是全局的,所以就不需要externals,直接用就好了~
2)UglifyJsPlugin這個plugin也是為了縮小最后的bundle.js的~
不過因?yàn)檠b了這個plugin之后熱加載的速度會變慢,所以建議開發(fā)的時候先不要用~
另外還有一些辦法可以讓bundle.js變小,比如關(guān)掉devtool之類的,具體可以看我之前寫的一篇筆記:
https://segmentfault.com/n/13...
這次整個的應(yīng)用我是這么安排的:
node_modules不說了,反正基本不用管;
public里面放的是最后的bundle.js和index.html,和我自己做頭像的一張照片嘿嘿~
src里就是主要寫的東西啦~因?yàn)槭怯胷eact-redux的provider和connect寫的,所以分成了containers和components,components放UI組件,containers放容器組件;
css我用的是sass,這次試了下css-module,也挺容易的,只要在webpack.config.js里面的css-loader后面加上?modules就可以用css-module了,
具體用法:https://segmentfault.com/n/13...
因?yàn)闆]有服務(wù)器端,這次的郵件就用inbox.json這個文件模擬;
reducers.js記錄這次使用的所有reducer,最后用redux里的combineReducers合并成一個,用createStore引入到
先看一眼我這次的reducers:
//import MAILS from "./src/inbox.json"; import {combineReducers} from "redux" import MAILS from "./src/inbox.json" //1、mails //數(shù)據(jù)庫里所有的Mails(包括顯示的和沒顯示的) //先對MAILS進(jìn)行處理,每個加上一個id let id = 0 for(const mail of MAILS){ mail.id = id++; } console.log(MAILS); const mails = (state = MAILS, action) => { switch(action.type){ case "COMPOSE": return [...state, {from: action.from, address: action.address, time:action.time, message: action.message, subject:action.subject, id: id++, tag: action.tag, read:"true"}] case "DELETE_MAIL": //根據(jù)id把這封郵件找出來,tag改成"deleted" return state.map(mail => { if(mail.id !== action.id){return mail;}else{ return(Object.assign({}, mail, {"tag": "deleted"})); } }) case "OPEN_MAIL": return state.map(mail => { if(mail.id !== action.id){return mail;}else{ return(Object.assign({},mail,{"read":"true"})); } }) default: return state } } //2、currentSection //顯示在mailist里的mails const currentSection = (state = "inbox", action) => { switch(action.type){ case "SELECT_TAG": return action.tag; default: return state } } //3、selected //顯示在maildetail里的那封郵件 const selectedEmailID = (state = null, action) => { switch(action.type){ case "OPEN_MAIL": return action.id; case "DELETE_MAIL": const mails = action.mails const selected= mails.find(mail => mail.tag === action.tag && mail.id > action.id); if(!selected){return null} return selected.id case "SELECT_TAG": return null default: return state } } //4、composeORnot //如果值為true,maillist和maildetail不出現(xiàn),只出現(xiàn)composepart //如果值為false, 反過來 const composeORnot = (state = false,action) => { switch(action.type){ case "TURN_COMPOSE": return !state; case "SELECT_TAG": return false default: return state } } //5、新加一個unread const showUnread = (state = false,action) => { switch(action.type){ case "TURN_UNREAD": return action.bool; default: return state } } const inboxApp = combineReducers({mails,currentSection,selectedEmailID,composeORnot,showUnread}); export default inboxApp
這次用的reducers:
1) mails:
COMPOSE: 每寫一封郵件在原來的mails后面插入一封;
DELETE: 目標(biāo)郵件的tag改成"deleted";
OPEN:目標(biāo)郵件的"read"改成"true".
2) currentSection:
SELECT_TAG:在右邊欄選到哪個tag就render那個tag下的郵件隊列;
3)selectedEmailID:
OPEN_MAIL: open的為目標(biāo)郵件;
DELETE_MAIL: delete的郵件的下一封且同tag的郵件選為目標(biāo)郵件;
SELECT_TAG:選其他的tag,select的mail就取消,等待用戶選擇;
4)composeORnot:
TURN_COMPOSE: 就是在頁面上點(diǎn)"compose"這個按鈕,mailList和mailDetail不出現(xiàn),出現(xiàn)的是compose郵件的地方;
SELECT_TAG:在右邊欄選tag的時候,自動回到mailList和mailDetail;
5) showUnread
其實(shí)就是在頁面上的"all"和"unread"切換;
reducers其實(shí)就是用來記錄state的變化,所以寫react會用到幾個state, 這邊就需要幾個reducers~ 不過有一點(diǎn)不一樣的是,如果是寫react應(yīng)用,思維邏輯是從action => state的變化,但是react+redux就是從state的變化 => action。
舉個例子來說,如果我在react里寫delete mail這個action,是這樣的:
//react w/o redux deleteEmail(id){ const emails = this.state.emails; const index = emails.findIndex(x=>x.id === id); emails[index].tag="deleted"; selectedEmail = emails.find( x=> x.tag===emails[id].tag && x.id > id); this.setState({ selectedEmail: selectedEmail, emails }) }
這個deleteEmail的動作其實(shí)影響到了
emails(選中的這封mail的tag變成"deleted")
selectedEmail(自動選同一個tag隊列里的下一封郵件)
兩個state。但是寫的時候,邏輯其實(shí)是從actions的角度出發(fā)的。
但是如果用redux寫,就是從state的角度出發(fā)考慮,可以看我上面的reducers里的mails和selectedEmailId兩個state里都有DELETE_MAIL這個action。
另外設(shè)計states的時候,要盡量減少各個states之間的耦合,因?yàn)樗鼈冎g在reducers.js里是沒法互相引用的;但是如果實(shí)在沒法完全剝離開也是有辦法解決的。比如我上面寫的selectedEmailId這個reducer,在DELETE_MAIL這個動作發(fā)出之后,要選擇下一封郵件作為target,但是不能直接用action.id+1, 因?yàn)闆]法確定在inbox.json文件里,隊列里下一個mail的tag是和你刪掉的一樣的,所以這時候我選擇把mails當(dāng)做參數(shù)傳進(jìn)去:
case "DELETE_MAIL": const mails = action.mails //注意這個 const selected= mails.find(mail => mail.tag === action.tag && mail.id > action.id); if(!selected){return null} return selected.id
用的時候就直接把mails放在mapStateToProps, 然后再傳給dispatch就行~就可以解決兩個state互相引用的問題啦~
二、react和redux的連接這次是用react-redux這個庫來連接的,當(dāng)然也可以選擇不用react-redux,直接在root組件上把所有的action都dispatch一下,然后一級一級傳下去。
react-redux這個庫也很好理解,主要使用了Provider和connect兩個方法.
1)Provider使用provider就是讓所有的組件有取到redux里保存的state的可能性。只要在root組件外面包一層就可以。需要的屬性就是store。
//index.jsx const store = createStore(inboxApp) class App extends Component{ render(){ return(2)connect()) } }
connect就是一個比較重要的方法啦,它的意思就是把容器組件和UI組件聯(lián)系在一起。這樣只要寫好UI組件,外面用connect()包一層就行啦~
這個應(yīng)用的UI組件和容器組件是這樣的:
前面加大v的就是容器組件,基本都是一一對應(yīng)的關(guān)系,當(dāng)然有些不需要邏輯層的可以只用UI組件就行,比如MailItem這個~
拿Sidebar舉個例子(截取部分):
//sidebar.jsx const Sidebar =({currentSection, unreadcount,trashcount,sentcount,handleCategory,turncompose}) => { return (......上面({})里的參數(shù),基本都是需要外層的容器組件傳給它的。
再看一下vsidebar.jsx里(截取部分):const mapStateToProps = (state) => { return { currentSection: state.currentSection, unreadcount: countunread(state.mails), trashcount: counttrash(state.mails), sentcount: countsent(state.mails) } } const mapDispatchToProps = (dispatch,ownProps) =>{ return { turncompose: () => { dispatch({type: "TURN_COMPOSE"}) }, handleCategory: (tag) =>{ dispatch({type: "SELECT_TAG", tag: tag}) } } } const VSidebar = connect(mapStateToProps,mapDispatchToProps)(Sidebar) export default VSidebar也就是把需要的state用mapStateToProps傳遞,把方法用mapDispatchToProps來傳遞即可~寫法就是return鍵值對~
三、需要注意的細(xì)節(jié)記錄一下這次遇到的奇形怪狀的bug,都是非常細(xì)節(jié)的地方:
1、需要引用的組件一定要寫export!!需要引用的組件一定要寫export!!需要引用的組件一定要寫export!! 重要的話說三遍。我被這個疏忽折磨了一個晚上。?
2、如果要用ref取到input里的value的話,這個被命名的input一定別忘記先定義一下變量,舉個例子:
towhom = v} placeholder = "address"/>如果只是這么寫,在其他地方用towhom.value或者其他towhom的方法就會報錯。
得先寫一行:let towhom;3、寫mapDispatchToProps的時候,一定別忘記dispatch外層包個大括號~
const mapDispatchToProps = (dispatch) => { return { handlecompose: (address,message,subject) => {dispatch({ type:"COMPOSE", from: "Chen Yisha", address:address, time: timeFormat(new Date()), message:message, subject: subject, tag:"sent", read:true })}, deleteemail: (mails,id,tag)=> {dispatch({type: "DELETE_MAIL",mails,id,tag})} } }4、如果UI組件用函數(shù)的方法寫,需要兩個及以上的參數(shù)的時候要加大括號:
//如果只有一個參數(shù): const ComposePart = (display) => {...} //如果有兩個以上參數(shù)(別忘記這個大括號): const ComposePart = ({display, handleCompose}) => {...}四、css部分好啦,功能部分基本完成之后只要加上樣式表就ok~
我這次用的css-module,可以解決全局classname混亂的問題,可以參考下:
https://segmentfault.com/n/13...其實(shí)css還是很強(qiáng)大的,除了解決應(yīng)用漂不漂亮的問題,還可以使用className完成一些邏輯層面的東西。
比如我在多個組件里都用到了style={{display:display}}。后面的display是個參數(shù),可以用mapStateToProps傳進(jìn)去,或者依靠其他的state判斷一下。然后只要用一個三元運(yùn)算符就可以解決要不要這個組件顯示的問題。
比如:// 在vcomposepart.jsx里,需要依靠composeORnot這個reducer判斷用戶有沒有選擇"comopse’,如果選擇了就顯示composepart,沒有選擇就顯示mailList和mailDetail這兩個組件。 // vcomposepart.jsx const mapStateToProps = (state) => { return { display: state.composeORnot? "block":"none" } } //composepart.jsx const ComposePart = ({display, handleCompose}) => { let towhom, subject, mailbody return(......然后還有一些小技巧:
1)想要有投影一邊的box-shadow,可以用:after(用在選擇某個sidebar的tag的時候)
.currentSection{ border-left: 5px solid $green; &:after{ content: ""; background:linear-gradient(90deg, $light-green, #fff); width:30px; height:40px; display: block; position: relative; left:-30px; top:-40px; z-index:-1; } }2) 在整個應(yīng)用外部用一個box-shadow, 我覺得會顯得精致漂亮很多~
.mailbox{ position:absolute; top:50%; left:50%; transform: translate(-50%,-50%);//這三行可以讓整個應(yīng)用居中 height: auto; width:auto; background-color: #fff; box-shadow: 0 0 20px #eee; //注意這行 border-radius: 5px; }3)如果需要幾個組件在x軸或者y軸上排齊,可以在父級上使用flexbox:
.flexb{ display: flex; display: -webkit-flex; flex-direction:row; flex-wrap:nowrap; justify-content:center; height: 500px; }基本就是這樣啦!還有什么問題大家可以看下我的源碼~ 謝謝支持~!
https://github.com/yisha0307/...文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/81754.html
相關(guān)文章
React技術(shù)棧實(shí)現(xiàn)XXX點(diǎn)評App demo
摘要:項目的架構(gòu)也是最近在各種探討研究。還求大神多指點(diǎn)項目技術(shù)總結(jié)技術(shù)棧項目結(jié)構(gòu)探究初體驗(yàn)關(guān)于項目中的配置說明項目簡單說明開發(fā)這一套,我個人的理解是體現(xiàn)的是代碼分層職責(zé)分離的編程思想邏輯與視圖嚴(yán)格區(qū)分。前端依舊使用技術(shù)棧完成。 項目地址:https://github.com/Nealyang/R...技術(shù)棧:react、react-router4.x 、 react-redux 、 webp...
從零開始編寫React-Express單頁博客應(yīng)用(學(xué)習(xí)總結(jié))
摘要:單頁博客應(yīng)用編寫總結(jié)很久之前就想寫一個博客應(yīng)用在一開始想要直接用和模板直接寫但是暑假一開始的時候不小心入了的坑所以就一不做二不休直接用寫那既然用了不寫個單頁應(yīng)用也過意不去了不前前后后寫了將近兩個星期現(xiàn)在看來這其實(shí)是一個很容易的應(yīng)用但是鑒于 React-Express單頁博客應(yīng)用編寫總結(jié) 很久之前就想寫一個博客應(yīng)用.在一開始想要直接用express和ejs模板直接寫, 但是暑假一開始的時...
指尖前端重構(gòu)(React)技術(shù)調(diào)研分析
摘要:一為什么選擇是當(dāng)前前端應(yīng)用最廣泛的框架。目前來看的生態(tài)系統(tǒng)要比大的多,在等最大的技術(shù)社區(qū)搜索兩者,的搜索結(jié)果是的十倍左右,另外據(jù)近期統(tǒng)計使用的站點(diǎn)是的幾百倍以上。其中是基于技術(shù),依然是瀏覽器應(yīng)用。 一、為什么選擇React React是當(dāng)前前端應(yīng)用最廣泛的框架。三大SPA框架 Angular、React、Vue比較。 Angular出現(xiàn)最早,但其在原理上并沒有React創(chuàng)新的性能優(yōu)化...
利用react-redux-tpl快速開發(fā)react-redux-webpack項目
摘要:將它開源出來,希望能給某些同學(xué)帶來幫助,如果有設(shè)計的不好的地方,也希望能及時指出,共同進(jìn)步。和為組件,下的文件一般為直接配合路由使用的包裝組件,為具體業(yè)務(wù)組件,這個地方有的規(guī)范將組件分為三層,我個人認(rèn)為這個必要性不大。 個人學(xué)習(xí)使用react已經(jīng)有一段時間了,雖然沒有在真正的產(chǎn)品項目中大規(guī)模使用react,但是在自己的博客、小網(wǎng)站、一些開源項目中已經(jīng)使用過好幾次了,使用react創(chuàng)建項...
【單頁面博客從前端到后端】基于 DVA+ANTD 搭建博客前后臺界面
摘要:在的的配置中添加自定義主題由腳手架和官網(wǎng)介紹,我們已經(jīng)自己配置并新建好了主題文件。單頁面博客從前端到后端環(huán)境搭建單頁面博客從前端到后端基于搭建博客前后臺界面單頁面博客從前端到后端基于和的權(quán)限驗(yàn)證與的設(shè)計 在上篇文章我們已經(jīng)搭建好了基礎(chǔ)的開發(fā)環(huán)境,接下來會介紹如何引入 DVA 和 ANTD ,以及在引入過程中需要注意的問題。這里只會詳細(xì)的書寫部分組件,其他的組件都是大同小異。你可以在 g...
發(fā)表評論
0條評論
閱讀 3209·2021-11-24 09:39
閱讀 2939·2021-11-23 09:51
閱讀 898·2021-11-18 10:07
閱讀 3548·2021-10-11 10:57
閱讀 2749·2021-10-08 10:04
閱讀 3007·2021-09-26 10:11
閱讀 1052·2021-09-23 11:21
閱讀 2792·2019-08-29 17:28