原文連接: React項目詳解
GITHUB: react-webpack-config
持續更新...
webpack
須要安裝的依賴webpack
,webpack-cli
,react
,react-dom
babel-loader
,@babel/core
,@babel/preset-env
,@babel/preset-react
.babelrc
,{"presets": ["@babel/preset-env","@babel/preset-react"]}
設置scripts
:javascript
"dev": "webpack --mode development", "build": "webpack --mode production"
設置webpack-dev-server
:java
devServer: { compress: true, port: 9000, hot: true }, "start": "webpack-dev-server --config webpack.config.js"
設置performance
:react
performance: { hints: false }
Component
基本組件webpack
let title = <h1>Hello, world!</h1> ReactDOM.render(title,document.getElementById('root'))
動態組件git
import React from 'react'; import ReactDOM from 'react-dom'; let displayTime = () => { let nowTime = ( <div> <span>如今時間:{new Date().toLocaleTimeString()}</span> </div> ); ReactDOM.render(t nowTime, document.getElementById('root') ); }; setInterval(displayTime, 1000);
class
組件構建器github
import React, {Component} from 'react'; import ReactDOM from 'react-dom'; class HelloTitle extends Component { render() { return <h1>Hello,World!</h1> } } ReactDOM.render( <HelloTitle/>, document.getElementById('root') );
props
屬性web
import React, {Component} from 'react'; import ReactDOM from 'react-dom'; class HelloTitle extends Component { render() { return <h1>Hello,{this.props.name}!</h1> } } let titleDiv = ( <div> <HelloTitle name="React"/> <HelloTitle name="World"/> </div> ); ReactDOM.render( titleDiv, document.getElementById('root') );
props
多層使用服務器
import React, {Component} from 'react'; import ReactDOM from 'react-dom'; class HelloTitle extends Component { render() { return <h1>Hello,{this.props.name}!</h1> } } class HelloDiv extends Component { render() { return <div><HelloTitle name={this.props.name}/></div> } } ReactDOM.render( <HelloDiv name="React"/>, document.getElementById('root') );
組件複用babel
import React, {Component} from 'react'; import ReactDOM from 'react-dom'; class HelloTitle extends Component { render() { return <h1 style={this.props.style}>{this.props.content}</h1> } } class HelloDiv extends Component { render() { return <div> <HelloTitle content="比較大的字" style={{'fontSize': 18}}/> <HelloTitle content="比較小的字" style={{'fontSize': 12}}/> </div> } } ReactDOM.render( <HelloDiv/>, document.getElementById('root') );
Component
的狀態state
和生命週期state
屬性constructor(props) { super(props); this.state = { time: new Date().toLocaleTimeString() } } render() { return <h1>如今時間是{this.state.time}</h1> }
組件構建完成後先執行的動做,componentDidMount()
:網絡
componentDidMount() { let upTime = () => { this.setState({time: new Date().toLocaleTimeString()}) }; setInterval(upTime, 1000) }
setState()
修改狀態值this.setState({time: new Date().toLocaleTimeString()})
constructor
中初始化組件內部的資料。render()
在網頁上輸出組件內容。componentDidMount()
進行一次調用。state
值被修改時執行componentDidUpdate()
。componentWillUnmount()
的內容一次。componentDidMount()
render
到實體DOM階段完成的時候觸發;method
只會被呼叫一次;setState()
,並會再次從新render
、component
一次;side effect
的function
,如setInterval
、呼叫API等等。componentWillUnmount()
setState()
;side effect
的function
。class Clock extends Component { constructor(props) { super(props); this.state = { currentTime: new Date().toLocaleString() } } componentDidMount() { this.timer = setInterval(this.updateTime, 1000) } componentWillUnmount() { clearInterval(this.timer) } updateTime = () => { this.setState({ currentTime: new Date().toLocaleString() }) }; render() { const {currentTime} = this.state; return ( <div className="clock"> <div>{currentTime}</div> </div> ) } }
component
各階段的生命週期方法Mounting
):組件一開始呈現到真實網頁的過程class LifeCycle extends Component { constructor(props) { super(props); // 建構式,推動組件初始state設定,綁定方法,接受父級props的地方 // 只會被調用一次 // 不適合使用具備side effect的工做,如AJAX調用 } static getDerivedStateFromProps(nextProps, prevState) { // React V16.3新增的生命週期方法 // 在組件建構以後,render()被調用以前 // 每次接收新的props後回傳object更新state;也能夠回傳null不作更新 } UNSAFE_componentWillMount() { // 即將在React V17中移除,不可與static getDerivedStateFromProps同時出現 // 在render()調用以後被調用,且以後被調用一次 // 會用到的地方僅在於服務器端的應用 // 適合設定初始化數據的地方,不適合作有side effect的工做 } render() { // 在class component中「惟一」必要存在的方法 // 儘可能pure,不該該改變component的state // 如須要和browser有互動的話,將其放進componentDidMount()中 return ( <div></div> ) } componentDidMount() { // 在render()被調用後,組件已經在真實DOM呈現以後被調用執行 // 只會被調用一次,適合執行AJAX、計時器設定、加入eventListener等有反作用的工做 // 調用setState()可能會再執行一次render() } }
class LifeCycle extends Component { UNSAFE_componentWillReceiveProps(nextProps) { // 即將在React V17中移除,不可與static getDerivedStateFromProps同時出現 } static getDerivedStateFromProps(nextProps, prevState) { // React V16.3新增的新生命週期方法 // 在組件構建以後,render()被調用以前 // 每次接收新的props後回傳object更新state;也可回傳null不作更新 // 搭配componentDidUpdate,能夠達到與componentWillReceivedProps的效果 } shouldComponentUpdate() { // 讓使用者自定義是否進行component更新,預設都會默認更新 // 只有少部分的狀況纔會使用此方法,如進行復雜耗時的計算 // 儘可能不要檢查太過深層的值,也不適合執行具備side effect的工做 } UNSAFE_componentWillUpdate(nextProps, nextState) { // 即將在React V17中移除,不可與static getDerivedStateFromProps同時出現 // 在組件即將更新「以前」調用 // 不能在此使用setState方法,若要改變請用getDerivedStateFromProps } getSnapshotBeforeUpdate(prevProps, prevState) { // 在render以後,在最新的渲染前輸出交給真實DOM前會當即執行 // 返回的值做爲componentDidUpdate的第三個值 // 搭配componentDidUpdate,能夠達到與componentWillUpdate的效果 } componentDidUpdate(prevProps, prevState, snapshot) { // 會發生在更新「以後」被調用 // 若是shouldComponentUpdate回傳的是false,就不會調用此方法 // 適合執行具備side effect的地方 } }
class LifeCycle extends Component { componentWillMount() { // 在組件即將要移除真實DOM時會執行 // 能夠在這裏作中斷網絡鏈接、清楚計時器、移除事件監聽器 // 不適合使用setState方法 } }
catch error
class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { hasError: false } } componentDidCatch(error, info) { // 是error handle的方法,與掛載和更新階段有密切關係 // 能夠用來捕捉子組件的任何JavaScript的錯誤 // 能夠記錄這些錯誤,或是呈如今目前反饋用的操做界面上 // 不能在事件的callback上使用 // 顯示在反饋的UI上 this.setState({hasError: true}); // 也能夠用額外的錯誤記錄服務 logErrorMyService(error, info); } render() { if (this.state.hasError) { return <h1>Something went wrong...</h1> } return this.props.children } }
上面就是component
可使用的生命週期方法,最經常使用主要是這些:
constructor()
render()
componentDidMount()
compoinentDidUpdate()
componentWillUnmount()
Component
的事件處理class InputGender extends Component { constructor(props) { super(props); this.state = { gender: '' }; this.changeGender = this.changeGender.bind(this) } changeGender(event) { console.log(event.target.value); this.setState({ gender: event.target.value }); } componentDidUpdate() { console.log(`已將state.gender變更爲:${this.state.gender}`) } render() { return ( <select onChange={this.changeGender}> <option value="M">男</option> <option value="W">女</option> </select> ) } }
class HelloTitle extends Component { render() { return <h1>{this.props.title}</h1> } } {(this.state.gender === 'M') ? <HelloTitle title="先生"/> : <HelloTitle title="女士"/>}
state
取得輸入資料class EasyForm extends Component { constructor(props) { super(props); this.state = { name: "" }; this.changeState = this.changeState.bind(this); this.submitForm = this.submitForm.bind(this); } changeState(event) { this.setState({ name: event.target.value, }); } submitForm(event) { let element = document.querySelector('span'); element.innerHTML = `${this.state.name}`; event.preventDefault() } render() { return ( <div> <form onSubmit={this.submitForm}> <label>姓名:</label> <input id="name" name="name" onChange={this.changeState} value={this.state.name}/> <input type="submit" value="提交" style={{'marginLeft': 6}}/> </form> <p>如今輸入的名字是:<span style={{'color': '#FF7F24'}}></span></p> </div> ) } }
class EasyForm extends Component { constructor(props) { super(props); this.state = { lists: [ {id: '01', listName: '寫文章', check: false}, {id: '02', listName: '寫代碼', check: false}, {id: '03', listName: '旅遊', check: true}, {id: '04', listName: '踢球', check: true}, {id: '05', listName: '公益', check: false}, ] }; this.submitForm = this.submitForm.bind(this); this.changeState = this.changeState.bind(this); } changeState(index) { let arrLists = this.state.lists; arrLists[index].check ? arrLists[index].check = false : arrLists[index].check = true; this.setState({ lists: arrLists, }); } submitForm(event) { let status = "目前作了:"; this.state.lists.map((list) => list.check ? status += `${list.listName} ` : ''); console.log(status); event.preventDefault() } render() { let lists = this.state.lists.map((list, index) => ( <div key={list.id}> <input type="checkbox" checked={list.check} onChange={this.changeState.bind(this, index)} key={list.id} /> <label>{list.listName}</label> </div> )); return ( <form onSubmit={this.submitForm}> <div> <label>每日待辦清單:</label> {lists} </div> <input type="submit" value="發送表單"/> </form> ) } }
class EasyForm extends Component { constructor(props) { super(props); this.submitForm = this.submitForm.bind(this); this.filebox = React.createRef() } submitForm(event) { console.log(`選擇文檔爲:${this.filebox.current.files[0].name}`) event.preventDefault() } render() { return ( <form onSubmit={this.submitForm}> <div> <label>上傳文檔:</label> <input type="file" ref={this.filebox}/> </div> <input type="submit" value="送出表單"/> </form> ) } }
refs
操做DOMclass App extends Component { constructor() { super(); this.state = { itemList: [] }; this.addFile = React.createRef(); } addItem = () => { const {itemList} = this.state; const tempList = Object.assign([], itemList); if (this.addFile.current.value !== '') { tempList.push(this.addFile.current.value); } this.setState({ itemList: tempList }); this.addFile.current.value = '' }; render() { const {itemList} = this.state; return ( <div className="App"> <input type="text" name="addFile" ref={this.addFile}/> <input type="button" onClick={this.addItem} value="ADD"/> <ul className="list"> {itemList.map((item, index) => <li key={`item_${index}`}>{item}</li> )} </ul> </div> ); } }
TODOLIST
TodoList.js
class TodoList extends Component { constructor(props) { super(props); this.state = { list: [] } } addItem = (text) => { const {list} = this.state; if (text !== '') { const tempArr = list.concat({ id: list.length + 1, text, status: false }); this.setState({list: tempArr}) } }; toggleStatus = (id) => { const {list} = this.state; const tempArr = list.map(item => { if (item.id.toString() === id.toString()) { return ({ id: item.id, text: item.text, status: !item.status }) } return item; }); this.setState({list: tempArr}) }; render() { const {list} = this.state; const divStyle = { width: '250px', margin: 'auto', textAlign: 'left' }; return ( <div style={divStyle}> <TodoForm onAddItem={this.addItem}/> <ul> {list.map(item => ( <TodoItem key={item.id} id={item.id} status={item.status} onItemClick={this.toggleStatus} > {item.text} </TodoItem> ))} </ul> </div> ) } }
TodoForm.js
class TodoForm extends Component { constructor(props) { super(props); this.inputRef = React.createRef(); } formSubmit = (e) => { const {onAddItem} = this.props; e.preventDefault(); onAddItem(this.inputRef.current.value); this.inputRef.current.value = '' }; render() { return ( <form onSubmit={this.formSubmit}> <input type="text" name="todoItem" ref={this.inputRef} autoComplete="off"/> <button type="submit" value="submit">submit</button> </form> ) } }
TodoItem.js
class TodoItem extends Component { handleItemClick = (e) => { const {onItemClick} = this.props; onItemClick(e.target.id) }; render() { const {children, id, status} = this.props; return ( <li id={id} onClick={this.handleItemClick} data-status={status} style={ status ? {textDecoration: 'line-through'} : {textDecoration: 'none'} } > {children} </li> ) } }