Upgrade to Pro — share decks privately, control downloads, hide ads and more …

既存プロダクトのViewをReactに置き換える

boiyama
December 15, 2016

 既存プロダクトのViewをReactに置き換える

【ヒカ☆ラボ】最新フロントエンドフレームワークを利用した開発事例!
https://atnd.org/events/83403
用の資料です

boiyama

December 15, 2016
Tweet

More Decks by boiyama

Other Decks in Programming

Transcript

  1. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ৽ٕज़Λݕ౼ͨ͠എܠ ▸ 4೥લʹ࡞ΒΕͨۀ຿γεςϜ ▸ ϑϩϯτΤϯυFWʹ͸Backbone.jsΛ࠾༻ ▸ ػೳͷܧ͗଍͠Λ܁Γฦ͍ͯͬͨ݁͠ՌɺDOMૢ࡞ͷॲཧ͕ᷓΕ͔͑ͬͨ Backbone ViewʹͳΔ

    ▸ ΋͏গ͠εϚʔτʹUIΛ࡞ΕΔํ๏ʹม͑ΔͨΊɺjQueryʹґଘ͠ͳ͍React ͱAngular2͕ީิʹ্͕Δ ▸ Ͱ͖Ε͹طଘࢿ࢈΋׆༻͍ͨ͠ͱ͍͏͜ͱͰɺUI·ΘΓͷΈΛReactʹஔ͖׵ ͑ΔํࣜͱɺAngular2Ͱ1͔Β࡞Γ௚͢ํࣜͷ̎௨Γݕ౼͢Δ͜ͱʹͳͬͨ
  2. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Reactʹ͍ͭͯ ▸ MV*Ͱ͍͏ViewͷΈఏڙ͢ΔϑϨʔϜϫʔΫ ▸ React͚ͩͳΒ֮͑Δ͜ͱ͸গͳ͍Ͱ͢ ▸ React ComponentΫϥεΛܧঝ͠ɺHTML, CSS,

    JSͷͻͱ͔ͨ·ΓΛఆٛͨ͠ίϯ ϙʔωϯτΛ࡞Δͷ͕جຊతͳ࢖͍ํͰ͢ɻ ▸ ίϯϙʔωϯτ͸ɺJSXͱ͍͏จ๏Λ࢖ͬͯHTMLͷΑ͏ʹݺͼग़͠·͢ʢྫɿ <Example hoge=“fuga” />ʣ ▸ ίϯϙʔωϯτʹ౉ͨ͠஋͸propsɺίϯϙʔωϯτ͕࣋ͭ஋͸stateʹೖΓɺͦΕ Βͷ஋͕ߋ৽͞ΕΔͱίϯϙʔωϯτ͕࠶ඳը͞Ε·͢ ▸ Πϕϯτॲཧ͸λάଐੑʹઃఆ͠·͢ʢྫɿ<Example onClick={handleClick} />ʣ
  3. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Backboneʹ͍ͭͯ ▸ RESTful APIͱͷ࿈ܞΛγϯϓϧʹந৅Խɻେ͖̏ͭ͘ͷػೳΛ࣋ͪ·͢ ▸ Model: APIͷ୯਺Ϧιʔεͱಉظ ▸ Collection:

    Modelͷू߹ͰɺAPIͷෳ਺Ϧιʔεͱಉظ ▸ View: ඥͮ͘Model΍CollectionͷσʔλΛDOMʹ൓ө͠ɺDOMͷϢʔβೖྗ஋ ΛModel΍Collectionʹ൓ө
  4. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ReactԽ Part.1 : ΊͪΌΊͪΌബ͘ReactΛ࢖͏ ▸ Ұ൪؆୯ͳ࢖͍ํͰ͢ɻ ؆୯ʹಋೖͰ͖·͕͢ɺ͜ͷ࢖͍ํͰReact࢖͍͍ͨਓ͍ͳ͍ͱ ࢥ͏ͷͰɺࢀߟఔ౓ʹ঺հ͠·͢ ▸

    ࢀߟURL ▸ https://facebook.github.io/react/docs/installation.html#using-a-cdn ▸ https://facebook.github.io/react/docs/react-without-es6.html ▸ https://facebook.github.io/react/docs/react-without-jsx.html ▸ ຊདྷReact͸ɺYarn,npm౳ͷύοέʔδϚωʔδϟɺwebpack,Browserify౳ͷόϯυϥɺ Babel౳ͷίϯύΠϥΛซͤͯ׆༻͢Δ͜ͱΛਪ঑͍ͯ͠·͕͢ɺࠓճ͸ͦΕΒͷπʔϧ Λ࢖Θͣ࢖͏ํ๏Ͱ͢
  5. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Α͋͘ΔBackboneϓϩδΣΫτͷྫ ▸ htmlʹςϯϓϨʔτΛॻ͍ͯɺ <script type="text/template" id="item-template"> <div class="view"> <input

    class="toggle" type="checkbox" <%= completed ? 'checked' : '' %>> <label><%- title %></label> <button class="destroy"></button> </div> <input class="edit" value="<%- title %>"> </script>
  6. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ςϯϓϨʔτΛReactԽ͢Δ ▸ htmlͷςϯϓϨʔτΛফͯ͠ɺReact ComponentΛ࡞Γ·͢ var Item = React.createClass({ render:

    function() { return React.createElement('div', null, React.createElement('div', {className: 'view'}, React.createElement('input', { className: ‘toggle', type: ‘checkbox', checked: this.props.completed }, null), React.createElement('label', null, this.props.title), React.createElement('button', {className: 'destroy'}, null) ), React.createElement('input', { className: ‘edit', defaultValue: this.props.title }, null) ); } });
  7. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ <script type="text/template" id="item-template"> <div class="view"> <input class="toggle" type="checkbox" <%=

    completed ? 'checked' : '' %>> <label><%- title %></label> <button class="destroy"></button> </div> <input class="edit" value="<%- title %>"> </script> var Item = React.createClass({ render: function() { return React.createElement('div', null, React.createElement('div', {className: 'view'}, React.createElement('input', { className: ‘toggle', type: ‘checkbox', checked: this.props.completed }, null), React.createElement('label', null, this.props.title), React.createElement('button', {className: 'destroy'}, null) ), React.createElement('input', { className: ‘edit', defaultValue: this.props.title }, null) ); } });
  8. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ReactԽ Part.2 : JSXͰBackbone TemplateΛஔ͖׵͑Δ ▸ ύοέʔδϚωʔδϟɺόϯυϥɺίϯύΠϥΛಋೖ͢Δ͜ͱʹͳΓ·͕͢ɺ ReactࣗମͰ֮ ͑Δ͜ͱ͸গͳ͍࢖͍ํͰ͢ɻ

    πʔϧʹ׳Ε͍ͯͳ͚Ε͹ɺ࿅शʹͪΐ͏Ͳ͍͍͘Β͍ͷม ߋྔͰ͠ΐ͏ɻ ▸ ࢀߟURL ▸ https://nodejs.org/en/download/package-manager/ ▸ https://yarnpkg.com/en/docs/install#alternatives-tab ▸ https://facebook.github.io/react/docs/installation.html#installing-react ▸ https://webpack.js.org/guides/installation/ ▸ https://babeljs.io/docs/setup/#installation ▸ https://facebook.github.io/react/docs/introducing-jsx.html
  9. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ඞཁͳ΋ͷΛΠϯετʔϧ͢Δ ▸ ύοέʔδϚωʔδϟYarnΛΠϯετʔϧ ▸ ύοέʔδϚωʔδϟͱ͍͑͹npmͰ͕͢ɺReactؔ࿈ͷυΩϡϝϯτͰ͸yarnϕʔ εͰॻ͔Ε͍ͯΔ͜ͱ͕ଟ͍ͷͰࠓճ͸ͪ͜ΒΛ࢖͍·͢ɻ ▸ ίϚϯυ͸େମޓ׵͍ͯ͠ΔͷͰɺnpm͕͍͍ํ͸దٓஔ͖׵͑ͯಡΜͰ͍ͩ͘͞ɻ ▸

    npmίϚϯυରরදɿ https://yarnpkg.com/en/docs/migrating-from-npm ▸ YarnΛ࢖ͬͯɺReactͱɺόϯυϥʹwebpackɺίϯύΠϥʹbabelΛΠϯετʔϧ yarn add react react-dom yarn add webpack babel-core babel-loader babel-preset-react --dev
  10. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ςϯϓϨʔτΛReactԽ͢Δ ▸ React ComponentΛ࡞ͬͯΈ·͢ ▸ ҰݟHTMLʹݟ͑Δ͜ͷจ๏͕JSXͰ͢ɻ className, defaultValue౳গ͠HTMLͱ ҧ͏ଐੑ͕͋Γ·͢Ͷɻ

    var Item = React.createClass({ render: function() { return ( <div> <div className="view"> <input className="toggle" type="checkbox" checked={this.props.checked} /> <label>{this.props.title}</label> <button className="destroy"></button> </div> <input className="edit" defaultValue={this.props.title} /> </div> ); } });
  11. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ <script type="text/template" id="item-template"> <div class="view"> <input class="toggle" type="checkbox" <%=

    completed ? 'checked' : '' %>> <label><%- title %></label> <button class="destroy"></button> </div> <input class="edit" value="<%- title %>"> </script> var Item = React.createClass({ render: function() { return ( <div> <div className="view"> <input className="toggle" type="checkbox" checked={this.props.checked} /> <label>{this.props.title}</label> <button className="destroy"></button> </div> <input className="edit" defaultValue={this.props.title} /> </div> ); } });
  12. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ template: _.template($('#item-template').html()), render: function () { this.$el.html(this.template(this.model.toJSON())); }, render:

    function () { ReactDOM.render( <Item title={this.model.get('title')} checked={this.model.get('completed')} />, this.$el.get(0) ); },
  13. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ςϯϓϨʔτΛReactԽ͢Δ ▸ webpack.config.jsΛ࡞ΓɺϏϧυઃఆΛॻ͖·͢ module.exports = { entry: { 'components/Item.js':

    './js/components/Item.js', 'views/todo-view.js': './js/views/todo-view.js' }, output: { path: `${__dirname}/dist`, filename: '[name]' }, module: { loaders: [ { test: /\.js$/, loader: 'babel', query: { presets: ['react'] }
  14. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ςϯϓϨʔτΛReactԽ͢Δ ▸ Ϗϧυ͠·͠ΐ͏ ▸ ͜ΕͰdistʹϏϧυͨ͠jsϑΝΠϧ͕ੜ੒͞ΕΔͷͰɺ͜ΕΛhtmlʹಡΈ ࠐ·ͤ·͢ yarn run build

    <script src="node_modules/react/dist/react.js"></script> <script src="node_modules/react-dom/dist/react-dom.js"></ script> <script src="dist/components/Item.js"></script> <script src="dist/views/todo-view.js"></script>
  15. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Underscore Templateͷ৔߹ ▸ js var ChildView = Backbone.View.extend({ template:

    _.template($('#child').html()), render: function () { this.$el.html(this.template(this.model.toJSON())); } }); var ParentView = Backbone.View.extend({ template: _.template($('#parent').html()), render: function () { this.$el.html(this.template()); var parent = this.$el.find('.parent'); this.collection.each(function (model) { var child = new ChildView({model: model}); parent.append(child.$el); }); } });
  16. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Reactͷ৔߹ ▸ ίϯϙʔωϯτ͔ΒίϯϙʔωϯτΛݺͼग़ͤ·͢ var Parent = React.createClass({ render: function()

    { var children = []; this.props.collection.each(function (model) { children.push(<Child model={model}>); }); return <div className="parent">{children}</div>; } }); var Child = React.createClass({ render: function() { return <div className="child">{this.props.text}</div>; } });
  17. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Part.2 ·ͱΊ ▸ ͜ͷΑ͏ʹγϯϓϧʹهड़Ͱ͖ɺؾܰʹڞ௨UI෦඼Λ෼཭ͯ͠͠·͑·͢ɻ
 Child͕ඳըͷΈͰ͍͍৔߹͸ɺ͜ͷख๏ͰऔΓೖΕΔͷ͸͋Γͩͱࢥ͍·͢ɻ ▸ ͔͠͠ͳ͕Βɺ
 Childʹੜ੒࣌΍Ϣʔβೖྗ࣌ͷΠϕϯτॲཧΛ࣋ͨͤͯɺͦΕ΋ڞ௨Խ͍ͨ͠ ͱ͍͏৔߹ɺ


    React ComponentͰͦ͏͍ͬͨࣄ͸ॻ͚·͕͢ɺ΋ͷʹΑͬͯBackbone View ʹॻ͍ͯ͋ͬͨΓReact Componentʹॻ͍ͯ͋ͬͨΓ͢ΔͱɺՄಡੑ΍อकੑ ΛԼ͛ͯ͠·͏ͷͰɺ
 ݁ہChildͷBackbone ViewΛ࡞ͬͯParentView͔Βݺͼग़͢͜ͱʹͳΓ·͢ɻ
  18. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ReactԽ Part.3 : Backbone ViewΛReact Componentʹஔ͖׵͑Δ ▸ React ComponentͷػೳͰBackbone

    ViewͰ΍͍ͬͯΔ͜ͱΛҠ২͠·͢ɻ ▸ มߋྔ΋݁ߏ͋ΓɺES2015+Λಋೖ͠JSͷॻ͖ํ͕݁ߏมΘΓ·͕͢ɺ
 Backboneʹ͸ͳ͍ViewͷՄಡੑɾอकੑ޲্ͷͨΊͷ༷ʑͳϝϦοτΛڗ डͰ͖·͢ɻ ▸ ࢀߟURL ▸ https://facebook.github.io/react/docs/components-and-props.html ▸ https://facebook.github.io/react/docs/handling-events.html
  19. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Α͋͘ΔBackbone View var View = Backbone.View.extend({ tagName: 'li', //

    ユーザ入力イベントとハンドラの紐付け events: { 'click .toggle': 'toggleCompleted' }, // モデル変更イベントとハンドラの紐付け initialize: function () { this.listenTo(this.model, 'change', this.render); }, render: function () { this.$el.html(this.template(this.model.toJSON())); }, toggleCompleted: function () { this.model.toggle(); } });
  20. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ React ComponentԽ // 依存ライブラリの参照はHTMLではなくjsに書く import Item, {Component} from 'react';

    // classが使える class Todo extends Component { // 描画後イベント componentDidMount() { // モデル変更イベントとハンドラの紐付け this.props.model.on('change', this.handleChangeModel); } handleChangeModel() { // 描画実行 this.forceUpdate(); } handleClickToggle() { this.props.model.toggle(); } 〜つづく〜
  21. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ React ComponentԽ 〜つづき〜 render() { const title = this.props.model.get('title');

    const completed = this.props.model.get('completed'); // ユーザ入力イベントとハンドラの紐付け return ( <li> <div className="view"> <input className="toggle" type="checkbox" checked={completed} onClick={this.handleClickToggle} /> <label>{title}</label> </div> </li> ); } }
  22. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Componentͷ໾ׂ ▸ ReactΛ࢖༻͢ΔFWͷFlux Utils΍ReduxͰ͸ɺ
 ComponentΛ2ͭ໾ׂͰ෼ׂ͢Δ͜ͱΛਪ঑͍ͯ͠·͢ɻ ▸ 1ͭ͸ɺ౉͞ΕΔpropsʹԠͯ͡UIΛඳը͢ΔεςʔτϨείϯϙʔωϯτ 
 ΋͏1ͭ͸ɺσʔλͱViewΛૢ࡞͢Δίϯϙʔωϯτ

    
 લऀ͸Views·ͨ͸Presentational Componentsɺ 
 ޙऀ͸Containers·ͨ͸Container Componentsͱݺ͹Ε·͢ ▸ ࢀߟURL ▸ https://facebook.github.io/flux/docs/flux-utils.html#best-practices ▸ http://redux.js.org/docs/basics/UsageWithReact.html#presentational-and- container-components
  23. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Part3ͷComponentΛViewͱContainerʹ෼ׂ͢Δ ▸ View ▸ propsΛඳը͢Δ͚ͩ ͳͷͰFunctional Componentʹ͠·͢ ▸ https://

    facebook.github.io/ react/docs/ components-and- props.html#functional- and-class-components const Item = (props) => { // 状態は外から渡される const {title, checkedToggle} = props; const handleClickToggle = () => { // 外から渡されたハンドラにそのまま返す props.onClickToggle(); } return ( <li> <div className="view"> <input className="toggle" type="checkbox" checked={checkedToggle} onClick={handleClickToggle} /> <label>{title}</label> </div> </li> ); }
  24. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ Part3ͷComponentΛViewͱContainerʹ෼ׂ͢Δ ▸ Container class ItemContainer extends Component { componentDidMount()

    { this.props.model.on('change', this.handleChangeModel); } handleChangeModel() { this.forceUpdate(); } handleClickToggle() { this.state.model.toggle(); } render() { const title = this.props.model.get('title'); const completed = this.props.model.get('completed'); // Itemの状態はこちらで管理する return ( <Item title={title} checkedToggle={completed} onClickToggle={handleClickToggle} /> ); } }
  25. طଘϓϩμΫτͷViewΛReactʹஔ͖׵͑Δ ReactԽ Part.6 : Backbone΍ΊΔ ▸ ৽ن։ൃϓϩδΣΫτ͕࢝·ΓɺτϨϯυͷReduxͱɺ Facebook͕ϝϯςφϯε͢ΔFlux UtilsͰൺֱ͢Δ͜ͱʹͳΓ ·ͨ͠ɻ

    ▸ ͜ͷ2ͭͷେ͖ͳҧ͍͸ɺReduxʹ͸1ͭͷΦϒδΣΫτʹΞϓ ϦέʔγϣϯશମͷεςʔτΛ·ͱΊΔݪଇ͕͋Δɺͱ͍͏ͱ ͜ΖͰ͢ɻ ▸ ElmͷӨڹΛड͚ͨRedux͸ɺؔ਺ܕϓϩάϥϛϯάͷ׆༻ʹ Αͬͯอकੑͷߴ͍ίʔυΛॻ͚ΔͷͰ͍͍ͱࢥ͍·ͨ͠ɻ