Hi!大家好啊!因為大家的支持!鐵人賽總算過一半了!希望剩下來的賽程請各位大大繼續指教!
話說今天為了研究一個東西浪費了一堆時間,讓我現在感覺很沒勁,哈哈哈(超級苦笑XD)!我們準備要進入最後的React Router
篇了(雖然今天可能還不會提到主角本身XD),為了迎接他,這一篇先來了解React
的目錄結構!
先前我們做的那些練習,都把jsx
、js
、html
包含設定檔全部都丟在一起,相信如果有強迫症的大大,從第一篇忍到現在一定看得很辛苦!其實我在一開始就想要提目錄結構了,但是因為還有Redux
的關係,就想說一次講一講,在這邊和各位大大說聲不好意思!
說到結構這種東西,真的沒有基本的說法,有時候隨著公司的案子結構,有時候又照著維護案的結構,但是還是有主流的一個基本結構,至於要怎麼調整,大家可以為自己的目錄結構做個客製化XD,那以下先說說最簡單的基本目錄:
以下解釋一下每個資料夾的內容及用途:
package.json
和webpack.config.js
等設定檔。index.html
和負責頁面渲染的index.jsx
,雖然他最後會被webpack
打包成index_bundle.js
。index.js
將export
的內容匯出,這樣在其他文件中import
的時候看起來比較不會亂,而Main
組件負責最後的輸出,而這裡的Main
組件也只是一個頁面,當然也可以在實務上建立各個頁面匯出的總組件,所以也可以改成這樣子放:index.js
統一匯出,因為當我們使用import
時,會預設取目錄文件的index.js
,詳細說明可以看這裡。有了目錄看起來專案的樣子是不是完整多了XD,可是為了保持整個目錄的結構,我們的程式碼也需要做相對應的修改:
webpack.config.js
:src
中,所以打包的目錄和輸出的目錄都要更改。
const path = require('path');
module.exports = {
//這個webpack打包的對象,這裡面加上剛剛建立的index.js
entry: {
index: ['./src/index.jsx']
},
output: {
//這裡是打包後的檔案名稱
filename: 'src/index_bundle.js',
//打包後的路徑,這裡使用path模組的resolve()取得絕對位置,也就是目前專案的根目錄
path: path.resolve('./'),
},
module:{
rules:[
{test:/\.jsx$/,use:{loader:'babel-loader',options:{presets:['@babel/preset-react','@babel/preset-env']}}},
{test:/\.js$/,use:{loader:'babel-loader',options:{presets:['@babel/preset-env']}}}
]
}
};
Title
和Main
組件:
Title/Title.jsx
:
import React from "react"
class Title extends React.Component {
render(){
return <h1>{this.props.title}</h1>
}
}
//把該目錄的Title組件匯出
export {Title}
Title/index.js
:import
和export
寫在一起的寫法,等於直接匯出剛剛在Title.jsx
中export
的所有物件。
export * from "./Title.jsx"
Main/Main.jsx
:import
進Title
匯出的物件,因為前面有提到預設是index.js
,所以只需要指定到目錄就好,如此一來就能夠使用Title
組件了,最後一樣再把他匯出:
import React from "react"
import {Title} from "../Title"
class Main extends React.Component{
render(){
return <Title title="Hello!World!" />
}
}
export {Main}
Main/index.js
:export * from "./Main.jsx"
index.jsx
:ReactDOM.render
輸出到root
中:
import React from "react"
import ReactDOM from "react-dom"
import {Main} from "./components/Main"
ReactDOM.render(<Main />,document.getElementById('root'));
完成後就可以試著打包或使用webpack-dev-server
執行:
是不是覺得反而花更大的心力在處理這些事情了XD,不過在專案越來越大的時候,這樣子的分類反而不會搞混各個組件的位置!以下是基本目錄結構的GitHub連結:
GitHub目錄連結
基本的沒問題後,接著要來加入Redux
了!所以應該算稍微進階吧XD,不過我一直一直在猶豫要不要用之前的留言版,因為調整目錄就幾乎整個都要變動了,會改到超級多東西(這邊有點浮誇啦XD),雖然猶豫到最後我還是Do了!哈哈哈!那下面就一起來改寫吧!目標是這樣子:
應該比第一次看的時候還要清楚多了!以下解釋每個資料夾和檔案的用途:
index.js
統一匯出。action-types.js
和要給store
管理的資料。Reducer
,一樣由index.js
匯出。store
,一樣由index.js
匯出。接下來就重頭戲了,開始改寫之前留言板的內容吧!
action-types.js
:export const ADD_MESSAGE = "ADD_MESSAGE"
2.models.js
:const data = {message:[{id:'1',name:'神Q',message:'嗨!大家好啊!'},
{id:'2',name:'小馬',message:'早安啊!昨天有沒有好好發文?'},
{id:'3',name:'王子',message:'ㄛ!別說了,那真的超級累!'},
{id:'4',name:'神Q',message:'哈哈哈!加油啦!再一下就結束了!'},
{id:'5',name:'王子',message:'結束後我一定要爆睡一頓!'},]}
export {data}
message.js
:action-types.js
中匯入指令後建構動作並匯出:
import {ADD_MESSAGE} from "../constants/action-types.js"
export const addMessage = message => ({
type : ADD_MESSAGE, payload : message
})
index.js
:export * from "./message.js"
messageReducer.js
:models.js
的資料和message.js
建構的動作做成一個reducer
,一樣在最後把他匯出:
import {ADD_MESSAGE} from "../constants/action-types.js"
import {data} from "../constants/models.js"
const messageReducer = (state = data,action) =>{
switch(action.type){
case ADD_MESSAGE:{
action.payload.id = String(state.message.length+1)
return { ...state, message: [...state.message, action.payload] }
break
}
default:{
return state
break
}
}
}
export {messageReducer}
index.js
:reducer
並匯出:
export * from "./messageReducer.js"
store
:
configureStore.js
匯入reducer
來產生store
:
import {createStore} from "redux"
import {messageReducer} from "../reducers"
const store = createStore(messageReducer)
export {store}
index.js
負責整理匯出:
export * from "./configureStore.js"
connect
後才交給Main
去組合出最後的頁面匯出:
InputMessage.jsx
:
addMessage
這個動作。mapDispatchToProps
指定對store
執行dispatch
動作。connect
,產生InputMessage
並匯出import React from "react"
import {connect} from "react-redux"
import {addMessage} from "../../actions"
class ConnectInputMessage extends React.Component {
constructor(props){
super(props)
this.state = ({name:'',message:''})
this.changeState = this.changeState.bind(this)
this.clearMessage = this.clearMessage.bind(this)
this.submitMessage = this.submitMessage.bind(this)
}
changeState(event){
this.setState({[event.target.name]:event.target.value})
}
clearMessage(){
this.setState({name:'',message:''})
}
submitMessage(){
let messageData = {
name:this.state.name,
message:this.state.message,
}
this.props.addMessage(messageData)
this.clearMessage()
}
render(){
return(
<div>
暱稱:<input type="text" name="name"
value={this.state.name}
onChange={this.changeState} />
<br/>
訊息:
<br/><textarea name="message"
value={this.state.message}
onChange={this.changeState}></textarea>
<input type="button" value="送出留言"
onClick={this.submitMessage} />
</div>
)
}
}
const mapDispatchToProps = dispatch => {
return {
addMessage : message =>{ dispatch(addMessage(message)) }
}
}
const InputMessage = connect(null,mapDispatchToProps)(ConnectInputMessage)
export {InputMessage}
MessageList.jsx
:
mapStateToProps
指定要向state
取的資料。ConnectMessageList
和mapStateToProps
做connect
產生MessageList
後匯出。import React from "react"
import {connect} from "react-redux"
class ConnectMessageList extends React.Component {
render(){
let message = this.props.message.map((item)=>{
return <li key={item.id}>{item.name}:{item.message}</li>
})
return(
<ul>
{message}
</ul>
)
}
}
const mapStateToProps = state =>{
return {message : state.message}
}
const MessageList = connect(mapStateToProps)(ConnectMessageList)
export {MessageList}
Main.jsx
:MessageList
和InputMessage
組件,並將它們組合起來後匯出:
import React from "react"
import {MessageList} from "../MessageList"
import {InputMessage} from "../InputMessage"
class Main extends React.Component{
render(){
return (
<div>
<InputMessage />
<MessageList />
</div>
)
}
}
export {Main}
idnex.js
的內容,但是一樣都是他們提供匯出的功能,就和第一段一樣,我就不再另外PO了!只是不能忘記哦!index.js
的HTML
和上一個例子一樣,差別在要打包的index.jsx
內容:store
和連結react
及redux
用的Provider
組件,之後把Main
包在Provider
中用render
渲染到畫面上。
import React from "react"
import ReactDOM from "react-dom"
import {Provider} from "react-redux"
import {store} from "./store"
import {Main} from "./components/Main"
ReactDOM.render(<Provider store={store}>
<Main />
</Provider>
,document.getElementById('root'));
雖然看起來很複雜,但是經過調整基本架構後再加入redux
進目錄中,感覺就比較不會那麼卡手了,只要對各個資料夾及檔案負責的內容清楚,剩下就只是import
和export
的事情而已,這裡附上留言板的目錄架構:
GitHub目錄架構連結
因為是留言板,所以也有Page:GitPage連結
小弟我第一次就拿留言板來改,結果怎麼改都錯誤,才決定砍掉從練從Hello!World!
開始,搞到最後這篇感覺像半複習一樣XD,還請各位多多包涵,另外因為一開始就有提到說,目錄架構真的有非常非常多種,幾乎沒有任何一種標準答案,所以如果大大們有推薦的架構或可以改進的,麻煩留言告訴我!小弟我感激不盡
最後要感謝各位大大的觀看,如果文中有任何錯誤或解釋不清楚的地方,還麻煩各位大大留言告訴我,小弟會盡快修改會補充文章內容的,謝謝大家
參考資料:
這篇的內容,好豐富啊......等之後有空時,再來照大大練習。 謝謝!
我以為大大是創作派的!
原來是我有眼不識泰山
之前維護的 Angular 專案也有類似 index.js
的用法,每個資料夾下會有一個檔案用來整理匯出,不過我不喜歡這種用法,哈哈哈。
其實我是第一次接觸到這種用法,
因為目前用React
做的都是小作品,
所以只有覺得在import
時寫起來很簡潔很方便,
不曉得大大有甚麼看法