現在開発中の自社製品に対し、パフォーマンスを初めて意識した際にまだ特に大きく改善する点が見当たらなかったというだけの話
ReactͷύϑΥʔϚϯεΛվળ͢Δඞཁ͕ͳ͔ͬͨ
View Slide
ࣗݾհɿືɹٛ໌• ϑϩϯτΤϯυΤϯδχΞ• νʔϜεϐϦοτʹೖࣾ• ήʔϜେ͖• ࣾͷےτϨڭʹೖ৴ͭͭ͋͠Δ
ຊͷ͓ωοτͰ࠷ۙReactͷύϑΥʔϚϯεΛվળͨ͠ΛΑ͘ݟΔͷͰࣗͰͬͯΈͨΒಛʹඞཁͳ͔ͬͨͱ͍͏
લఏͱͯ͠ReactͷಛΛৼΓฦΔ
Reactͷ̏ͭͷಛ• JUST THE UI• VIRTUAL DOM• DATA FLOWVIRTUAL DOMʹΑΓඞཁ࠷ݶͷύϑΥʔϚϯεकΒΕ͍ͯΔ
͋Γ͕ͱ͏͓ΘΓ
͏গ͠ଓ͚·͢ɾɾɾ
·ͱΊ• ReactVirtul DomͰͷࠩߋ৽Λߦ͏ͨΊɹ࠷ॳ͔Β͋ΔఔͷύϑΥʔϚϯε͕อͯΔΑ͏ʹग़དྷ͍ͯΔɻ
վΊͯDOM͍ͭߋ৽͞ΕΔͷ͔ʁ
Stateͷߋ৽
containeramountΛߋ৽State͕ߋ৽࣌ɺ࠶ϨϯμϦϯά͕ൃੜίϯϙʔωϯτ܈$PNQPOFOU% $PNQPOFOU& $PNQPOFOU'$PNQPOFOU$$PNQPOFOU#$PNQPOFOU"TFU4UBUF \BNPVOUWBMVF^࣮ࡍʹBNPVOUΛ͏ίϯϙʔωϯτ
ٯΛݴ͏ͱɾɾɾ
containersetStateʹΑΓมVΛߋ৽state͕ίϯϙʔωϯτʹ͍ͬͯΕɺྫ͑ະ༻Ͱ࠶ϨϯμϦϯά͕Δίϯϙʔωϯτ܈ίϯϙʔωϯτ$PNQPOFOU% $PNQPOFOU& $PNQPOFOU'$PNQPOFOU$$PNQPOFOU#$PNQPOFOU"BNPVOUΛνΣοΫBNPVOUΛʜBNPVOUΛνΣοΫBNTFU4UBUF \BNPVOUWBMVF^࣮ࡍʹBNPVOUΛ͏ίϯϙʔωϯτ
࠶ϨϯμϦϯάͷճΛݮΒ͢ʹʁ
stateઙ͘อͪɺ֤ίϯϙʔωϯτͷߋ৽ʹඞཁͳͷ͚ͩ͢Α͏৺͕͚Δthis.state = {// ඞཁͳstateΛҰͭͷΦϒδΣΫτʹશͯ֨ೲ͍ͯ͠ΔύλʔϯmasterObject: {date: ‘2017-5-15’,amount: 3000,name: ‘tawashi’,}};this.state = {// stateΛׂͨ͠date: ‘2017-5-15’,amount: 3000,name: ‘tawashi’,};
Spread AttributesChildrenͳͲͷ͝ར༻ܭըతʹrender() {// Spread Attributesreturn ;}• όέπϦϨʔʹർΕͨਓͷٹੈओ• ͝ར༻ܭըతʹ
ෆཁͳpropsͷड͚͠ΛࢭΊΔclass MeatMarker extends Component {render() {return A={this.props.A}B={this.props.B}V={this.props.V} />;}}• ԼҐίϯϙʔωϯτʹ͢ඞཁͳͷ͚ͩʹ͠·͠ΐ͏• ϨϏϡʔݟ͔ͬ͠͠Γͱ
ReactϙϦεʢϨϏϡʔΞʣͱ͔ͯͬ͠͠Γࣄ͍͖ͯ͠·͠ΐ͏
·ͱΊ• ReactVirtul DomͰͷࠩߋ৽Λߦ͏ͨΊɹɹ࠷ॳ͔Β͋ΔఔͷύϑΥʔϚϯε͕อͯΔΑ͏ʹग़དྷ͍ͯΔɻ• React͕ύϑΥʔϚϯεΛൃشͰ͖ΔΑ͏ʹߏ͞Ε͍ͯΔ͔ৗʹ֬ೝ͢Δɻ
͏ҰؤுͬͯΈΔ
componentWillUpdate(nextProps) {console.log(this.state.amount);console.log(nextProps.amount);}// લճͱඳը݁Ռ͕Ұॹ͕ͩ࠶ϨϯμϦϯά࣮ࢪ͞ΕΔrender(){ ɹˡɹreturn ({amount});}ߋ৽ͷલޙͰState͕ಉҰͷ߹Ͱ࠶ϨϯμϦϯά͕Δ
shouldComponentUpdate
shouldComponentUpdateclass MeatMarker extends Component {shouldComponentUpdate: function(nextProps, nextState) {if (this.props.userName !== nextProps.userName) {return true;ɹˡ update࣮ࢪ} else {return false;ɹˡɹupdate͞Εͳ͍}}• ίϯϙʔωϯτΛ࠶ඳը͢Δ͔Ͳ͏͔Λܾఆ͢Δ• updateલʹ࣮ߦ͞ΕΔɻ• ΓͰfalseΛฦͤupdate࣮ࢪ͞Εͳ͍ɹ(σϑΥϧτͰඞͣtrue͕ฦΓ·͢)
shouldComponentUpdateΑ͋͘Δ࣮shouldComponentUpdate: function(nextProps, nextState) {// user໊͕ߋ৽લޙͰಉҰͳΒߋ৽ॲཧ࣮ࢪ͠ͳ͍return this.props.userName !== nextProps.userName;}̍.݅Λઃܭ࣮ͯ͠2.lodashimmutable.jsΛར༻࣮ͨ͠shouldComponentUpdate: function(nextProps, nextState) {// ߋ৽લޙͰ͕มΘ͍ͬͯͳ͚Εɺupdate࣮ࢪ͠ͳ͍const propsDiff = _.isEqual(nextProps, this.props);const stateDiff = _.isEqual(nextState, this.state);return !(propsDiff && stateDiff);}
PureComponentͰOKclass MeatMarker extends Component {constructor(props) {super(props);ɹɹɹɹɹɹɹ!//extendsઌΛPureComponentʹมߋ͢Δʮ͚ͩʯͰར༻Ͱ͖Δclass MeatMarker extends PureComponent {constructor(props) {super(props);• React v15.3Ҏ߱Ͱ࣮͞Ε͍ͯΔ• shouldComponentUpdateΛཪͰ࣮• શͯͷstateͱpropsʹshallowͳൺֱΛ࣮ࢪ
೦ͷͨΊͷshallowͳൺֱͷ͓͞Β͍̍ճapi.get("/api/user",(req, res) => { this.setState({user:res}) );console.log(this.state.user);݁Ռ{user: ‘minato’ , company: ‘TeamSpirit’}̎ճapi.get("/api/user",(req, res) => { this.setState({user:res}) );console.log(this.state.user);݁Ռ{user: ‘minato’ , company: ‘TeamSpirit’}̍ճͱ̎ճͰผͷΦϒδΣΫτΛੜ͍ͯ͠ΔͷͰtrue͕ؼΓ·͢
WARNING!
Կߟ࣮͑ͣ͢Δͷ·͍ͣclass BaseComponent extends React.Component {shouldComponentUpdate: function(nextProps, nextState) {const propsDiff = _.isEqual(nextProps, this.props);const stateDiff = _.isEqual(nextState, this.state);return !(propsDiff && stateDiff);}}ɹྫ͑ҎԼͷΑ͏ʹߋ৽લޙͷstateɺpropsΛൺֱ͢ΔBaseClassΛ࣮͠ɺશͯͷclassͰܧঝͯ͠ൺֱॲཧΛ࣮͢ΔͱɺٯʹύϑΥʔϚϯε͕Լ͢ΔՄೳੑ͕͋Δʢͱݴ͏͔͢Δʣ
Ͳ͜ʹ࣮͖͔ͪ͢ΌΜͱݕ౼͔ͯ͠Βʹ͠·͠ΐ͏
·ͱΊ• ReactVirtul DomͰͷࠩߋ৽Λߦ͏ͨΊɹɹ࠷ॳ͔Β͋ΔఔͷύϑΥʔϚϯε͕อͯΔΑ͏ʹग़དྷ͍ͯΔɻ• React͕ύϑΥʔϚϯεΛൃشͰ͖ΔΑ͏ʹߏ͞Ε͍ͯΔ͔ৗʹ֬ೝ͢Δɻ• shouldComponentUpdateͰߋ৽લޙͷ͕ಉҰͷ߹ʹߋ৽ॲཧΛߦΘͳ͍Α͏ʹ͢Δ
͜͜·Ͱ༧
͔͜͜Βରྍ๏
ύϑΥʔϚϯεͷϘτϧωοΫΛ୳͢
PerfͷಋೖɻPerfͱԿ͔ʁ• ίϯϙʔωϯτͷඳըʹ͔͔Δ࣌ؒΛௐΔ͜ͱ͕Ͱ͖Δπʔϧ• ϒϥβͷίϯιʔϧ্Ͱಈ࡞͢Δ↓͜Μͳײ͡ͷ݁Ռ͕ग़ͯͲ͜ʹ͕͔͔͍࣌ؒͬͯΔ֬ೝͰ͖Δ
PerfͰͷݪҼಛఆͱରॲํ๏import React from ‘react';import Perf from 'react-addons-perf';class MeatMaker extends React.Component {componentDidMount() {window.Perf = Perf;};}1. componentDidMountͰPerfΛWindowʹηοτ͢ΔɻReactιʔείʔυ
PerfͰͷݪҼಛఆͱରॲํ๏2. Perf.start()Λ࣮ߦɻ3. ͖ͳ͚ͩΞΫγϣϯΛى͜͢ɻ4. Perf.stop()Λ࣮ߦɻ5. getLastMeasurementsͰ༰Λ֬ೝ͠ɺ͕͔͔͍࣌ؒͬͯΔՕॴΛ֬ೝɻϒϥβίϯιʔϧ͜͜ΒลͳΜ͔ͦ͏
PerfͰͷݪҼಛఆͱରॲํ๏ʢผղʣimport React from ‘react';import Perf from 'react-addons-perf';class MeatMaker extends React.Component {componentDidMount() {window.Perf = Perf;}render (){return ( {Perf.start()}{Perf.stop()} );}}ίϯϙʔωϯτͷಡΈࠐΈલޙͰstartɺstopΛ࣮ࢪɺ֬ೝͦͷޙϒϥβ͔ΒߦͬͯOK
ݪҼॴΛಛఆͨ͠ޙͷରॲํ๏ʁجຊલ߲·ͰͷLTͷ༰Λࢥ͍ฦ͍͚ͯͨͩ͠Εେৎʢͷͣʣʂ
۩ମతʹʁʢ͓͞Β͍ʣ• ߏΛݟ͠• ෆཁͳpropsͷআ• shouldComponentUpdate(PureComponent)ͷ࣮
ݪҼͱରॲฤʢ͓·͚ʣgetSubContainer() {switch (this.state.selectedKey) {case 'meat':return ();case 'chicken':return ();default :return null;}}Window.Perf.start()Λ࣮ߦ↓ຖճίϯϙʔωϯτΛ࠶࡞͍ͯ͠ΔͷͰॏ͍
ݪҼͱରॲฤʢ͓·͚ʣrenderSubContainer() {{const meat = this.state.selectedKey === ‘meat’ }{const chicken = this.state.selectedKey === ‘chicken’ }return (););}ঢ়گʹԠͯ͡ίϯϙʔωϯτΛඇදࣔʹ͠ɺ࠶࡞ࣙΊͯΈͨ
࠶ϨϯμϦϯάૣ͘ͳͬͨʂ
͕
ॳظදࣔ͘ͳΓϝϞϦͷৗ࣌ͷ༻ྔ૿͑ͨ
ଥͳτϨʔυΦϑ͔ݕূඞཁΫϫΨλखฤΈͷϚϑϥʔ
·ͱΊ• ReactVirtul DomͰͷࠩߋ৽Λߦ͏ͨΊɺ࠷ॳ͔Β͋ΔఔͷύϑΥʔϚϯε͕อͯΔΑ͏ʹग़དྷ͍ͯΔɻ• React͕ύϑΥʔϚϯεΛൃشͰ͖ΔΑ͏ʹߏ͞Ε͍ͯΔ͔ৗʹ֬ೝ͢Δɻ• shouldComponentUpdateͰߋ৽લޙͷ͕ಉҰͷ߹ʹߋ৽ॲཧΛߦΘͳ͍Α͏ʹ͢Δɻ• ϨϯμϦϯά͕͍߹PerfΛͬͯݪҼΛಛఆ͠ɺରॲྍ๏Λ࣮͢Δɻ࣮ܭըతʹ
࠷ޙʹࠓߋͰ͕͢λΠτϧͷ݅ʹ͍ͭͯ
ॳΊͯฐࣾͷͷύϑΥʔϚϯεΛҙࣝͨ࣌͠• ฐࣾͷϓϩμΫτࠒͷϨϏϡʔΛ͋Δఔ͔ͬ͠Γ͍ͬͯͨ͜ͱ͋Γɺɹɹɹɹผʹࠓ͙͢ύϑΥʔϚϯεΛҙࣝͨ͠मਖ਼Λ࣮ࢪ͠ͳ͍͍͔ͯ͘ͳɻͱ͍͏ͷ͕࠷ॳͷײͰͨ͠ɻ• ࣮ͯ͠ମײͰ͖Δఔͷ͕ࠩ͋·Γແ͘ɺଞͷݕ౼ࣄ߲͕ग़ͦ͏ͩͬͨ• ϚδͰࠔͬͨΒPerfͰͳΜͱ͔͢Δ
·ͱΊͷ·ͱΊ• ReactVirtual Domͷࢥ͋ΓɺҙࣝͤͣͱύϑΥʔϚϯε͋Δఔग़Δɻ• ઃܭͱࠒͷϨϏϡʔ͕େࣄɻઃܭࠒͷϨϏϡʔ͕ສશͳΒɺͦͦύϑΥʔϚϯεͷվળࣗମ͕ෆཁͳ͜ͱଟ͍ɻ• ϚδͰࠔͬͨΒPerfͰͳΜͱ͔͢Δ
ྑ͍ReactϥΠϑΛ
͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠