<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>69log</title>
  
  <subtitle>福岡でwebの仕事をしています</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://blog.kazu69.net/"/>
  <updated>2019-08-03T01:05:08.000Z</updated>
  <id>http://blog.kazu69.net/</id>
  
  <author>
    <name>kazu69</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Golangでのgrpcエラーハンドリング</title>
    <link href="http://blog.kazu69.net/2019/07/02/grpc-error-hadling/"/>
    <id>http://blog.kazu69.net/2019/07/02/grpc-error-hadling/</id>
    <published>2019-07-01T17:21:10.000Z</published>
    <updated>2019-08-03T01:05:08.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="gRPCのエラーの扱い方"><a href="#gRPCのエラーの扱い方" class="headerlink" title="gRPCのエラーの扱い方"></a>gRPCのエラーの扱い方</h2><p>gRPCでのエラーに関してgolangではどう向き合うべきなのか知るべくひとまず知識をまとめた。</p><a id="more"></a><h2 id="golangでのerrorハンドリング"><a href="#golangでのerrorハンドリング" class="headerlink" title="golangでのerrorハンドリング"></a>golangでのerrorハンドリング</h2><p>golangではerrorをどのように取扱うのがよりよいのかというのがしばしば話題になることがある。<br>golang自体がシンプルなゆえに、考慮することが多い。</p><ul><li>エラーの種類</li><li>stack trace</li><li>メッセージ</li><li>ステータス</li><li>errorの伝搬</li></ul><p>標準パッケージだけでは難しいことも多いため、<a href="https://godoc.org/github.com/pkg/errors" target="_blank" rel="noopener">errors</a>なども利用して行うことが多い。(<a href="https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html" target="_blank" rel="noopener">Error handling in Upspin</a>)</p><h2 id="grpcのgolangでのerrorハンドリング"><a href="#grpcのgolangでのerrorハンドリング" class="headerlink" title="grpcのgolangでのerrorハンドリング"></a>grpcのgolangでのerrorハンドリング</h2><p>自分はgolangを使ったmicro serviceの開発をしていることもあり、grpcでのエラーに関してどう向き合うべきなのか知る<br>べく知識をまとめた。</p><h3 id="gRPCのエラーのまとめ"><a href="#gRPCのエラーのまとめ" class="headerlink" title="gRPCのエラーのまとめ"></a>gRPCのエラーのまとめ</h3><p>サーバー、クライアントでのerror handlingの例としては<a href="https://github.com/avinassh/grpc-errors/tree/master/go" target="_blank" rel="noopener">avinassh/grpc-errors</a>が参考になる。<br>protoを利用してる前提なので<a href="https://cloud.google.com/apis/design/errors#error_model" target="_blank" rel="noopener">error model</a>も参考になる。</p><p>gRPCのエラーはGoogle APIのエラーモデルの設計に基づきエラーコード、ステータス、詳細情報が返される。<br><a href="https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto" target="_blank" rel="noopener">google.rpc.Status</a></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">package google.rpc;</span><br><span class="line">message Status &#123;</span><br><span class="line">    int32 code = 1;</span><br><span class="line">    string message = 2;</span><br><span class="line">    repeated google.protobuf.Any details = 3;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>status codeは<a href="https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto" target="_blank" rel="noopener">google/rpc/code.proto</a>にあるとおりで、このステータスはHTTP Status Codeとの整合性を保つことができるようになっている。</p><p><a href="https://cloud.google.com/apis/design/errors#handling_errors" target="_blank" rel="noopener">https://cloud.google.com/apis/design/errors#handling_errors</a></p><p>こうすることでクライアントとはhttpで、micro serviceとはgrpcでメッセージングしているサービスの場合においても<br>整合性を保ったままエラーの設計ができる。</p><h3 id="gRPC-client"><a href="#gRPC-client" class="headerlink" title="gRPC client"></a>gRPC client</h3><p><a href="https://godoc.org/google.golang.org/grpc/status" target="_blank" rel="noopener">status</a>、<a href="https://godoc.org/google.golang.org/grpc/codes" target="_blank" rel="noopener">codes</a>パッケージを使うことで</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">"google.golang.org/grpc/codes"</span></span><br><span class="line"><span class="string">"google.golang.org/grpc/status"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">st, ok := status.FromError(err)</span><br><span class="line"><span class="keyword">if</span> !ok &#123;</span><br><span class="line">log.Printf(<span class="string">"Unknown Error: %+v\n"</span>, err)</span><br><span class="line"><span class="comment">// return unknown error</span></span><br><span class="line">&#125;</span><br><span class="line">responseWriter := &amp;httptest.ResponseRecorder&#123;&#125;</span><br><span class="line"><span class="keyword">switch</span> st.Code() &#123;</span><br><span class="line"><span class="keyword">case</span> codes.NotFound:</span><br><span class="line">log.Printf(<span class="string">"Error NotFound %+v\n"</span>, err)</span><br><span class="line">responseWriter.WriteHeader(http.StatusNotFound)</span><br><span class="line"><span class="keyword">case</span> codes.PermissionDenied:</span><br><span class="line">log.Printf(<span class="string">"Error PermissionDenied %+v\n"</span>, err)</span><br><span class="line">responseWriter.WriteHeader(http.StatusForbidden)</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line">log.Printf(<span class="string">"Error Internal %+v\n"</span>, err)</span><br><span class="line">responseWriter.WriteHeader(http.StatusInternalServerError)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="gRPC-server"><a href="#gRPC-server" class="headerlink" title="gRPC server"></a>gRPC server</h3><p>またgrpcサーバーの実装としては<a href="https://godoc.org/google.golang.org/genproto/googleapis/rpc/errdetails" target="_blank" rel="noopener">googleapis/rpc/errdetails</a>を使うことでエラー詳細を含めたエラーを生成することが容易にできる。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> (</span><br><span class="line">    <span class="string">"google.golang.org/grpc/status"</span></span><br><span class="line">    <span class="string">"google.golang.org/genproto/googleapis/rpc/errdetails"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">st := status.New(codes.InvalidArgument, <span class="string">"some error message"</span>)</span><br><span class="line"><span class="comment">// add error message detail</span></span><br><span class="line">st, err := st.WithDetails(</span><br><span class="line">&amp;errdetails.BadRequest&#123;</span><br><span class="line">FieldViolations: []*errdetails.BadRequest_FieldViolation&#123;</span><br><span class="line">&amp;errdetails.BadRequest_FieldViolation&#123;</span><br><span class="line">Field:       <span class="string">"Message"</span>,</span><br><span class="line">Description: <span class="string">"error detail"</span>,</span><br><span class="line">&#125;,</span><br><span class="line">&#125;,</span><br><span class="line">&#125;,</span><br><span class="line">&amp;errdetails.LocalizedMessage&#123;</span><br><span class="line">Locale:  <span class="string">"ja"</span>,</span><br><span class="line">Message: <span class="string">"エラー詳細"</span>,</span><br><span class="line">&#125;,</span><br><span class="line">)</span><br><span class="line"><span class="comment">// unexpected error</span></span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="built_in">panic</span>(fmt.Sprintf(<span class="string">"Unexpected error: %+v"</span>, err))</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// return error</span></span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span>, st.Err()</span><br></pre></td></tr></table></figure><h3 id="Parse-error-detail"><a href="#Parse-error-detail" class="headerlink" title="Parse error detail"></a>Parse error detail</h3><p>またこの詳細メッセージはclient側では<code>status.Details</code>を使うことで取り出すことができる。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> _, detail := <span class="keyword">range</span> st.Details() &#123;</span><br><span class="line"><span class="keyword">switch</span> t := detail.(<span class="keyword">type</span>) &#123;</span><br><span class="line"><span class="keyword">case</span> *errdetails.BadRequest:</span><br><span class="line"><span class="keyword">for</span> _, violation := <span class="keyword">range</span> t.GetFieldViolations() &#123;</span><br><span class="line">log.Printf(<span class="string">"The %q field was wrong:\n"</span>, violation.GetField())</span><br><span class="line">log.Printf(<span class="string">"\t%s\n"</span>, violation.GetDescription())</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> *errdetails.LocalizedMessage:</span><br><span class="line">log.Printf(<span class="string">"%s:%s"</span>, t.GetLocale(), <span class="keyword">string</span>(t.GetMessage()))</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="参考にしたページ"><a href="#参考にしたページ" class="headerlink" title="参考にしたページ"></a>参考にしたページ</h3><p><a href="https://cloud.google.com/apis/design/errors" target="_blank" rel="noopener">Google API Error</a><br><a href="https://www.grpc.io/docs/guides/error/" target="_blank" rel="noopener">Error Handling</a></p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;gRPCのエラーの扱い方&quot;&gt;&lt;a href=&quot;#gRPCのエラーの扱い方&quot; class=&quot;headerlink&quot; title=&quot;gRPCのエラーの扱い方&quot;&gt;&lt;/a&gt;gRPCのエラーの扱い方&lt;/h2&gt;&lt;p&gt;gRPCでのエラーに関してgolangではどう向き合うべきなのか知るべくひとまず知識をまとめた。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>Golangのhttp clientのmiddlewearの実装パターン</title>
    <link href="http://blog.kazu69.net/2019/05/05/learning_golang_middlewear_pattern_for_client/"/>
    <id>http://blog.kazu69.net/2019/05/05/learning_golang_middlewear_pattern_for_client/</id>
    <published>2019-05-05T04:21:53.000Z</published>
    <updated>2019-07-07T09:19:34.000Z</updated>
    
    <content type="html"><![CDATA[<p>以前にためした<a href>Golangのmiddlewear実装パターンを学ぶ</a>の続きで、<br>HttpClientでも同じパターンを適用したときのメモ。</p><a id="more"></a><p>とてもシンプルなclientの利用事例は以下のようになる。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">"net/http"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">client := &amp;http.Client&#123;&#125;</span><br><span class="line">resp, err := client.Get(<span class="string">"https://example.com"</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ますは前回と同じくcallstackを見てみる。</p><h2 id="http-client-callstack"><a href="#http-client-callstack" class="headerlink" title="http client callstack"></a>http client callstack</h2><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// net/http/client.go</span></span><br><span class="line"><span class="keyword">type</span> Client <span class="keyword">struct</span> &#123;</span><br><span class="line">    ...</span><br><span class="line">    Transport RoundTripper</span><br><span class="line">    ...</span><br></pre></td></tr></table></figure><p>Client構造体のコードコメントを見ると以下のようにあり</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Transport specifies the mechanism by which individual</span><br><span class="line">HTTP requests are made.</span><br><span class="line">If nil, DefaultTransport is used.</span><br></pre></td></tr></table></figure><p>どのメカニズムでtransportするのかは<code>Transport</code>の実装できまる。<br>nilの場合はデフォルトでHTTPが使われる。つまりhttp以外で会話するclientを作成する際も<br>このtransportをサポートしていれば良いってこと。<br>実際はhttp clientのtransportが<code>RoundTripper</code>なので、別のプロトコルの場合はこのRoundTripperを実装することになるのかな。</p><p>話は少しそれるがTransport構造体もざっとみてみると</p><ul><li>Transportは将来の再利用のために接続をキャッシュする<ul><li>CloseIdleConnectionsで管理する</li><li>MaxConnsPerHostで設定可能</li></ul></li><li>Transportはgorutineで再利用可能(スレッドセーフ)</li><li>HTTP URLがHTTP1.1または2を使う<ul><li>HTTP2をサポートしてる</li></ul></li><li>ネットワークエラーのときのみ再接続</li><li>Proxyで特定のURLに対してhttp(s), socketでのproxyができる</li><li>そのほかTCP dialやkeep-aliveもここで扱かわれる</li></ul><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> RoundTripper <span class="keyword">interface</span> &#123;</span><br><span class="line">    RoundTrip(*Request) (*Response, error)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>RoundTripper</code>のiterfaceからRoundTrip関数を持ち、実際にこのRoundTripでサーバーとの会話を行う。</p><p>続いて実際にRequestを行っている処理を見てみる。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Client)</span> <span class="title">Get</span><span class="params">(url <span class="keyword">string</span>)</span> <span class="params">(resp *Response, err error)</span></span> &#123;</span><br><span class="line">req, err := NewRequest(<span class="string">"GET"</span>, url, <span class="literal">nil</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span>, err</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> c.Do(req)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Client)</span> <span class="title">Do</span><span class="params">(req *Request)</span> <span class="params">(*Response, error)</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> c.do(req)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Client)</span> <span class="title">do</span><span class="params">(req *Request)</span> <span class="params">(retres *Response, reterr error)</span></span> &#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">reqs = <span class="built_in">append</span>(reqs, req)</span><br><span class="line"><span class="keyword">var</span> err error</span><br><span class="line"><span class="keyword">var</span> didTimeout <span class="function"><span class="keyword">func</span><span class="params">()</span> <span class="title">bool</span></span></span><br><span class="line"><span class="function"><span class="title">if</span> <span class="title">resp</span>, <span class="title">didTimeout</span>, <span class="title">err</span> = <span class="title">c</span>.<span class="title">send</span><span class="params">(req, deadline)</span>; <span class="title">err</span> != <span class="title">nil</span></span> &#123;</span><br><span class="line"><span class="comment">// c.send() always closes req.Body</span></span><br><span class="line">reqBodyClosed = <span class="literal">true</span></span><br><span class="line"><span class="keyword">if</span> !deadline.IsZero() &amp;&amp; didTimeout() &#123;</span><br><span class="line">err = &amp;httpError&#123;</span><br><span class="line"><span class="comment">// <span class="doctag">TODO:</span> early in cycle: s/Client.Timeout exceeded/timeout or context cancelation/</span></span><br><span class="line">err:     err.Error() + <span class="string">" (Client.Timeout exceeded while awaiting headers)"</span>,</span><br><span class="line">timeout: <span class="literal">true</span>,</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span>, uerr(err)</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        ...</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *Client)</span> <span class="title">send</span><span class="params">(req *Request, deadline time.Time)</span> <span class="params">(resp *Response, didTimeout <span class="keyword">func</span>()</span> <span class="title">bool</span>, <span class="title">err</span> <span class="title">error</span>)</span> &#123;</span><br><span class="line"><span class="keyword">if</span> c.Jar != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">for</span> _, cookie := <span class="keyword">range</span> c.Jar.Cookies(req.URL) &#123;</span><br><span class="line">req.AddCookie(cookie)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">resp, didTimeout, err = send(req, c.transport(), deadline)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span>, didTimeout, err</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> c.Jar != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">if</span> rc := resp.Cookies(); <span class="built_in">len</span>(rc) &gt; <span class="number">0</span> &#123;</span><br><span class="line">c.Jar.SetCookies(req.URL, rc)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> resp, <span class="literal">nil</span>, <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// send issues an HTTP request.</span></span><br><span class="line"><span class="comment">// Caller should close resp.Body when done reading from it.</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">send</span><span class="params">(ireq *Request, rt RoundTripper, deadline time.Time)</span> <span class="params">(resp *Response, didTimeout <span class="keyword">func</span>()</span> <span class="title">bool</span>, <span class="title">err</span> <span class="title">error</span>)</span> &#123;</span><br><span class="line">req := ireq <span class="comment">// req is either the original request, or a modified fork</span></span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">resp, err = rt.RoundTrip(req)</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> resp, <span class="literal">nil</span>, <span class="literal">nil</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ということで、最終的に<code>RoundTripper</code>の<code>RoundTrip</code>をcallすることになる。</p><p>これからclientのmiddlewearはRoundTripを実装するRoundTripperであれば良い。</p><p>ちなみにTransportのRoundtripの実装を見ると</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// roundTrip implements a RoundTripper over HTTP.</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Transport)</span> <span class="title">roundTrip</span><span class="params">(req *Request)</span> <span class="params">(*Response, error)</span></span> &#123;</span><br><span class="line">...</span><br><span class="line"><span class="comment">// Get the cached or newly-created connection to either the</span></span><br><span class="line"><span class="comment">// host (for http or https), the http proxy, or the http proxy</span></span><br><span class="line"><span class="comment">// pre-CONNECTed to https server. In any case, we'll be ready</span></span><br><span class="line"><span class="comment">// to send it requests.</span></span><br><span class="line">pconn, err := t.getConn(treq, cm)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">t.setReqCanceler(req, <span class="literal">nil</span>)</span><br><span class="line">req.closeBody()</span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span>, err</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> resp *Response</span><br><span class="line"><span class="keyword">if</span> pconn.alt != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="comment">// HTTP/2 path.</span></span><br><span class="line">t.decHostConnCount(cm.key()) <span class="comment">// don't count cached http2 conns toward conns per host</span></span><br><span class="line">t.setReqCanceler(req, <span class="literal">nil</span>)   <span class="comment">// not cancelable with CancelRequest</span></span><br><span class="line">resp, err = pconn.alt.RoundTrip(req)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">resp, err = pconn.roundTrip(treq)</span><br><span class="line">&#125;</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>optionのroundTripperである<code>pconn.alt</code>がある場合はhttp2などが利用される。<br>また<code>getConn</code>をみると既にidleなconnectionがある場合はそちらを利用する設計なのがわかる。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// getConn dials and creates a new persistConn to the target as</span></span><br><span class="line"><span class="comment">// specified in the connectMethod. This includes doing a proxy CONNECT</span></span><br><span class="line"><span class="comment">// and/or setting up TLS.  If this doesn't return an error, the persistConn</span></span><br><span class="line"><span class="comment">// is ready to write requests to.</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *Transport)</span> <span class="title">getConn</span><span class="params">(treq *transportRequest, cm connectMethod)</span> <span class="params">(*persistConn, error)</span></span> &#123;</span><br><span class="line">...</span><br><span class="line"><span class="keyword">if</span> pc, idleSince := t.getIdleConn(cm); pc != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">if</span> trace != <span class="literal">nil</span> &amp;&amp; trace.GotConn != <span class="literal">nil</span> &#123;</span><br><span class="line">trace.GotConn(pc.gotIdleConnTrace(idleSince))</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// set request canceler to some non-nil function so we</span></span><br><span class="line"><span class="comment">// can detect whether it was cleared between now and when</span></span><br><span class="line"><span class="comment">// we enter roundTrip</span></span><br><span class="line">t.setReqCanceler(req, <span class="function"><span class="keyword">func</span><span class="params">(error)</span></span> &#123;&#125;)</span><br><span class="line"><span class="keyword">return</span> pc, <span class="literal">nil</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">pc, err := t.dialConn(ctx, cm)</span><br><span class="line">dialc &lt;- dialRes&#123;pc, err&#125;</span><br><span class="line">&#125;()</span><br><span class="line"></span><br><span class="line">idleConnCh := t.getIdleConnCh(cm)</span><br><span class="line">...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="middlewearの実装"><a href="#middlewearの実装" class="headerlink" title="middlewearの実装"></a>middlewearの実装</h2><p>ここまでの結果から実際の具体的な実装をしていく。<br>middlewearは<code>http.RoundTripper</code>をオプションにとり<code>http.RoundTripper</code>を返す関数であればよい。<br>またhttp.Serrverのmiddlewearを実装下のrと同じく、clientのmiddlewear関数もhttp.Requestを受け、http.Responseを返す関数をオプションとする関数を返す必要がある。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">adapter</span><span class="params">(next http.RoundTripper)</span> <span class="title">http</span>.<span class="title">RoundTripper</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> RoundTripperFunc(<span class="function"><span class="keyword">func</span><span class="params">(r *http.Request)</span> <span class="params">(*http.Response, error)</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> next.RoundTrip(r)</span><br><span class="line">&#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>またこれらの関数をClient構造体のTransportメンバーに渡せるようにする関数を作成する必要がある。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">func useAdapters(adapters ...adapter) adapter &#123;</span><br><span class="line">return func(final http.RoundTripper) http.RoundTripper &#123;</span><br><span class="line">fn := final</span><br><span class="line">for i := len(adapters) - 1; i &gt;= 0; i-- &#123;</span><br><span class="line">fn = adapters[i](fn)</span><br><span class="line">&#125;</span><br><span class="line">return fn</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>のようにすることでhttp.clientのmiddlewearでmiddlewearを利用できるようになる。<br>この実装を見ても、serverのmiddlewearと同じ実装出ることがわかる。</p><p>実際のコードは以下</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">"fmt"</span></span><br><span class="line"><span class="string">"net/http"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> adapter <span class="function"><span class="keyword">func</span><span class="params">(http.RoundTripper)</span> <span class="title">http</span>.<span class="title">RoundTripper</span></span></span><br><span class="line"><span class="function"><span class="title">type</span> <span class="title">RoundTripperFunc</span> <span class="title">func</span><span class="params">(*http.Request)</span> <span class="params">(*http.Response, error)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="params">(f RoundTripperFunc)</span> <span class="title">RoundTrip</span><span class="params">(r *http.Request)</span> <span class="params">(res *http.Response, err error)</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> f(r)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">adapterA</span><span class="params">(next http.RoundTripper)</span> <span class="title">http</span>.<span class="title">RoundTripper</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> RoundTripperFunc(<span class="function"><span class="keyword">func</span><span class="params">(r *http.Request)</span> <span class="params">(*http.Response, error)</span></span> &#123;</span><br><span class="line">fmt.Println(<span class="string">"Executing adapterA before adapter"</span>)</span><br><span class="line">res, err := next.RoundTrip(r)</span><br><span class="line">fmt.Println(<span class="string">"Executing adapterA after adapter"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> res, err</span><br><span class="line">&#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">adapterB</span><span class="params">(next http.RoundTripper)</span> <span class="title">http</span>.<span class="title">RoundTripper</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> RoundTripperFunc(<span class="function"><span class="keyword">func</span><span class="params">(r *http.Request)</span> <span class="params">(*http.Response, error)</span></span> &#123;</span><br><span class="line">fmt.Println(<span class="string">"Executing adapterB before adapter"</span>)</span><br><span class="line">res, err := next.RoundTrip(r)</span><br><span class="line">fmt.Println(<span class="string">"Executing adapterB after adapter"</span>)</span><br><span class="line"><span class="keyword">return</span> res, err</span><br><span class="line">&#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">useAdapters</span><span class="params">(adapters ...adapter)</span> <span class="title">adapter</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(final http.RoundTripper)</span> <span class="title">http</span>.<span class="title">RoundTripper</span></span> &#123;</span><br><span class="line">fn := final</span><br><span class="line"><span class="keyword">for</span> i := <span class="built_in">len</span>(adapters) - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i-- &#123;</span><br><span class="line">fn = adapters[i](fn)</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> fn</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">adapters := useAdapters(adapterA, adapterB)</span><br><span class="line">client := &amp;http.Client&#123;</span><br><span class="line">Transport: adapters(http.DefaultTransport),</span><br><span class="line">&#125;</span><br><span class="line">resp, err := client.Get(<span class="string">"https://example.com"</span>)</span><br><span class="line">fmt.Println(<span class="string">"RES: %s, Log: %s"</span>, resp, err)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h3><p>golangのhttp server, clientともに同じ設計でmiddleweaerの実装があ可能な設計が取られており、<br>またclientはtransporterの実装によりプロトコルを変更・拡張することができる。<br>柔軟な設計はgolangでアプリケーションを作成するにあたっても良い手本となりそう。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;以前にためした&lt;a href&gt;Golangのmiddlewear実装パターンを学ぶ&lt;/a&gt;の続きで、&lt;br&gt;HttpClientでも同じパターンを適用したときのメモ。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>Golangのmiddlewear実装パターンを学ぶ</title>
    <link href="http://blog.kazu69.net/2019/05/03/learning_golang_middlewear_pattern/"/>
    <id>http://blog.kazu69.net/2019/05/03/learning_golang_middlewear_pattern/</id>
    <published>2019-05-02T18:12:25.000Z</published>
    <updated>2019-08-03T00:56:52.000Z</updated>
    
    <content type="html"><![CDATA[<p>Golangでwebアプリケーションなどを開発するに当たり、<br>リクエストやレスポンスをインターセプトし適宜利用するmiddlewearの実装パターンを<br>読んでみたときのメモ。</p><a id="more"></a><p>今回はHTTPサーバーおよびHTTPクライアントを読んでみた。<br>結果として、標準パッケージのみで比較的容易に実装できる。<br>これはgolangの柔軟な言語仕様なんだろう。<br>golangらしいコーディングの勉強にもなりました。</p><h2 id="http-server-callstack"><a href="#http-server-callstack" class="headerlink" title="http server callstack"></a>http server callstack</h2><p>およそ一般的なechoサーバーの実装は以下のような感じと思われる</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">  <span class="string">"fmt"</span></span><br><span class="line">  <span class="string">"net/http"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">handler</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">  fmt.Fprintf(w, <span class="string">"Hello, World"</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">  http.HandleFunc(<span class="string">"/"</span>, handler)</span><br><span class="line">  http.ListenAndServe(<span class="string">":3000"</span>, <span class="literal">nil</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>callstackを見ていくと</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">HandleFunc</span><span class="params">(pattern <span class="keyword">string</span>, handler <span class="keyword">func</span>(ResponseWriter, *Request)</span>)</span> &#123;</span><br><span class="line">DefaultServeMux.HandleFunc(pattern, handler)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>patternに適合したhandlerを登録する。DefaultServeMuxで初期設定で提供されている。<br>ちなみにServeMuxはhttpマルチプレクサになる。</p><p>また、コードコメントに以下のようにある</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Longer patterns take precedence over shorter ones, so that</span><br><span class="line">if there are handlers registered for both &quot;/images/&quot;</span><br><span class="line">and &quot;/images/thumbnails/&quot;, the latter handler will be</span><br><span class="line">called for paths beginning &quot;/images/thumbnails/&quot; and the</span><br><span class="line">former will receive requests for any other paths in the</span><br><span class="line">&quot;/images/&quot; subtree.</span><br></pre></td></tr></table></figure><p>パスのパターンマッチはより長いパターンのほうが短いパターンより優先されるとのこと。<br>さらにhandler optionは</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">handler <span class="function"><span class="keyword">func</span><span class="params">(ResponseWriter, *Request)</span></span></span><br></pre></td></tr></table></figure><p>ここからわかるようにhandlerはrequest writerとresponseの参照をオプションとする関数である。<br>これを満たせばよい。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(mux *ServeMux)</span> <span class="title">HandleFunc</span><span class="params">(pattern <span class="keyword">string</span>, handler <span class="keyword">func</span>(ResponseWriter, *Request)</span>)</span> &#123;</span><br><span class="line"><span class="keyword">if</span> handler == <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="built_in">panic</span>(<span class="string">"http: nil handler"</span>)</span><br><span class="line">&#125;</span><br><span class="line">mux.Handle(pattern, HandlerFunc(handler))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>HandleFuncはServeMuxのHandleをcallすることになる。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> ServeMux <span class="keyword">struct</span> &#123;</span><br><span class="line">mu    sync.RWMutex</span><br><span class="line">m     <span class="keyword">map</span>[<span class="keyword">string</span>]muxEntry</span><br><span class="line">es    []muxEntry <span class="comment">// slice of entries sorted from longest to shortest.</span></span><br><span class="line">hosts <span class="keyword">bool</span>       <span class="comment">// whether any patterns contain hostnames</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(mux *ServeMux)</span> <span class="title">Handle</span><span class="params">(pattern <span class="keyword">string</span>, handler Handler)</span></span> &#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">    e := muxEntry&#123;h: handler, pattern: pattern&#125;</span><br><span class="line">mux.m[pattern] = e</span><br><span class="line"><span class="keyword">if</span> pattern[<span class="built_in">len</span>(pattern)<span class="number">-1</span>] == <span class="string">'/'</span> &#123;</span><br><span class="line">mux.es = appendSorted(mux.es, e)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>pathにマッチしているhandlerを登録している。</p><p>次はこのhandlerを呼び出すところ。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// net/http/server.go</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">ListenAndServe</span><span class="params">(addr <span class="keyword">string</span>, handler Handler)</span> <span class="title">error</span></span> &#123;</span><br><span class="line">server := &amp;Server&#123;Addr: addr, Handler: handler&#125;</span><br><span class="line"><span class="keyword">return</span> server.ListenAndServe()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(srv *Server)</span> <span class="title">ListenAndServe</span><span class="params">()</span> <span class="title">error</span></span> &#123;</span><br><span class="line"><span class="keyword">if</span> srv.shuttingDown() &#123;</span><br><span class="line"><span class="keyword">return</span> ErrServerClosed</span><br><span class="line">&#125;</span><br><span class="line">addr := srv.Addr</span><br><span class="line"><span class="keyword">if</span> addr == <span class="string">""</span> &#123;</span><br><span class="line">addr = <span class="string">":http"</span></span><br><span class="line">&#125;</span><br><span class="line">ln, err := net.Listen(<span class="string">"tcp"</span>, addr)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">return</span> err</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> srv.Serve(tcpKeepAliveListener&#123;ln.(*net.TCPListener)&#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(srv *Server)</span> <span class="title">Serve</span><span class="params">(l net.Listener)</span> <span class="title">error</span></span> &#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line">        ...</span><br><span class="line"></span><br><span class="line">        c := srv.newConn(rw)</span><br><span class="line">c.setState(c.rwc, StateNew) <span class="comment">// before Serve can return</span></span><br><span class="line"><span class="keyword">go</span> c.serve(ctx)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *conn)</span> <span class="title">serve</span><span class="params">(ctx context.Context)</span></span> &#123;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line"><span class="comment">// HTTP cannot have multiple simultaneous active requests.[*]</span></span><br><span class="line"><span class="comment">// Until the server replies to this request, it can't read another,</span></span><br><span class="line"><span class="comment">// so we might as well run the handler in this goroutine.</span></span><br><span class="line"><span class="comment">// [*] Not strictly true: HTTP pipelining. We could let them all process</span></span><br><span class="line"><span class="comment">// in parallel even if their responses need to be serialized.</span></span><br><span class="line"><span class="comment">// But we're not going to implement HTTP pipelining because it</span></span><br><span class="line"><span class="comment">// was never deployed in the wild and the answer is HTTP/2.</span></span><br><span class="line">    serverHandler&#123;c.server&#125;.ServeHTTP(w, w.req)</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(sh serverHandler)</span> <span class="title">ServeHTTP</span><span class="params">(rw ResponseWriter, req *Request)</span></span> &#123;</span><br><span class="line">handler := sh.srv.Handler</span><br><span class="line"><span class="keyword">if</span> handler == <span class="literal">nil</span> &#123;</span><br><span class="line">handler = DefaultServeMux</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> req.RequestURI == <span class="string">"*"</span> &amp;&amp; req.Method == <span class="string">"OPTIONS"</span> &#123;</span><br><span class="line">handler = globalOptionsHandler&#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line">handler.ServeHTTP(rw, req)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最終的にhandlerは<code>ServeHTTP</code>をcallする形になる。</p><p>そしてこのhanderは以下のinterfaceを持っている。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Handler <span class="keyword">interface</span> &#123;</span><br><span class="line">ServeHTTP(ResponseWriter, *Request)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>これから作成するmiddlewearはこのインターフェイスを満たす関数である必要がある。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Except for reading the body, handlers should not modify the provided Request.</span><br></pre></td></tr></table></figure><p>middlewear自体の内部実装の注意点としては、<br>公式のコメントにある通りでRequestBodyをあくまでReadするだけで変更してはならない。</p><p>またServerには<code>Handle</code>と<code>HandleFunc</code>が実装されており、HandleFuncをつかうことでハンドラ関数を登録できることわかる。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Handle</span><span class="params">(pattern <span class="keyword">string</span>, handler Handler)</span></span> &#123; DefaultServeMux.Handle(pattern, handler) &#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">HandleFunc</span><span class="params">(pattern <span class="keyword">string</span>, handler <span class="keyword">func</span>(ResponseWriter, *Request)</span>)</span> &#123;</span><br><span class="line">DefaultServeMux.HandleFunc(pattern, handler)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>このHandleFuncの<code>ServerHTTP</code>は以下のような実装である</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> HandlerFunc <span class="function"><span class="keyword">func</span><span class="params">(ResponseWriter, *Request)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">// <span class="title">ServeHTTP</span> <span class="title">calls</span> <span class="title">f</span><span class="params">(w, r)</span>.</span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="params">(f HandlerFunc)</span> <span class="title">ServeHTTP</span><span class="params">(w ResponseWriter, r *Request)</span></span> &#123;</span><br><span class="line">f(w, r)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>HandlerFuncをcallする関数になっている。</p><h2 id="middlewearを実装"><a href="#middlewearを実装" class="headerlink" title="middlewearを実装"></a>middlewearを実装</h2><p>ここまで見てきた通りmiddlewearはhandlerで実装されるように設計していくことで、あとは既存の機構で適宜処理される。<br>またmiddlewerはアプリケーションの処理の前後にmiddlewearの処理挟むため、イメージ的に</p><p>[request] –&gt; [middlewear] –&gt; [another middlewear] –&gt;<br>… –&gt; [final] –&gt; [middlewear] –&gt; [another middlewear] –&gt; … –&gt; [response]</p><p>という動作が期待される</p><p>具体的には以下のような感じになってほしいものである。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http.Handle(<span class="string">"/"</span>, middleware1(middleware2(... (finalHandler) ...))</span><br></pre></td></tr></table></figure><p>引数として受けた次のmiddlewareを呼び出す前後に何らかの操作を実行し。<br>新しいmiddleware1を返す関数でmiddlewearを実装する必要がある。</p><p>各middleware関数は実装としては以下の形であればよい</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">middleware</span><span class="params">(next http.HandlerFunc)</span> <span class="title">http</span>.<span class="title">HandlerFunc</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> http.HandlerFunc(<span class="function"><span class="keyword">func</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">next.ServeHTTP(w, r)</span><br><span class="line">&#125;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>HandlerFuncをオプションにとり、HandlerFuncを返す関数である。</p><p>http.HandlerFuncはinterfaceから<code>ServeHTTP</code>を実装しているので最終的にそれをcallする。<br>あとはmiddlewearを複数登録できるようにラッパー関数を作成していく。</p><p>ということで実装してみる。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// add type for middlewear</span></span><br><span class="line"><span class="keyword">type</span> middleware <span class="function"><span class="keyword">func</span><span class="params">(http.HandlerFunc)</span> <span class="title">http</span>.<span class="title">HandlerFunc</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">// <span class="title">add</span> <span class="title">middlewear</span> <span class="title">function</span></span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="title">useMiddlewears</span><span class="params">(middlewares ...middleware)</span> <span class="title">middleware</span></span> &#123;</span><br><span class="line">    <span class="comment">// final handler register function</span></span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(final http.HandlerFunc)</span> <span class="title">http</span>.<span class="title">HandlerFunc</span></span> &#123;</span><br><span class="line">        <span class="comment">// Pass the previous function as an argument</span></span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(w http.ResponseWriter, req *http.Request)</span></span> &#123;</span><br><span class="line">fn := final</span><br><span class="line"><span class="keyword">for</span> i := <span class="built_in">len</span>(middlewares) - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i-- &#123;</span><br><span class="line">fn = middlewares[i](fn)</span><br><span class="line">            &#125;</span><br><span class="line">fn(w, req)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上記のように<code>mniddlewear</code>というタイプを作成し、middlewearを登録するラッパー関数を作成する。<br>今回は最終的にリクエストを処理する関数を別途登録するデザインとなっている。<br>またリスト内のfinal handlerから順番に登録した順番と逆順で直前の関数に渡すようにすることで、</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http.Handle(<span class="string">"/"</span>, middleware1(middleware2(... (finalHandler) ...))</span><br></pre></td></tr></table></figure><p>このパターンを実現できる。<br>最終的に以下のようになった。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"><span class="string">"log"</span></span><br><span class="line"><span class="string">"net/http"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> middleware <span class="function"><span class="keyword">func</span><span class="params">(http.HandlerFunc)</span> <span class="title">http</span>.<span class="title">HandlerFunc</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="title">middlewareA</span><span class="params">(next http.HandlerFunc)</span> <span class="title">http</span>.<span class="title">HandlerFunc</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> http.HandlerFunc(<span class="function"><span class="keyword">func</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">log.Println(<span class="string">"Executing middlewareA before handler"</span>)</span><br><span class="line">next.ServeHTTP(w, r)</span><br><span class="line">log.Println(<span class="string">"Executing middlewareA after handler"</span>)</span><br><span class="line">&#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">middlewareB</span><span class="params">(next http.HandlerFunc)</span> <span class="title">http</span>.<span class="title">HandlerFunc</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> http.HandlerFunc(<span class="function"><span class="keyword">func</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">log.Println(<span class="string">"Executing middlewareB before handler"</span>)</span><br><span class="line">next.ServeHTTP(w, r)</span><br><span class="line">log.Println(<span class="string">"Executing middlewareB after handler"</span>)</span><br><span class="line">&#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">finalHandler</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">log.Println(<span class="string">"Executing finalHandler"</span>)</span><br><span class="line">w.Write([]<span class="keyword">byte</span>(<span class="string">"Done"</span>))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">useMiddlewears</span><span class="params">(middlewares ...middleware)</span> <span class="title">middleware</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(final http.HandlerFunc)</span> <span class="title">http</span>.<span class="title">HandlerFunc</span></span> &#123;</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(w http.ResponseWriter, req *http.Request)</span></span> &#123;</span><br><span class="line">fn := final</span><br><span class="line"><span class="keyword">for</span> i := <span class="built_in">len</span>(middlewares) - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i-- &#123;</span><br><span class="line">fn = middlewares[i](fn)</span><br><span class="line">&#125;</span><br><span class="line">fn(w, req)</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">middlewears := useMiddlewears(middlewareA, middlewareB)</span><br><span class="line">http.HandleFunc(<span class="string">"/"</span>, middlewears(finalHandler))</span><br><span class="line">http.ListenAndServe(<span class="string">":3000"</span>, middlewears(finalHandler))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ go run main.go</span><br><span class="line">// $ curl http://localhost:3000/</span><br><span class="line">DONE</span><br><span class="line"></span><br><span class="line">2019/05/02 19:03:56 Executing middlewareA before handler</span><br><span class="line">2019/05/02 19:03:56 Executing middlewareB before handler</span><br><span class="line">2019/05/02 19:03:56 Executing finalHandler</span><br><span class="line">2019/05/02 19:03:56 Executing middlewareB after handler</span><br><span class="line">2019/05/02 19:03:56 Executing middlewareA after handler</span><br></pre></td></tr></table></figure><p>ということで期待通りの実装ができた。</p><h3 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h3><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Handler <span class="keyword">interface</span> &#123;</span><br><span class="line">ServeHTTP(ResponseWriter, *Request)</span><br><span class="line">&#125;</span><br><span class="line">...</span><br><span class="line"><span class="keyword">type</span> HandlerFunc <span class="function"><span class="keyword">func</span><span class="params">(ResponseWriter, *Request)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="params">(f HandlerFunc)</span> <span class="title">ServeHTTP</span><span class="params">(w ResponseWriter, r *Request)</span></span> &#123;</span><br><span class="line">f(w, r)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>http.Handlerが以下のinterfaceに依存しており、<br>ServeHTTPの実装がhandlerを呼ぶ形になっている。<br>この実装のため非常に効果的にmiddlewearを作成することができる。</p><p>このあたりがgolangは柔軟に設計されていて,interfaceにのみ依存したkたちで<br>実際の実装をしていく感じを体験できるものだと思う。<br>次はhttp.Clientでも同じように見ていければ。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Golangでwebアプリケーションなどを開発するに当たり、&lt;br&gt;リクエストやレスポンスをインターセプトし適宜利用するmiddlewearの実装パターンを&lt;br&gt;読んでみたときのメモ。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>Typescript Compiler APIを斜め読み</title>
    <link href="http://blog.kazu69.net/2019/04/03/typescript-compiler-relearning/"/>
    <id>http://blog.kazu69.net/2019/04/03/typescript-compiler-relearning/</id>
    <published>2019-04-02T16:25:12.000Z</published>
    <updated>2019-06-23T14:59:45.000Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API" target="_blank" rel="noopener">TypeScript Compiler API</a>を利用して独自のAST変換機構を試すために調べたメモ。</p><a id="more"></a><p>TypescriptCompiler APIを利用することでASTの解析および変更が可能。<br>独自の型解析機構や構文生成機構を作成できる。例えば、TSからJSへの変更の際に独自に必要な負荷情報を追加・編集してコンパイルすることが可能になる。<br>ちなみにASTの情報は<a href="https://astexplorer.net/" target="_blank" rel="noopener">AST Exploer</a>で確認ができるため、開発の際にはこれを参考にする。</p><p>簡単なtransformコンパイラを作成して見ていく。</p><h2 id="Config-parser"><a href="#Config-parser" class="headerlink" title="Config parser"></a>Config parser</h2><p>まずはtypescriptのconfig解析機構。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> ts <span class="keyword">from</span> <span class="string">'typescript'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> configFileName: <span class="built_in">string</span>[] = process.argv.slice(<span class="number">2</span>);</span><br><span class="line"><span class="comment">// compiler option</span></span><br><span class="line"><span class="keyword">const</span> optionToExpend: ts.CompilerOptions = <span class="literal">null</span>;</span><br><span class="line"><span class="comment">// config file information</span></span><br><span class="line"><span class="keyword">const</span> createConfigFileHost: ts.ParseConfigFileHost = &#123;</span><br><span class="line">  <span class="comment">// Reporter of unrecoverable error</span></span><br><span class="line">  <span class="comment">// When parsing config file</span></span><br><span class="line">  onUnRecoverableConfigFileDiagnostic(diagnostic: ts.Diagnostic) &#123;</span><br><span class="line">    <span class="built_in">console</span>.error(diagnostic);</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(ts.flattenDiagnosticMessageText(diagnostic.messageText, <span class="string">"\n"</span>));</span><br><span class="line">  &#125;,</span><br><span class="line">  useCaseSensitiveFileNames: <span class="literal">false</span>,    <span class="comment">// file names are treated with case sensitivity &lt;boolean&gt;</span></span><br><span class="line">  readDirectory: ts.sys.readDirectory, <span class="comment">// used to cache and handle directory structure modifications &lt;string&gt;</span></span><br><span class="line">  fileExists: ts.sys.fileExists,       <span class="comment">// specified path exists and is a file. &lt;boolean&gt;</span></span><br><span class="line">  readFile: ts.sys.readFile,           <span class="comment">// Use to read file text for source files &lt;string&gt; </span></span><br><span class="line">  getCurrentDirectory: ts.sys.getCurrentDirectory, <span class="comment">// current directory of the program &lt;strig&gt;</span></span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">const</span> configParseResult: ts.ParsedCommandLine = ts.getParsedCommandLineOfConfigFile(configFileName[<span class="number">0</span>], optionToExpend, createConfigFileHost);</span><br><span class="line"><span class="built_in">console</span>.log(configParseResult);</span><br></pre></td></tr></table></figure><p><code>getParsedCommandLineOfConfigFile</code>を使うことでファイル名を指定してconfigファイルをreadできる。<br>解析結果のオブジェクトをもとにプログラムを作成し、コンパイルを行う</p><p>コンパイル関数</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// typescript compile option</span></span><br><span class="line"><span class="keyword">const</span> CJS_CONFIG: ts.CompilerOptions = &#123;</span><br><span class="line">  experimentalDecorators: <span class="literal">true</span>,</span><br><span class="line">  jsx: ts.JsxEmit.React,</span><br><span class="line">  <span class="keyword">module</span>: ts.ModuleKind.ESNext,</span><br><span class="line">  moduleResolution: ts.ModuleResolutionKind.NodeJs,</span><br><span class="line">  noEmitOnError: false,</span><br><span class="line">  noUnusedLocals: true,</span><br><span class="line">  noUnusedParameters: true,</span><br><span class="line">  stripInternal: true,</span><br><span class="line">  resolveJsonModule: true,</span><br><span class="line">  declaration: true,</span><br><span class="line">  baseUrl: __dirname,</span><br><span class="line">  target: ts.ScriptTarget.ES5</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">function compile(</span><br><span class="line">  input: string,</span><br><span class="line">  options: ts.CompilerOptions = CJS_CONFIG</span><br><span class="line">) &#123;</span><br><span class="line">  <span class="keyword">const</span> file = globSync(input);</span><br><span class="line">  <span class="keyword">const</span> compilerHost = ts.createCompilerHost(options);</span><br><span class="line">  <span class="keyword">const</span> program: ts.Program = ts.createProgram(file, options, compilerHost);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> sourceFile: ts.SourceFile = program.getSourceFile(input);</span><br><span class="line">  <span class="keyword">const</span> WriteFileCallback = <span class="function">(<span class="params">fileName, data, writeByteOrderMark, onError, sourceFiles</span>) =&gt;</span> &#123;</span><br><span class="line">    fs.writeFileSync(fileName, data, <span class="string">'utf8'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">const</span> cancellationToken: ts.CancellationToken = <span class="literal">undefined</span>;</span><br><span class="line">  <span class="keyword">const</span> emitOnlyDtsFiles: <span class="built_in">boolean</span> = <span class="literal">false</span>;</span><br><span class="line">  <span class="keyword">const</span> customTransformers: ts.CustomTransformers = &#123;</span><br><span class="line">    before: [],             <span class="comment">// to evaluate before built-in .js transformations.</span></span><br><span class="line">    after: [],              <span class="comment">// to evaluate after built-in .js transformations.</span></span><br><span class="line">    afterDeclarations: [],  <span class="comment">// to evaluate after built-in .d.ts transformations</span></span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="comment">// EmitResult &#123;</span></span><br><span class="line">  <span class="comment">//  emitSkipped: boolean;</span></span><br><span class="line">  <span class="comment">//  diagnostics: ReadonlyArray&lt;Diagnostic&gt;;</span></span><br><span class="line">  <span class="comment">//  emittedFiles?: string[];</span></span><br><span class="line">  <span class="comment">// &#125;</span></span><br><span class="line">  <span class="keyword">const</span> emitResult: ts.EmitResult = program.emit(sourceFile, WriteFileCallback, cancellationToken, emitOnlyDtsFiles, customTransformers);</span><br><span class="line">  <span class="keyword">const</span> allDiagnostics = ts</span><br><span class="line">    .getPreEmitDiagnostics(program)</span><br><span class="line">    .concat(emitResult.diagnostics);</span><br><span class="line"></span><br><span class="line">  allDiagnostics.forEach(<span class="function"><span class="params">diagnostic</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> &#123; line, character &#125; = diagnostic.file.getLineAndCharacterOfPosition(</span><br><span class="line">      diagnostic.start</span><br><span class="line">    );</span><br><span class="line">    <span class="keyword">let</span> message = ts.flattenDiagnosticMessageText(diagnostic.messageText, <span class="string">"\n"</span>);</span><br><span class="line">    <span class="built_in">console</span>.log(</span><br><span class="line">      <span class="string">`<span class="subst">$&#123;diagnostic.file.fileName&#125;</span> (<span class="subst">$&#123;line + 1&#125;</span>,<span class="subst">$&#123;character + 1&#125;</span>): <span class="subst">$&#123;message&#125;</span>`</span></span><br><span class="line">    );</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> CarriageReturnLineFeed: ts.NewLineKind = <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">const</span> printerOptions: ts.PrinterOptions = &#123;</span><br><span class="line">    removeComments: <span class="literal">true</span>,</span><br><span class="line">    newLine: CarriageReturnLineFeed,</span><br><span class="line">    omitTrailingSemicolon: <span class="literal">false</span>,</span><br><span class="line">    noEmitHelpers: <span class="literal">true</span>,</span><br><span class="line">  &#125;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// debug code print</span></span><br><span class="line">  <span class="comment">// const printer: ts.Printer = ts.createPrinter(printerOptions)</span></span><br><span class="line">  <span class="comment">// const code: string = printer.printFile(sourceFile);</span></span><br><span class="line">  <span class="comment">// console.log('code: =&gt;', code)</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> exitCode = emitResult.emitSkipped ? <span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">  <span class="keyword">if</span> (exitCode) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`Process exiting with code '<span class="subst">$&#123;exitCode&#125;</span>'.`</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  process.exit(exitCode);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>createProgram</code>でProgramというコンパイル単位のインスタンスを生成する。<br>Programインスタンスはコンパイルに必要な設定情報やソースファイル情報などをもっている。</p><p>Programの<code>emit()</code>によりjsファイル、宣言ファイルを発行する。<br>ここでtargetSourceFileがない場合は、すべての対ファイルに対してのjsファイルと宣言ファイルが発行される。<br>emmit()の<code>customTransformers</code>オプションには、独自のtransform関数をフックできるようになっている。<br>それぞれ、jsファイル発行前後、宣言ファイル発行後のイベントフックできる。</p><p>各ステップでのコンパイルの結果は<code>Diagnostic</code>という共通のInterfaceをもっており、<br>programインスタンスから収集が可能になっている。</p><p>コンパイル結果のjsコード結果を取得する場合は<code>createPrinter()</code>を使い、printerインスタンスを使う。</p><p>今度はコードのtransformを行うcustom transform関数を<code>customTransformers</code>に追加する。<br>custom transformにはtypescriptのASTが与えられる。<br>またvisitorというAST解析、コード修正を<code>再帰的</code>に呼び出す必要がある。</p><p>簡単なtransformerを作成する。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">customTransform</span>(<span class="params">options: customTransformOption</span>): <span class="title">ts</span>.<span class="title">TransformerFactory</span>&lt;<span class="title">ts</span>.<span class="title">SourceFile</span>&gt; </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> (context: ts.TransformationContext): ts.Transformer&lt;ts.SourceFile&gt; =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function">(<span class="params">sorceFile: ts.SourceFile</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> ts.visitNode(sorceFile, transformer(context, sorceFile, options))</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>transformerはNode変更に必要な関数<code>TransformerFactory</code>を生成する必要がある。<br>visitNodeで所定のNodeに対してtransnformを適用する(ここでは<code>transformer</code>)。<br>transformerの処理は以下のようにする。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">transformer</span>(<span class="params">context: ts.TransformationContext, sorce: ts.SourceFile, options: customTransformOption</span>): <span class="title">ts</span>.<span class="title">Visitor</span> </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> visitor: ts.Visitor = (node: ts.Node): ts.Node[]|ts.Node =&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (ts.isFunctionDeclaration(node)) &#123;</span><br><span class="line">      <span class="keyword">const</span> declaration: ts.VariableDeclaration = ts.createVariableDeclaration(</span><br><span class="line">        ts.createIdentifier(<span class="string">'val'</span>),</span><br><span class="line">        ts.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),</span><br><span class="line">        ts.createNumericLiteral(<span class="string">'1000'</span>)</span><br><span class="line">      );</span><br><span class="line">      <span class="keyword">const</span> declarations: ts.VariableDeclaration[] = [declaration];</span><br><span class="line">      <span class="keyword">return</span> [ts.createVariableDeclarationList(declarations, ts.NodeFlags.Const), node];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ts.visitEachChild(node, visitor, context);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> visitor;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>今回は関数宣言を変数宣言に置き換える簡単なものを作成する。<br><code>createVariableDeclarationList</code>を作成して新たなNodeを生成しているが、<br><code>updateVariableDeclarationList</code>などもあり、既存のNodeに追加することも可能である。<br>基本的に<code>create</code>と<code>update</code>はそれぞれサポートされているのでオリジナルのNodeを更新することも可能。<br>webpackやrollupなどのツールの場合、独自のtransformerの設定がサポートされているので、自前のビルド環境に組み込むことは簡単。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// webpack.config.js</span></span><br><span class="line">&#123;</span><br><span class="line">    test: <span class="regexp">/\.ts$/</span>,</span><br><span class="line">    loader: ‘ts-loader’,</span><br><span class="line">    options: &#123;</span><br><span class="line">    getCustomTransformers: <span class="function"><span class="params">()</span> =&gt;</span> (&#123;</span><br><span class="line">      after: [customTransform] <span class="comment">// set custom transformer</span></span><br><span class="line">    &#125;),</span><br><span class="line">    transpileOnly: <span class="literal">true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// rollup.config.js</span></span><br><span class="line"><span class="keyword">import</span> typescript <span class="keyword">from</span> <span class="string">'rollup-plugin-typescript2'</span>;</span><br><span class="line"><span class="keyword">import</span> customTransform <span class="keyword">from</span> <span class="string">'path/to/customTransform'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">  plugins: [</span><br><span class="line">    typescript(&#123; <span class="attr">transformers</span>: [<span class="function"><span class="params">service</span> =&gt;</span> (&#123;</span><br><span class="line">      before: [],</span><br><span class="line">      after: [ customTransform() ]</span><br><span class="line">    &#125;)] &#125;)</span><br><span class="line">  ]</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>このtransformerを実際に実行するためにいくつか変更をする。</p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="deletion">- const WriteFileCallback = (fileName, data, writeByteOrderMark, onError, sourceFiles) =&gt; &#123;&#125;</span></span><br><span class="line"><span class="addition">+ const WriteFileCallback = (fileName, data, writeByteOrderMark, onError, sourceFiles) =&gt; &#123;</span></span><br><span class="line"><span class="addition">+     fs.writeFileSync(fileName, data, 'utf8');</span></span><br><span class="line"><span class="addition">+ &#125;</span></span><br></pre></td></tr></table></figure><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">const customTransformers: ts.CustomTransformers = &#123;</span><br><span class="line">    before: [],             // to evaluate before built-in .js transformations.</span><br><span class="line">    after: [],              // to evaluate after built-in .js transformations.</span><br><span class="line"><span class="deletion">-    afterDeclarations: [],  // to evaluate after built-in .d.ts transformations</span></span><br><span class="line">  &#125;;</span><br><span class="line">const customTransformers: ts.CustomTransformers = &#123;</span><br><span class="line"><span class="addition">+    before: [</span></span><br><span class="line"><span class="addition">+      customTransform(&#123;program&#125;)</span></span><br><span class="line"><span class="addition">+   ],             // to evaluate before built-in .js transformations.</span></span><br><span class="line">    after: [],              // to evaluate after built-in .js transformations.</span><br><span class="line">    afterDeclarations: [],  // to evaluate after built-in .d.ts transformations</span><br><span class="line">  &#125;;</span><br></pre></td></tr></table></figure><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="addition">+ compile(file, CJS_CONFIG);</span></span><br></pre></td></tr></table></figure><p>この変更を加えて次のファイルのcompileを実行する。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// example.ts</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Example1</span>(<span class="params"></span>) </span>&#123;&#125;;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npx ts-node custom-ast-transform.ts example.ts</span><br></pre></td></tr></table></figure><p>transformされた結果としてのコードを得ることができる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// example.js</span></span><br><span class="line"><span class="keyword">var</span> val = <span class="number">1000</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Example1</span>(<span class="params"></span>) </span>&#123; &#125;</span><br><span class="line">;</span><br></pre></td></tr></table></figure><p>コールスタックをみていくと、program#emitで<code>program#runWithCancellationToken</code>が呼ばれ、このrunWithCancellationTokenは処理を<a href="https://github.com/microsoft/TypeScript/blob/c5578a2b4318d38f102956b00ae375ba9b05b616/src/compiler/program.ts#L1530" target="_blank" rel="noopener"><code>program#emitWorker</code></a>に移譲する。<br>この内部で<a href="https://github.com/microsoft/TypeScript/blob/375487ec602c7cc8b4dd9e3991ad6145d8bbe21f/src/compiler/checker.ts#L33" target="_blank" rel="noopener">createTypeChecker</a>により<code>checker</code>オブジェクトが渡される。これにより多くのAPIが提供される。その一つの<a href="https://github.com/microsoft/TypeScript/blob/375487ec602c7cc8b4dd9e3991ad6145d8bbe21f/src/compiler/checker.ts#L800" target="_blank" rel="noopener">TypeChecker#getEmitResolver</a>により<a href="https://github.com/microsoft/TypeScript/blob/375487ec602c7cc8b4dd9e3991ad6145d8bbe21f/src/compiler/types.ts#L3578" target="_blank" rel="noopener">emitresolver</a>が提供される。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">program#emit</span><br><span class="line">  program#runWithCancellationToken</span><br><span class="line">    program#emitWorker</span><br><span class="line">      checker#createTypeChecker</span><br><span class="line">         =&gt; checker</span><br></pre></td></tr></table></figure><p>引き続きemitWorker内で<a href="https://github.com/microsoft/TypeScript/blob/c5578a2b4318d38f102956b00ae375ba9b05b616/src/compiler/program.ts#L1576" target="_blank" rel="noopener">emitFiles</a>が呼ばれ、オプションとしてsourceFileとemitResolverが渡される。</p><p>emitFilesではtsをjsにtransformしていく。<br>forEachEmittedFileのオプションで渡される<a href="https://github.com/microsoft/TypeScript/blob/7611c5b9315b56c665121ae85ed5b20163399658/src/compiler/emitter.ts#L251" target="_blank" rel="noopener">emitSourceFileOrBundle</a>のemitJsFileOrBundleで<a href="https://github.com/microsoft/TypeScript/blob/53467ae4a4edb37c259b0b4e61972a90cd6c5587/src/compiler/transformer.ts#L141" target="_blank" rel="noopener">transformNodes</a>が呼ばれ変換される。<br><a href="https://github.com/microsoft/TypeScript/blob/53467ae4a4edb37c259b0b4e61972a90cd6c5587/src/compiler/transformer.ts#L199" target="_blank" rel="noopener">transformation</a>で変換する。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">emitter#emitFiles</span><br><span class="line">  emitter#forEachEmittedFile</span><br><span class="line">    emitSourceFileOrBundle</span><br><span class="line">      emitJsFileOrBundle</span><br><span class="line">        transformer#transformNodes</span><br><span class="line">          transformer#transformation</span><br></pre></td></tr></table></figure><p>最後に<a href="https://github.com/microsoft/TypeScript/blob/7611c5b9315b56c665121ae85ed5b20163399658/src/compiler/emitter.ts#L420" target="_blank" rel="noopener">printSourceFileOrBundle</a>が実施されてファイルに出力される。</p><p>今回は調査のために簡単なサンプルを作成しただけになるが、Typescript Compiler APIを活用することで独自の型チェック機構(linter)のようなものや型解析からのドキュメント自動生成、Debuger、polyfillの自動追加機構などのプラグインを作成可能になる。</p><p>APIの変更履歴は<a href="https://github.com/microsoft/TypeScript/wiki/API-Breaking-Changes" target="_blank" rel="noopener">API Breaking Changes</a>にあるので、利用するTypescriptのバージョンによっては確認しておくと良さそう。</p><p>型推論自体は<a href="https://github.com/microsoft/TypeScript/blob/375487ec602c7cc8b4dd9e3991ad6145d8bbe21f/src/compiler/checker.ts#L15161" target="_blank" rel="noopener">inferTypes</a>で行われ、このメソッドは<code>createTypeChecker</code>で生成される<code>checker</code>を経由して実行されている。<br><code>initializeTypeChecker</code>で</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">program#getTypeChecker</span><br><span class="line">    ts#createTypeChecker</span><br><span class="line">        initializeTypeChecker</span><br></pre></td></tr></table></figure><p>型推論に関しては別途で見てみよう。</p><h3 id="参考ページ"><a href="#参考ページ" class="headerlink" title="参考ページ"></a>参考ページ</h3><p><a href="https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API" target="_blank" rel="noopener">Using the Compiler API</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;a href=&quot;https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TypeScript Compiler API&lt;/a&gt;を利用して独自のAST変換機構を試すために調べたメモ。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>GraphQLについて再入門</title>
    <link href="http://blog.kazu69.net/2019/02/21/learning-graphql/"/>
    <id>http://blog.kazu69.net/2019/02/21/learning-graphql/</id>
    <published>2019-02-20T15:12:25.000Z</published>
    <updated>2019-05-14T15:44:21.000Z</updated>
    
    <content type="html"><![CDATA[<p>知ったつもりだったけど、いざとなると知らないこと多かったので<br>もう少しだけ突っ込んで入門したときの走り書き。</p><a id="more"></a><p><a href="http://shop.oreilly.com/product/0636920137269.do" target="_blank" rel="noopener">Learning GraphQL</a> を読むんで再認識したことも中心にメモした。</p><h3 id="グラフ理論"><a href="#グラフ理論" class="headerlink" title="グラフ理論"></a>グラフ理論</h3><p>GraphQLはこのグラフ理論に基づく考え方を知っておく必要がある。<br>ノード（節点・頂点）の集合とエッジ（枝・辺）の集合で構成されるグラフに関する数学の理論。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">G(グラフ) = (V(頂点)、E(頂点))</span><br></pre></td></tr></table></figure><p>で表すことができる。</p><p>ノード間に方向や階層がないものを<code>無向グラフ</code>という。</p><p>ノード間の移動はどこからでもできて、方向は任意。非線形データ構造。<br>また、辺が順序付けられたペアであるときは<code>有向グラフ</code>になる</p><h3 id="実世界のグラフ"><a href="#実世界のグラフ" class="headerlink" title="実世界のグラフ"></a>実世界のグラフ</h3><p>Facebookの各ユーザーの結びつきは、相互に関連し合う多数の関係を持つ構造で無向グラフ。<br>それに対してTwitterは有向グラフ。フォローするが、フォローされるわけではないため。</p><h2 id="GraphQL"><a href="#GraphQL" class="headerlink" title="GraphQL"></a>GraphQL</h2><h3 id="GraphQLとは"><a href="#GraphQLとは" class="headerlink" title="GraphQLとは"></a>GraphQLとは</h3><p>GraphQLはクエリ言語および実行エンジンであり、クエリを使用してデータを変更または削除できる。<br>GraphQLクエリをAPIに送信し、1つ以上のデータベース、REST API、WebSocketなどにデータを格納する。<br>MySQLのINSERT、UPDATE、DELETEと違いデータ変更を1つのMutation型にまとめ、データ型にまとめ実行するなどが可能。<br>ソケットを介しデータ変更を監視するためのsubscribe型がある。</p><h3 id="GraphQLの操作"><a href="#GraphQLの操作" class="headerlink" title="GraphQLの操作"></a>GraphQLの操作</h3><p>Graphqlの操作タイプは3種類ある。</p><ul><li>query: 問い合わせクエリ</li><li>mutation: 作成、更新、削除クエリ</li><li>subscription: websocketを介した変更監視クエリ</li></ul><p>GraphQLのqueryの例</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">query &#123;</span><br><span class="line">  person &#123;</span><br><span class="line">    name</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上記は以下のようなhttpリクエストを通じて実行できる。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ curl <span class="string">'http://my.graphql.com'</span></span><br><span class="line">  -H <span class="string">'Content-Type：application/json'</span></span><br><span class="line">  --data <span class="string">'&#123;"query"： "&#123;person &#123;name&#125;&#125;"&#125;'</span></span><br></pre></td></tr></table></figure><p>データ更新する場合は次の雰囲気のクエリを実行する。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">mutation &#123;</span><br><span class="line">  healthStatus(id: &quot;TOM&quot; status: &apos;OK&apos;) &#123;</span><br><span class="line">    name</span><br><span class="line">    status</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>こちらは以下のhttpリクエストで行える</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl <span class="string">'http://my.graphql.com'</span></span><br><span class="line">  -H <span class="string">'Content-Type: application/json'</span></span><br><span class="line">  --data <span class="string">'&#123;"query":"mutation &#123;healthStatus(id: \"TOM\" status: \"OK\") &#123;name status&#125;&#125;"&#125;'</span></span><br></pre></td></tr></table></figure><p>フィールドには引数を受け付けることもできる</p><h3 id="Fragmant"><a href="#Fragmant" class="headerlink" title="Fragmant"></a>Fragmant</h3><p>再利用可能なクエリの実行単位。<br>大きなクエリや頻繁に利用されるデータ要件を分割管理することができる。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">fragment somethingFields on Person &#123;</span><br><span class="line">  name</span><br><span class="line">  likes &#123;</span><br><span class="line">    foods</span><br><span class="line">    books</span><br><span class="line">    sports</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Introspection"><a href="#Introspection" class="headerlink" title="Introspection"></a>Introspection</h3><p>ざっくりGraphQLがどのようなクエリやフィールドをサポートしているのかを問合る機能。</p><p><strong>Schema、</strong>Type、<strong>TypeKind、</strong>Field、<strong>nputValue、</strong>EnumValue、<strong>Directive<br>アンダースコア(</strong>)で始まるこれらはすべてイントロスペクションを表す。<br>内部の情報をクエリ経由で参照できる。つまりそのgraphqlサービスが提供するすべてを知ることができる。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">__type(name: &quot;Node&quot;) &#123;</span><br><span class="line">  name</span><br><span class="line">  kind</span><br><span class="line">  fields &#123;</span><br><span class="line">    name</span><br><span class="line">    type &#123;</span><br><span class="line">      kind</span><br><span class="line">      ofType &#123;</span><br><span class="line">        name</span><br><span class="line">        kind</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"__type"</span>: &#123;</span><br><span class="line">    <span class="attr">"name"</span>: <span class="string">"Node"</span>,</span><br><span class="line">    <span class="attr">"kind"</span>: <span class="string">"INTERFACE"</span>,</span><br><span class="line">    <span class="attr">"fields"</span>: [</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">"name"</span>: <span class="string">"id"</span>,</span><br><span class="line">        <span class="attr">"type"</span>: &#123;</span><br><span class="line">          <span class="attr">"kind"</span>: <span class="string">"NON_NULL"</span>,</span><br><span class="line">          <span class="attr">"ofType"</span>: &#123;</span><br><span class="line">            <span class="attr">"name"</span>: <span class="string">"ID"</span>,</span><br><span class="line">            <span class="attr">"kind"</span>: <span class="string">"SCALAR"</span></span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Directive"><a href="#Directive" class="headerlink" title="Directive"></a>Directive</h2><p>Directiveを利用することで既存の型システムに注釈をつけることができる。<br>ただしこればGraphqlサーバーの実装による。<br><a href="https://graphql.org/learn/queries/#directives" target="_blank" rel="noopener">https://graphql.org/learn/queries/#directives</a></p><h3 id="deprecatedディレクティブ"><a href="#deprecatedディレクティブ" class="headerlink" title="@deprecatedディレクティブ"></a>@deprecatedディレクティブ</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">directive @deprecated(</span><br><span class="line">  reason: String = &quot;No longer supported&quot;</span><br><span class="line">) on FIELD_DEFINITION | ENUM_VALUE</span><br><span class="line"></span><br><span class="line">type ExampleType &#123;</span><br><span class="line">  newField: String</span><br><span class="line">  oldField: String @deprecated(reason: &quot;Use `newField`.&quot;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上記の場合@deprecatedディレクティブ宣言が与えられると、引数types(reason: String)とlocations(FIELD_DEFINITION | ENUM_VALUE)を適用するかどうかはサーバーの実装次第になる。</p><p>@exampleディレクティ部はフィールド<code>FIELD_DEFINITION</code>と引数定義<code>ARGUMENT_DEFINITION</code>に注釈をつけることができる。</p><h3 id="skipディレクティブ"><a href="#skipディレクティブ" class="headerlink" title="@skipディレクティブ"></a>@skipディレクティブ</h3><p>クエリの実行をスキップする。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT</span><br><span class="line"></span><br><span class="line">query myQuery($isStatus: Boolean) &#123;</span><br><span class="line">  experimentalField @skip(if: $isStatus)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>$isStatusがtrueの場合のみクエリが実行される。</p><h3 id="includeディレクティブ"><a href="#includeディレクティブ" class="headerlink" title="@includeディレクティブ"></a>@includeディレクティブ</h3><p>@skipとは逆にクエリの条件にふくめることになる</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT</span><br><span class="line"></span><br><span class="line">query myQuery($isStatus: Boolean) &#123;</span><br><span class="line">  experimentalField @include(if: $isStatus)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="クエリでの再帰的検索"><a href="#クエリでの再帰的検索" class="headerlink" title="クエリでの再帰的検索"></a>クエリでの再帰的検索</h2><p>GraphQLはデータを無限に再帰検索しない。<br>Graphデータが無限再起でないことを保証する方法がない。<br>また再帰的にしても、クエリを利用するアプリケーションの制約があるため利用できないことがある。<br>対策法として、任意の深さまでデータを取得し、更に追加の情報を取得する必要がある場合は「もっと見る」などのリンクを追加することがよい。</p><p><a href="https://github.com/facebook/graphql/issues/91#issuecomment-254895093" target="_blank" rel="noopener">この辺の議論</a></p><h2 id="ID型について"><a href="#ID型について" class="headerlink" title="ID型について"></a>ID型について</h2><p>IDがintなのかstringがいいのかとかの話につながるが、<a href="https://facebook.github.io/graphql/June2018/#sec-ID" target="_blank" rel="noopener">Scaler-ID</a>を読むと以下の通り。</p><blockquote><p>The ID type is serialized in the same way as a String; however, it is not intended to be human‐readable. While it is often numeric, it should always serialize as a String.</p></blockquote><p>IDはStringと同じ方法でシリアライズされます。ただし、人間が読める形式ではありません。<br>多くの場合数値ですが、常にStringとしてシリアル化する必要がある。</p><h3 id="言語実装"><a href="#言語実装" class="headerlink" title="言語実装"></a>言語実装</h3><p><a href="https://github.com/graphql/graphql-js" target="_blank" rel="noopener">graphql.js</a>を例にすると、<a href="https://github.com/graphql/graphql-js/blob/master/src/language/lexer.js" target="_blank" rel="noopener">lexer</a>, <a href="https://github.com/graphql/graphql-js/blob/master/src/language/ast.js" target="_blank" rel="noopener">ast</a>, <a href="https://github.com/graphql/graphql-js/blob/master/src/language/parser.js" target="_blank" rel="noopener">parser</a>があることから、これを基準に各言語でも言語処理が可能。</p><h3 id="GraphQL周辺のコミュニティ"><a href="#GraphQL周辺のコミュニティ" class="headerlink" title="GraphQL周辺のコミュニティ"></a>GraphQL周辺のコミュニティ</h3><p>GraphQLは既にFaceBookを離れて<a href="https://gql.foundation/" target="_blank" rel="noopener"><code>GraphQL Foundation</code></a>となっている。</p><p><a href="https://www.apollographql.com/" target="_blank" rel="noopener">Apollo GraphQL</a>でGraphQL界隈のツールを多く提供している。<code>Meteor Development Group Inc.</code>が母体。</p><p><a href="https://www.prisma.io/" target="_blank" rel="noopener">prisma</a></p><h3 id="GraphQLをとり言えれたアプリケーションのアーキテクチャ"><a href="#GraphQLをとり言えれたアプリケーションのアーキテクチャ" class="headerlink" title="GraphQLをとり言えれたアプリケーションのアーキテクチャ"></a>GraphQLをとり言えれたアプリケーションのアーキテクチャ</h3><p>REST APIでは、複数のエンドポイントにアクセスしてデータを収集する。<br>GraphQLはデータ要件を含む単一のクエリをGraphQLサーバーに送信するだけになる。<br>よって、複数のエンドポイントを束ねたエンドポイントを構築するよりも、ミドルウェアとしてGraphQLを導入する。<br>また、スキーマファーストでの開発となる。既にあるリソースを用い、データ構造を抽象化していくことになる。<br>あらかじめ各データのスキーマを定義しておくことでデータ結合のためのビジネスロジックの開発から解放される。</p><p><a href="https://netflix.github.io/falcor/" target="_blank" rel="noopener">Falcor</a>のような解決方法もあるが<code>GraphQL</code>のエコシステムを選ぶことが懸命になっている。</p><h3 id="セキュリティ上検討すべきこと"><a href="#セキュリティ上検討すべきこと" class="headerlink" title="セキュリティ上検討すべきこと"></a>セキュリティ上検討すべきこと</h3><h4 id="認証"><a href="#認証" class="headerlink" title="認証"></a>認証</h4><p>実行クライアントを制限。認証Tokenなどを使い実行クライアントを認証することができる。<br>(Githubなど)</p><h4 id="SlowQuery"><a href="#SlowQuery" class="headerlink" title="SlowQuery"></a>SlowQuery</h4><p>Slowクエリなどの問題は適切なタイムアウトを設定しておく必要がある。<br>ただし、jmutationなどが実行された場合、すでにデータに不整合が発生していることもある。</p><h4 id="Max-Depth"><a href="#Max-Depth" class="headerlink" title="Max Depth"></a>Max Depth</h4><p>クエリの深度を限定する。再帰的に検索することにもなるため、クエリ実行時にAST分析を行い、最大深度以上のフィールをを無視する。(またはエラーにする)実装が必要。<br>DoSなどでネスとクエリで攻撃をかけることも可能。</p><h4 id="Complex-Query"><a href="#Complex-Query" class="headerlink" title="Complex Query"></a>Complex Query</h4><p>複雑なクエリも定義できるが、実装は難しくなることもある。<br>また、複雑ゆえに本来隠匿されるべきデータへのアクセスにより、情報が漏洩することや、<br>データ構造からSQL/NoSQLへのInjectionも起こりえそうなため<br>できるだけクエリはシンプルに保つことが望ましそう。</p><h4 id="Validation"><a href="#Validation" class="headerlink" title="Validation"></a>Validation</h4><p>アカウント認証、パスワード認証などの検証機構は提供されてない(あくまでクエリ言語なのため)。<br>実装するやり方にもよるが、自前でタイプなどの実装が必要になることも。<br>Validation不備の場合、webのアプリケーションと同じく、injectionやXSSの脆弱性を生むことにもつながる。</p><p>そもそもGraphQLで実現すべき機能なのかも踏まえて検討するべき。</p><h2 id="実装に関して"><a href="#実装に関して" class="headerlink" title="実装に関して"></a>実装に関して</h2><h3 id="Apollo界隈のtool中心に"><a href="#Apollo界隈のtool中心に" class="headerlink" title="Apollo界隈のtool中心に"></a>Apollo界隈のtool中心に</h3><p>Apollo Server利用にしている場合。<br><a href="https://github.com/apollographql/graphql-tools/blob/wip-schema-directives/src/schemaVisitor.ts" target="_blank" rel="noopener"><code>SchemaDirectiveVisitor</code></a>を利用する。<br>このクラスにはl<a href="https://github.com/apollographql/graphql-tools/blob/wip-schema-directives/src/schemaVisitor.ts#L82-L99" target="_blank" rel="noopener">locationsに対応するメソッド</a>が準備されているのでそれをオーバーライドすることになる。</p><p><code>SchemaDirectiveVisitor</code>を継承した<code>DeprecatedDirective</code>を作成。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// directive @deprecated(</span></span><br><span class="line"><span class="comment">//   reason: String = "No longer supported"</span></span><br><span class="line"><span class="comment">// ) on FIELD_DEFINITION | ENUM_VALUE</span></span><br><span class="line"><span class="comment">// 上記を実装する場合</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; SchemaDirectiveVisitor &#125; <span class="keyword">from</span> <span class="string">'graphql-tools'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> DeprecatedDirective <span class="keyword">extends</span> SchemaDirectiveVisitor &#123;</span><br><span class="line">  <span class="keyword">public</span> visitFieldDefinition(field: GraphQLField&lt;<span class="built_in">any</span>, <span class="built_in">any</span>&gt;) &#123;</span><br><span class="line">    field.isDeprecated = <span class="literal">true</span>;</span><br><span class="line">    field.deprecationReason = <span class="keyword">this</span>.args.reason;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> visitEnumValue(value: GraphQLEnumValue) &#123;</span><br><span class="line">    value.isDeprecated = <span class="literal">true</span>;</span><br><span class="line">    value.deprecationReason = <span class="keyword">this</span>.args.reason;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; makeExecutableSchema &#125; <span class="keyword">from</span> <span class="string">'graphql-tools'</span>;</span><br><span class="line"><span class="keyword">import</span> DeprecatedDirective <span class="keyword">from</span> <span class="string">'directive/DeprecatedDirective'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> typeDefs = <span class="string">`</span></span><br><span class="line"><span class="string">type ExampleType &#123;</span></span><br><span class="line"><span class="string">  newField: String</span></span><br><span class="line"><span class="string">  oldField: String @deprecated(reason: "No longer supported")</span></span><br><span class="line"><span class="string">&#125;`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> schema = makeExecutableSchema(&#123;</span><br><span class="line">  typeDefs,</span><br><span class="line">  schemaDirectives: &#123;</span><br><span class="line">    deprecated: DeprecatedDirective</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>ユースケーストして日付フォーマットの変更の場合</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; defaultFieldResolver &#125; <span class="keyword">from</span> <span class="string">"graphql"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> typeDefs = <span class="string">`</span></span><br><span class="line"><span class="string">directive @date(format: String) on FIELD_DEFINITION</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">scalar Date</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">type Post &#123;</span></span><br><span class="line"><span class="string">  published: Date @date(format: "mmmm d, yyyy")</span></span><br><span class="line"><span class="string">&#125;`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> DateFormatDirective <span class="keyword">extends</span> SchemaDirectiveVisitor &#123;</span><br><span class="line">  visitFieldDefinition(field) &#123;</span><br><span class="line">    <span class="comment">// https://github.com/graphql/graphql-js/blob/master/src/execution/execute.js#L1238</span></span><br><span class="line">    <span class="comment">// fileld is `resolve`</span></span><br><span class="line">    <span class="keyword">const</span> &#123; resolve = defaultFieldResolver &#125; = field;</span><br><span class="line">    <span class="keyword">const</span> &#123; format &#125; = <span class="keyword">this</span>.args;</span><br><span class="line">    <span class="comment">// overwrite resolve filed method</span></span><br><span class="line">    field.resolve = <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> (<span class="params">...args</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">const</span> date = <span class="keyword">await</span> resolve.apply(<span class="keyword">this</span>, args);</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">require</span>(<span class="string">'dateformat'</span>)(date, format);</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="comment">// The formatted Date becomes a String, so the field type must change:</span></span><br><span class="line">    field.type = GraphQLString;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> schema = makeExecutableSchema(&#123;</span><br><span class="line">  typeDefs,</span><br><span class="line">  schemaDirectives: &#123;</span><br><span class="line">    date: DateFormatDirective</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="Apollo-GraphQL-Platform"><a href="#Apollo-GraphQL-Platform" class="headerlink" title="Apollo GraphQL Platform"></a>Apollo GraphQL Platform</h2><p><img src="https://www.apollographql.com/docs/img/platform-diagram.png" alt></p><h3 id="Apollo-Client"><a href="#Apollo-Client" class="headerlink" title="Apollo Client"></a>Apollo Client</h3><p>名前の通り。<br><a href="https://github.com/apollographql/apollo-client" target="_blank" rel="noopener">https://github.com/apollographql/apollo-client</a><br>アプリのデータと状態を管理するClient。</p><h3 id="InMemoryCache"><a href="#InMemoryCache" class="headerlink" title="InMemoryCache"></a>InMemoryCache</h3><p>Apollo Clientのキャッシュ機構。<br>Apollo ClientのDataProxyを通して、<br><code>readQuery</code>, <code>readFragment</code>, <code>writeQuery</code>, <code>writeFragment</code> インターフェイスを経由してcacheとやり取りする。</p><h4 id="readQuery"><a href="#readQuery" class="headerlink" title="readQuery"></a>readQuery</h4><p><code>query</code>の場合は、サーバーにリクエストするが、<code>readQuery</code>はApplicationにcacheあがない場合はエラーを投げる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; todo &#125; = client.readQuery(&#123;</span><br><span class="line">  query: gql<span class="string">`</span></span><br><span class="line"><span class="string">    query ReadTodo &#123;</span></span><br><span class="line"><span class="string">      todo(id: 5) &#123;</span></span><br><span class="line"><span class="string">        id</span></span><br><span class="line"><span class="string">        text</span></span><br><span class="line"><span class="string">        completed</span></span><br><span class="line"><span class="string">      &#125;</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  `</span>,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">client.writeFragment(&#123;</span><br><span class="line">  id: <span class="string">'5'</span>,</span><br><span class="line">  fragment: gql<span class="string">`</span></span><br><span class="line"><span class="string">    fragment myTodo on Todo &#123;</span></span><br><span class="line"><span class="string">      completed</span></span><br><span class="line"><span class="string">    &#125;</span></span><br><span class="line"><span class="string">  `</span>,</span><br><span class="line">  data: &#123;</span><br><span class="line">    completed: <span class="literal">true</span>,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="Appolo-Link"><a href="#Appolo-Link" class="headerlink" title="Appolo Link"></a>Appolo Link</h3><p>GraphQLサーバーへのリクエストを制御するインターフェイス。<br>GraphQLリクエストを実行すると、各Linkの機能が次々に適用されます。<br>Apollo Clientコアにある機能をモジュラーリンクに移行する予定。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ApolloLink.from([...])</span><br></pre></td></tr></table></figure><p><code>from</code>で配列を受け取りそれらを単一のリンクに統合する。<br>内部的にreduceする。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ApolloLink.concat(x, y)</span><br></pre></td></tr></table></figure><p><code>concat</code>でリンクを結合して1つにする。</p><p>状況によってことなるリンクを使うのであれば</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ApolloLink.spilit(conditon, condition_is_true, condition_is_false)</span><br></pre></td></tr></table></figure><p><code>spilit</code>のオプションは第一引数に条件、第二引数にtrueの場合のリンク、第三引数にfalseの場合のリンク</p><h4 id="apollo-link-state"><a href="#apollo-link-state" class="headerlink" title="apollo-link-state"></a>apollo-link-state</h4><p>ローカルの状態を管理する。<br>起動時に<code>withClientState</code>でローカルのデータストアに状態を登録する。</p><h4 id="apollo-cache-persist"><a href="#apollo-cache-persist" class="headerlink" title="apollo-cache-persist"></a>apollo-cache-persist</h4><p>clientの永続化ストレージにstateを自動的に保存または復元する。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; InMemoryCache &#125; <span class="keyword">from</span> <span class="string">'apollo-cache-inmemory'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; persistCache &#125; <span class="keyword">from</span> <span class="string">'apollo-cache-persist'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cache = <span class="keyword">new</span> InMemoryCache(&#123;...&#125;);</span><br><span class="line"></span><br><span class="line">persistCache(&#123;</span><br><span class="line">  cache,</span><br><span class="line">  storage: <span class="built_in">window</span>.localStorage,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Continue setting up Apollo as usual.</span></span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient(&#123;</span><br><span class="line">  cache,</span><br><span class="line">  ...</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="apollo-link-error"><a href="#apollo-link-error" class="headerlink" title="apollo-link-error"></a>apollo-link-error</h3><p>GraphQLまたはネットワークエラー発生時のロジックを提供する。<br>以下のオブジェクトキーが利用できる。</p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>operation</td><td>エラーとなった操作</td></tr><tr><td>response</td><td>次のリンクチェーンに渡されるレスポンス</td></tr><tr><td>graphQLErrors</td><td>GraphQLエンドポイントからのエラー配列</td></tr><tr><td>networkError</td><td>errorsGraphQLサーバーのレスポンスエラーまたは結果のパースエラー</td></tr><tr><td>forward</td><td>チェーン内の次のリンクへの参照。return forward(operation)コールバックを呼び出すとリクエストが再試行される</td></tr></tbody></table><h2 id="GraphQL-Server"><a href="#GraphQL-Server" class="headerlink" title="GraphQL Server"></a>GraphQL Server</h2><h3 id="apollo-server"><a href="#apollo-server" class="headerlink" title="apollo-server"></a>apollo-server</h3><p>名前の通り。<br><a href="https://github.com/apollographql/apollo-server" target="_blank" rel="noopener">https://github.com/apollographql/apollo-server</a><br>JavaScript GraphQLサーバー。schmemeとresolverを定義。</p><p><img src="https://www.apollographql.com/docs/apollo-server/images/index-diagram.svg" alt></p><p>バックエンドに既存のREST API、Database, Microserviceの場合、それぞれが持っているミドルウェアと一緒に組み込むことが可能。<br>またAmazon LambdaやMicrosoft Azure Functionsなどのサーバーレス(Faas)をバックエンドに使うことも可能。</p><p>apllo-serverのintegrationには以下がある。</p><ul><li>apollo-server-express</li><li>apollo-server-koa</li><li>apollo-server-hapi</li><li>apollo-server-lambda</li><li>apollo-server-azure-functions</li><li>apollo-server-cloud-functions</li><li>apollo-server-cloudflare</li></ul><h3 id="Mocking"><a href="#Mocking" class="headerlink" title="Mocking"></a>Mocking</h3><p>フロントエンドの開発者がバックエンドの実装を待つことなくUIコンポーネントと機能を構築できるようにするためにmocking機能がある。</p><p>モックの例</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; ApolloServer, gql &#125; = <span class="built_in">require</span>(<span class="string">'apollo-server'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> typeDefs = gql<span class="string">`</span></span><br><span class="line"><span class="string">  type Query &#123;</span></span><br><span class="line"><span class="string">    hello: String</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> mocks = &#123;</span><br><span class="line">  Person: <span class="function"><span class="params">()</span> =&gt;</span> (&#123;</span><br><span class="line">    name: casual.name,</span><br><span class="line">    age: <span class="function"><span class="params">()</span> =&gt;</span> casual.integer(<span class="number">0</span>, <span class="number">120</span>),</span><br><span class="line">  &#125;),</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> server = <span class="keyword">new</span> ApolloServer(&#123;</span><br><span class="line">  typeDefs,</span><br><span class="line">  mocks,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">server.listen().then(<span class="function">(<span class="params">&#123; url &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`Server ready at <span class="subst">$&#123;url&#125;</span>`</span>)</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="MockList-Resolver"><a href="#MockList-Resolver" class="headerlink" title="MockList Resolver"></a>MockList Resolver</h4><p>モックの作成を自動化するために<code>MockList</code>クラスを利用できる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; MockList &#125; = <span class="built_in">require</span>(<span class="string">'apollo-server'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> mocks = &#123;</span><br><span class="line">  Person: <span class="function"><span class="params">()</span> =&gt;</span> (&#123;</span><br><span class="line">    <span class="comment">// a list of length between 2 and 6 (inclusive)</span></span><br><span class="line">    friends: <span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">new</span> MockList([<span class="number">2</span>,<span class="number">6</span>]),</span><br><span class="line">    <span class="comment">// a list of three lists each with two items: [[1, 1], [2, 2], [3, 3]]</span></span><br><span class="line">    listOfLists: <span class="function"><span class="params">()</span> =&gt;</span> <span class="keyword">new</span> MockList(<span class="number">3</span>, () =&gt; <span class="keyword">new</span> MockList(<span class="number">2</span>)),</span><br><span class="line">  &#125;),</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h4 id="Resolver"><a href="#Resolver" class="headerlink" title="Resolver"></a>Resolver</h4><p>リゾルバーはGraphQL操作をデータに変換するための方法を提供します。</p><h4 id="Resolver-Map"><a href="#Resolver-Map" class="headerlink" title="Resolver Map"></a>Resolver Map</h4><p>すべてのクエリに対応するために、スキーマはすべてのフィールドに対して解決関数を持っている。<br>この関数の集合を<code>Resolver Map</code>という。<br>スキーマのフィールドと型を関数で紐付ける。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; gql &#125; = <span class="built_in">require</span>(<span class="string">'apollo-server'</span>);</span><br><span class="line"><span class="keyword">const</span> &#123; find, filter &#125; = <span class="built_in">require</span>(<span class="string">'lodash'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> schema = gql<span class="string">`</span></span><br><span class="line"><span class="string">  type Book &#123;</span></span><br><span class="line"><span class="string">    title: String</span></span><br><span class="line"><span class="string">    author: Author</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">  type Author &#123;</span></span><br><span class="line"><span class="string">    books: [Book]</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">  type Query &#123;</span></span><br><span class="line"><span class="string">    author: Author</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> resolvers = &#123;</span><br><span class="line">  Query: &#123;</span><br><span class="line">    author(parent, args, context, info) &#123;</span><br><span class="line">      <span class="keyword">return</span> find(authors, &#123; <span class="attr">id</span>: args.id &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">  Author: &#123;</span><br><span class="line">    books(author) &#123;</span><br><span class="line">      <span class="keyword">return</span> filter(books, &#123; <span class="attr">author</span>: author.name &#125;);</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>型とリゾルバは組み合わせることができるので、複数に分けて管理することは可能。</p><h4 id="リゾルバ関数"><a href="#リゾルバ関数" class="headerlink" title="リゾルバ関数"></a>リゾルバ関数</h4><p>schemeで定義されたフィールを返すPromise関数。</p><table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td>parent</td><td>親引数。親フィールドでリゾルバから返された結果を含むオブジェクト。最上位の場合は<code>rootValue</code>から渡された値。</td></tr><tr><td>args</td><td>クエリ引数。クエリ関数に渡される引数。</td></tr><tr><td>context</td><td>クエリ、リゾルバで共有されるオブジェクト。認証スコープ、データベース接続、カスタムフェッチなど複数のリゾルバで共有されるオブジェクト。</td></tr><tr><td>info</td><td>フィールド名、ルートからのパス、クエリの実行状態などが含まれる。<a href="https://github.com/graphql/graphql-js/blob/c82ff68f52722c20f10da69c9e50a030a1f218ae/src/type/definition.js#L489-L500" target="_blank" rel="noopener">参照</a></td></tr></tbody></table><p>contextの例</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> server = <span class="keyword">new</span> ApolloServer(&#123;</span><br><span class="line">  typeDefs,</span><br><span class="line">  resolvers,</span><br><span class="line">  context: <span class="function">(<span class="params">&#123; req &#125;</span>) =&gt;</span> (&#123;</span><br><span class="line">    authScope: getScope(req.headers.authorization)</span><br><span class="line">  &#125;)</span><br><span class="line">&#125;));</span><br><span class="line"></span><br><span class="line"><span class="comment">// resolver</span></span><br><span class="line">(parent, _, context) =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span>(context.authScope !== ADMIN) <span class="keyword">throw</span> AuthenticationError(<span class="string">'not admin'</span>);</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="react-apollo"><a href="#react-apollo" class="headerlink" title="react-apollo"></a>react-apollo</h3><p>ReactのためのApolloClient拡張。<br>GraphQLサーバーからデータを取得し、ReactのComponentにprops経由で接続する。<br>よく見られる高階関数パターン。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> ApolloClient <span class="keyword">from</span> <span class="string">'apollo-boost'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; ApolloProvider &#125; <span class="keyword">from</span> <span class="string">'react-apollo'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> client = <span class="keyword">new</span> ApolloClient();</span><br><span class="line"></span><br><span class="line">ReactDOM.render(</span><br><span class="line">  &lt;ApolloProvider client=&#123;client&#125;&gt;</span><br><span class="line">    &lt;MyRootComponent /&gt;</span><br><span class="line">  &lt;<span class="regexp">/ApolloProvider&gt;,</span></span><br><span class="line"><span class="regexp">  document.getElementById('root'),</span></span><br><span class="line"><span class="regexp">);</span></span><br></pre></td></tr></table></figure><p>でコンポーネントにclientを渡すことができる<br><a href="https://github.com/apollographql/react-apollo/blob/master/src/ApolloProvider.tsx#L9" target="_blank" rel="noopener">https://github.com/apollographql/react-apollo/blob/master/src/ApolloProvider.tsx#L9</a></p><p>各コンポーネントからクエリを発行する。<br>複数のクエリをcomposeして発行することも可能。</p><hr><p>ということでここまでで時間切れ。</p><h3 id="参考ページ"><a href="#参考ページ" class="headerlink" title="参考ページ"></a>参考ページ</h3><ul><li><a href="https://graphql.org/" target="_blank" rel="noopener">https://graphql.org/</a></li><li><a href="https://www.apollographql.com/docs/" target="_blank" rel="noopener">https://www.apollographql.com/docs/</a></li><li><a href="https://graphql.github.io/graphql-spec/" target="_blank" rel="noopener">https://graphql.github.io/graphql-spec/</a></li><li><a href="https://gql.foundation/" target="_blank" rel="noopener">https://gql.foundation/</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;知ったつもりだったけど、いざとなると知らないこと多かったので&lt;br&gt;もう少しだけ突っ込んで入門したときの走り書き。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>Node.js child_process, cluster, worker_thredsを確認した</title>
    <link href="http://blog.kazu69.net/2019/01/29/multiprocess_multithread_processing_of_nodejs/"/>
    <id>http://blog.kazu69.net/2019/01/29/multiprocess_multithread_processing_of_nodejs/</id>
    <published>2019-01-29T14:34:21.000Z</published>
    <updated>2019-02-24T02:02:51.000Z</updated>
    
    <content type="html"><![CDATA[<p>Node.jsにおけるいわゆる並行処理に関して再確認。<br>Node.jsは一般的にシングルスレッド、非同期IOとして認識されている。<br>これは言語としてマルチスレッドによる複雑性を排除した処理を選択した経緯があるらしい。</p><a id="more"></a><p><a href="https://nodejs.org/en/blog/release/v11.7.0/" target="_blank" rel="noopener">v11.7.0</a>で<code>worker_threads</code>が取り込まれたことで、スレッド処理が可能になった。</p><p><a href="https://github.com/nodejs/node/pull/25361" target="_blank" rel="noopener">https://github.com/nodejs/node/pull/25361</a></p><p>ちなみにv11.7.0 以前では <code>worker_threads</code> の利用の際には<a href="https://github.com/nodejs/node/blob/3a4521a4a2af30bac7f67b5a02b4433a51e9d169/src/node_options.cc#L189" target="_blank" rel="noopener"><code>--experimental-worker</code>オプション付き</a>で利用できていた。</p><h2 id="並行処理"><a href="#並行処理" class="headerlink" title="並行処理"></a>並行処理</h2><h3 id="EventLoop"><a href="#EventLoop" class="headerlink" title="EventLoop"></a>EventLoop</h3><p><a href="https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#event-loop-explained" target="_blank" rel="noopener">Event Loop Explained</a></p><p>IOバウンドな処理を考慮した場合、処理の実行タイミングを変更することで、回避することができる。<br>一般的なEventloopを利用したバックグラウンド処理がそれにあたる。<br>負荷の高いコードを分割して、<code>setImmediate</code>を活用し、IOの後で評価することができる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span>;</span><br><span class="line"><span class="keyword">const</span> num = [],</span><br><span class="line">      start = <span class="number">0</span>,</span><br><span class="line">      end = <span class="number">50000</span>;</span><br><span class="line"></span><br><span class="line">process.on(<span class="string">'count'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">c</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (c &gt; end) &#123;</span><br><span class="line">    <span class="keyword">const</span> result = num.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">prev, current, _, __</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> prev + current;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="built_in">console</span>.log(result);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  num.push(c);</span><br><span class="line"></span><br><span class="line">  setImmediate(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    process.emit(<span class="string">'count'</span>, ++c);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">process.emit(<span class="string">'count'</span>, start);</span><br></pre></td></tr></table></figure><h3 id="child-process"><a href="#child-process" class="headerlink" title="child_process"></a>child_process</h3><p>CPUバウンドな処理が渡されるとJavaScriptはシングルスレッドなので、WebアプリケーションだとUIがフリーズし、他の処理がキューイングされる。<br>そこで、CPUバウンドな処理の場合はマルチプロセス機構を使い処理することも可能。<br>マルチプロセスなので子プロセスは独自のメモリを持っているが、プロセス間に共有メモリはない。<br>また、プロセス間のメッセージングは<code>IPC</code>となる。</p><p><a href="https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/child_process.js#L117" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/child_process.js#L117</a></p><p>起動時にモジュール、を割り当て(その他のオプションも)、プロセスを起動する。</p><p><a href="https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/child_process.js#L59" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/child_process.js#L59</a></p><p>起動時のメモリ割り当てが高価な点やリアルタイム性から利用できるシーンが決まってくる。</p><p>複数プロセス利用は<a href="https://github.com/facebook/jest/tree/master/packages/jest-worker" target="_blank" rel="noopener">jest-worker</a>などで利用されている<a href="https://github.com/rvagg/node-worker-farm" target="_blank" rel="noopener"><code>node-worker-farm</code></a>のモジュール利用することが現在のところ良さそう。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="keyword">let</span> workerFarm = <span class="built_in">require</span>(<span class="string">'worker-farm'</span>),</span><br><span class="line">    process    = <span class="built_in">require</span>(<span class="string">'process'</span>),</span><br><span class="line">    workers    = workerFarm(<span class="built_in">require</span>.resolve(<span class="string">'./child'</span>));</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> num = [];</span><br><span class="line"><span class="keyword">const</span> start = <span class="number">0</span>,</span><br><span class="line">      end = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> child_prosess = <span class="built_in">require</span>(<span class="string">'os'</span>).cpus().length;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> range = <span class="function">(<span class="params">start, end, separate</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> result = [];</span><br><span class="line">  <span class="keyword">const</span> count = (end - start) / separate;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; separate; i++) &#123;</span><br><span class="line">    <span class="keyword">let</span> tmp = &#123;&#125;;</span><br><span class="line">    <span class="keyword">if</span> (i == <span class="number">0</span>) &#123;</span><br><span class="line">      tmp = &#123;</span><br><span class="line">        start,</span><br><span class="line">        end: count,</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      tmp = &#123;</span><br><span class="line">        start: result[i - <span class="number">1</span>].end + <span class="number">1</span>,</span><br><span class="line">        end: result[i - <span class="number">1</span>].end + count,</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    result.push(tmp);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ranges = range(start, end, child_prosess);</span><br><span class="line">ranges[ranges.length - <span class="number">1</span>].end += (end % child_prosess);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt;= child_prosess; i++) &#123;</span><br><span class="line">  workers(ranges[i], <span class="function"><span class="keyword">function</span> (<span class="params">err, outp</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (err) &#123;</span><br><span class="line">      <span class="built_in">console</span>.error(err);</span><br><span class="line">    &#125;</span><br><span class="line">    num = num.concat(outp)</span><br><span class="line">    <span class="keyword">if</span> (i == child_prosess - <span class="number">1</span>) &#123;</span><br><span class="line">      workerFarm.end(workers);</span><br><span class="line">      <span class="keyword">const</span> result = num.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">prev, current, _, __</span>) </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> prev + current;</span><br><span class="line">      &#125;);</span><br><span class="line">      <span class="built_in">console</span>.log(result);</span><br><span class="line">      process.on(<span class="string">'exit'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        process.exit(<span class="number">0</span>);</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// child.js</span></span><br><span class="line"><span class="meta">'use strict'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> num = [];</span><br><span class="line"><span class="keyword">const</span> count = <span class="function"><span class="keyword">function</span>(<span class="params">start, end</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = start; i &lt;= end; i++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (num.some(<span class="function"><span class="keyword">function</span>(<span class="params">v</span>) </span>&#123; <span class="keyword">return</span> v === i &#125;) === <span class="literal">false</span>) &#123;</span><br><span class="line">      num.push(i)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params">inp, callback</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (!inp) &#123; <span class="keyword">return</span> &#125;</span><br><span class="line">  <span class="keyword">const</span> &#123;start, end&#125; = inp;</span><br><span class="line">  count(start, end);</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">`<span class="subst">$&#123;process.pid&#125;</span> running`</span>);</span><br><span class="line">  callback(<span class="literal">null</span>, num);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="cluster"><a href="#cluster" class="headerlink" title="cluster"></a>cluster</h3><p>child_processと同じく、子プロセスをforkする。</p><p><a href="https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/internal/cluster/master.js#L162" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/internal/cluster/master.js#L162</a></p><p><a href="https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/internal/cluster/master.js#L102" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/internal/cluster/master.js#L102</a></p><p>child_processでは実行モジュールなどをオプションとして渡す必要があるが、<br>clusterは親プロセスと同じモジュールの先頭から実行するため不要になる。</p><p><a href="https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/internal/cluster/master.js#L284" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/2c84f6e75cc513fe6e958f0489d104ee883db232/lib/internal/cluster/master.js#L284</a></p><p>Nodejsのアプリケーション自体を並列化する。<br><a href="https://nodejs.org/api/cluster.html" target="_blank" rel="noopener"><code>clusterモジュール</code></a>が利用できる。<br>マスタープロセスにリクエストが来ると、ワーカープロセスに処理を委譲する。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> cluster = <span class="built_in">require</span>(<span class="string">'cluster'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> num = [];</span><br><span class="line"><span class="keyword">const</span> start = <span class="number">0</span>,</span><br><span class="line">      end = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> count = <span class="function">(<span class="params">start, end</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> tmp = [];</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = start; i &lt;= end; i++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (num.some(<span class="function">(<span class="params">v</span>)  =&gt;</span> &#123; <span class="keyword">return</span> v === i &#125;) === <span class="literal">false</span>) &#123;</span><br><span class="line">      tmp.push(i)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> tmp;</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> range = <span class="function">(<span class="params">start, end, separate</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> result = [];</span><br><span class="line">  <span class="keyword">const</span> count = (end - start) / separate;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; separate; i++) &#123;</span><br><span class="line">    <span class="keyword">let</span> tmp = &#123;&#125;;</span><br><span class="line">    <span class="keyword">if</span> (i == <span class="number">0</span>) &#123;</span><br><span class="line">      tmp = &#123;</span><br><span class="line">        start,</span><br><span class="line">        end: count,</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      tmp = &#123;</span><br><span class="line">        start: result[i - <span class="number">1</span>].end + <span class="number">1</span>,</span><br><span class="line">        end: result[i - <span class="number">1</span>].end + count,</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    result.push(tmp);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> child_prosess = <span class="built_in">require</span>(<span class="string">'os'</span>).cpus().length;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> messageReceive = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> finichProcess = <span class="number">0</span>;</span><br><span class="line">    cluster.on(<span class="string">'message'</span>, (worker ,message, handle) =&gt; &#123;</span><br><span class="line">      finichProcess++;</span><br><span class="line">      num = num.concat(message)</span><br><span class="line">      <span class="keyword">if</span> (finichProcess === child_prosess) &#123;</span><br><span class="line">        resolve(num);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> mainFunction = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> ranges = range(start, end, child_prosess);</span><br><span class="line">  ranges[ranges.length - <span class="number">1</span>].end += (end % child_prosess);</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; child_prosess; i++) &#123;</span><br><span class="line">    <span class="keyword">let</span> worker = cluster.fork();</span><br><span class="line">    worker.on(<span class="string">'online'</span>, () =&gt; &#123;</span><br><span class="line">      worker.send(ranges[i]);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> promise = messageReceive();</span><br><span class="line">  promise.then(<span class="function">(<span class="params">num</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> result =  num.reduce(<span class="function">(<span class="params">prev, current, _, __</span>) =&gt;</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> prev + current;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="built_in">console</span>.log(result);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> childProcessFunc = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  process.on(<span class="string">'message'</span>, (data) =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> response = count(data.start, data.end);</span><br><span class="line">    process.send(response);</span><br><span class="line">    cluster.worker.disconnect();</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">(<span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">if</span> (cluster.isMaster) &#123;</span><br><span class="line">    mainFunction();</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    childProcessFunc();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h3 id="マルチスレッド"><a href="#マルチスレッド" class="headerlink" title="マルチスレッド"></a>マルチスレッド</h3><p>マルチプロセスを利用した場合は、プロセス生成時に起動時に大量のメモリが消費される。<br>そこで共有メモリを利用し、マルチプロセスより軽量なマルチスレッドを使うことになる。</p><p><a href="https://github.com/nodejs/node/blob/master/lib/worker_threads.js" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/master/lib/worker_threads.js</a></p><p><code>Worker</code>コンストラクタ。実行するファイルのパスが引数。</p><p><a href="https://github.com/nodejs/node/blob/ba4df925eb7143606d5a57f49e4ecb179dd7743b/lib/internal/worker.js#L51" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/ba4df925eb7143606d5a57f49e4ecb179dd7743b/lib/internal/worker.js#L51</a></p><p><code>isMainThread</code>でマインスレッドか判定。<br><code>parentPort.postMessage()</code>メインスレッドにメッセージング。<br><code>IPC</code>でのメッセージングではない。</p><p><a href="https://github.com/nodejs/node/blob/master/lib/internal/worker.js#L106" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/master/lib/internal/worker.js#L106</a></p><p>メッセージの処理は以下にまとまっている</p><p><a href="https://github.com/nodejs/node/blob/master/lib/internal/worker/io.js" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/master/lib/internal/worker/io.js</a><br><a href="https://github.com/nodejs/node/blob/8375c706ad51a399451e4f43b075f3795c440dad/src/node_messaging.cc#L802" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/8375c706ad51a399451e4f43b075f3795c440dad/src/node_messaging.cc#L802</a></p><p> HTML structured clone algorithmと互換性のある形式のものが転送される。<br> <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#Supported_types" target="_blank" rel="noopener">https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#Supported_types</a></p><p><code>workerData</code> JavaScriptの値で、Workerコンストラクタに渡されたデータのクローン。</p><p><a href="https://github.com/nodejs/node/blob/ba4df925eb7143606d5a57f49e4ecb179dd7743b/lib/internal/worker.js#L116" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/ba4df925eb7143606d5a57f49e4ecb179dd7743b/lib/internal/worker.js#L116</a></p><p>関連するN-APIは以下なのかな？</p><ul><li>napi_threadsafe_function<br><a href="https://github.com/nodejs/node/blob/11387e1454d8b8311dfdb1bac6a7ec1a1494946d/doc/api/n-api.md#napi_threadsafe_function" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/11387e1454d8b8311dfdb1bac6a7ec1a1494946d/doc/api/n-api.md#napi_threadsafe_function</a></li></ul><p>workser threadから非同期的にJavaScript関数を呼び出す。</p><ul><li>napi_threadsafe_function_release_mode<br><a href="https://github.com/nodejs/node/blob/11387e1454d8b8311dfdb1bac6a7ec1a1494946d/doc/api/n-api.md#napi_threadsafe_function_release_mode" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/11387e1454d8b8311dfdb1bac6a7ec1a1494946d/doc/api/n-api.md#napi_threadsafe_function_release_mode</a></li></ul><p>workser threadが即時終了状態か</p><ul><li>napi_threadsafe_function_call_mode<br><a href="https://github.com/nodejs/node/blob/11387e1454d8b8311dfdb1bac6a7ec1a1494946d/doc/api/n-api.md#napi_threadsafe_function_call_mode" target="_blank" rel="noopener">https://github.com/nodejs/node/blob/11387e1454d8b8311dfdb1bac6a7ec1a1494946d/doc/api/n-api.md#napi_threadsafe_function_call_mode</a></li></ul><p>スレッドセーフ関数のキューがいっぱいになった時にブロックするか</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span>;</span><br><span class="line"><span class="keyword">const</span> &#123;</span><br><span class="line">  Worker,</span><br><span class="line">  isMainThread,</span><br><span class="line">  parentPort,</span><br><span class="line">  workerData</span><br><span class="line">&#125; = <span class="built_in">require</span>(<span class="string">'worker_threads'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> num = [];</span><br><span class="line"><span class="keyword">let</span> start = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">let</span> end = <span class="number">50000</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> count = <span class="function"><span class="keyword">function</span>(<span class="params">start, end</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">let</span> i = start; i &lt;= end; i++) &#123;</span><br><span class="line">    <span class="keyword">if</span> (num.some(<span class="function"><span class="keyword">function</span>(<span class="params">v</span>) </span>&#123; <span class="keyword">return</span> v === i &#125;) === <span class="literal">false</span>) &#123;</span><br><span class="line">      num.push(i)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125; </span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> range = <span class="function">(<span class="params">start, end, separate</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> result = [];</span><br><span class="line">  <span class="keyword">const</span> count = (end - start) / separate;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; separate; i++) &#123;</span><br><span class="line">    <span class="keyword">let</span> tmp = &#123;&#125;;</span><br><span class="line">    <span class="keyword">if</span> (i == <span class="number">0</span>) &#123;</span><br><span class="line">      tmp = &#123;</span><br><span class="line">        start,</span><br><span class="line">        end: count,</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      tmp = &#123;</span><br><span class="line">        start: result[i - <span class="number">1</span>].end + <span class="number">1</span>,</span><br><span class="line">        end: result[i - <span class="number">1</span>].end + count,</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    result.push(tmp);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (isMainThread) &#123;</span><br><span class="line">  <span class="keyword">const</span> threadCount = +process.argv[<span class="number">2</span>] || <span class="number">2</span>,</span><br><span class="line">        ranges = range(start, end, threadCount),</span><br><span class="line">        threads = <span class="keyword">new</span> <span class="built_in">Set</span>();</span><br><span class="line"></span><br><span class="line">  ranges[ranges.length - <span class="number">1</span>].end += (end % threadCount);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i &lt; threadCount; i++) &#123;</span><br><span class="line">    threads.add(<span class="keyword">new</span> Worker(__filename, &#123; <span class="attr">workerData</span>: &#123; <span class="attr">start</span>: ranges[i].start, <span class="attr">end</span>: ranges[i].end &#125;&#125;));</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  threads.add(<span class="keyword">new</span> Worker(__filename, &#123; <span class="attr">workerData</span>: &#123; <span class="attr">start</span>: ranges[<span class="number">0</span>].start, <span class="attr">end</span>: ranges[<span class="number">0</span>].end &#125;&#125;));</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> worker <span class="keyword">of</span> threads) &#123;</span><br><span class="line">    worker.on(<span class="string">'error'</span>, (err) =&gt; &#123; <span class="keyword">throw</span> err; &#125;);</span><br><span class="line">    worker.on(<span class="string">'online'</span>, () =&gt; &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`Thread running start ID: <span class="subst">$&#123;worker.threadId&#125;</span> ...`</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">    worker.on(<span class="string">'exit'</span>, () =&gt; &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(<span class="string">`Total thread, <span class="subst">$&#123;threads.size&#125;</span> running ...`</span>);</span><br><span class="line">      threads.delete(worker);</span><br><span class="line">      <span class="keyword">if</span> (threads.size === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">const</span> result = num.reduce(<span class="function"><span class="keyword">function</span>(<span class="params">prev, current, _, __</span>) </span>&#123;</span><br><span class="line">          <span class="keyword">return</span> prev + current;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="built_in">console</span>.log(result)</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;)</span><br><span class="line">    worker.on(<span class="string">'message'</span>, (msg) =&gt; &#123;</span><br><span class="line">      num = num.concat(msg);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  count(workerData.start, workerData.end);</span><br><span class="line">  parentPort.postMessage(num);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="参考ページ"><a href="#参考ページ" class="headerlink" title="参考ページ"></a>参考ページ</h3><p><a href="https://nodejs.org/dist/latest-v10.x/docs/api/worker_threads.html" target="_blank" rel="noopener">Node.js v10.15.1 Documentation Worker Threads</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Node.jsにおけるいわゆる並行処理に関して再確認。&lt;br&gt;Node.jsは一般的にシングルスレッド、非同期IOとして認識されている。&lt;br&gt;これは言語としてマルチスレッドによる複雑性を排除した処理を選択した経緯があるらしい。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>React Hooks api を眺める</title>
    <link href="http://blog.kazu69.net/2018/12/01/try-react-hook-api/"/>
    <id>http://blog.kazu69.net/2018/12/01/try-react-hook-api/</id>
    <published>2018-12-01T13:43:12.000Z</published>
    <updated>2019-01-04T00:33:11.000Z</updated>
    
    <content type="html"><![CDATA[<p>React Conf 2018 にて <a href="https://reactjs.org/docs/hooks-reference.html" target="_blank" rel="noopener">Hooks API</a>が発表された。<br>ざっと触った時のメモ。</p><a id="more"></a><h3 id="React-Hooksとは"><a href="#React-Hooksとは" class="headerlink" title="React Hooksとは"></a>React Hooksとは</h3><p>Functionalコンポーネントからコンポーネントの状態やライフサイクル機能をフックさせるAPI。<br>後方互換があるため既存のアプリにも将来的に組み込むことが可能。<br>提供されているReact Hooks apiから独自のHooksを</p><h3 id="React-Hooksを試す"><a href="#React-Hooksを試す" class="headerlink" title="React Hooksを試す"></a>React Hooksを試す</h3><p>React Hooks apiを使って無理やりカウンターを作る。<br>さすがに全ては使うことができないので、以下の機能を試した。</p><ul><li>Basic Hooks<ul><li>useState</li><li>useEffect</li><li>useContext</li></ul></li><li>Additional Hooks<ul><li>useReducer</li><li>useCallback</li><li>useRef</li></ul></li></ul><p>ちなみにReact Hooksは現時点でstableではないので、プロダクションアプリで利用すべきではない。<br>またTypeScriptなどで利用する場合、必ずしも型情報が充分に提供されているわけではない。<br>React Hooks apiのリストからわかる通り、<code>useReducer</code> <code>useContext</code> あたりを使うことで、将来的には<strong>Redux</strong>は必要なくなる感じっぽい。<br>(Redux Devtoolsなどのコミュニティが作り上げたエコシステムがあるのですぐには無くならないかもしれない)</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> React <span class="keyword">from</span> <span class="string">"react"</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; render &#125; <span class="keyword">from</span> <span class="string">"react-dom"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> IncrementAction =  <span class="string">`increment`</span>;</span><br><span class="line"><span class="keyword">const</span> DecrementAction = <span class="string">`decrement`</span>;</span><br><span class="line"><span class="keyword">const</span> ResetAction = <span class="string">`reset`</span>;</span><br><span class="line"><span class="keyword">const</span> InitAction = <span class="string">`init`</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> CounterAction = &#123;</span><br><span class="line">  <span class="keyword">type</span>?: <span class="built_in">string</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> CountType = &#123;</span><br><span class="line">  count: <span class="built_in">number</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> initialState: CountType = &#123; count: <span class="number">0</span> &#125;;</span><br><span class="line"><span class="keyword">const</span> initAction = &#123; <span class="keyword">type</span>: InitAction &#125;;</span><br><span class="line"><span class="keyword">const</span> CounterState: CountType &amp; CounterAction = <span class="built_in">Object</span>.assign(&#123;&#125;, initialState, initAction);</span><br><span class="line"><span class="keyword">type</span> CounterStateType = <span class="keyword">typeof</span> CounterState;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">reducer</span>(<span class="params">state: CounterStateType, action: CounterAction</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">    <span class="keyword">case</span> ResetAction:</span><br><span class="line">      <span class="keyword">return</span> initialState;</span><br><span class="line">    <span class="keyword">case</span> IncrementAction:</span><br><span class="line">      <span class="keyword">return</span> &#123; count: state.count + <span class="number">1</span> &#125;;</span><br><span class="line">    <span class="keyword">case</span> DecrementAction:</span><br><span class="line">      <span class="keyword">return</span> &#123; count: state.count - <span class="number">1</span> &#125;;</span><br><span class="line">    <span class="keyword">case</span> InitAction:</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">      <span class="keyword">return</span> state;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Counter: React.FC&lt;CounterStateType&gt; = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// const [state, setState] = useState(initialState);</span></span><br><span class="line">  <span class="keyword">const</span> [state, dispatch] = React.useReducer(reducer, CounterState);</span><br><span class="line"></span><br><span class="line">  React.useEffect(</span><br><span class="line">    () =&gt; &#123;</span><br><span class="line">      <span class="built_in">document</span>.title = <span class="string">`Counter count is  <span class="subst">$&#123;state.count&#125;</span>`</span>;</span><br><span class="line">    &#125;,</span><br><span class="line">    [state.count]</span><br><span class="line">  );</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> incrementButton = React.useRef&lt;HTMLButtonElement&gt;(<span class="literal">null</span>);</span><br><span class="line">  <span class="keyword">const</span> onResetButtonClickHandler = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (incrementButton &amp;&amp; incrementButton.current) &#123;</span><br><span class="line">      incrementButton.current.focus();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> &#123; increment, decrement, reset &#125; = React.useContext(CounterContext);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> increaseClickHandler = React.useCallback(</span><br><span class="line">    () =&gt; dispatch(&#123; <span class="keyword">type</span>: <span class="string">"increment"</span> &#125;),</span><br><span class="line">    []</span><br><span class="line">  );</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> decreaseClickHandler = React.useCallback(</span><br><span class="line">    () =&gt; dispatch(&#123; <span class="keyword">type</span>: <span class="string">"decrement"</span> &#125;),</span><br><span class="line">    []</span><br><span class="line">  );</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> resetClickHandler = React.useCallback(</span><br><span class="line">    () =&gt; dispatch(&#123; <span class="keyword">type</span>: <span class="string">"reset"</span> &#125;),</span><br><span class="line">    []</span><br><span class="line">  );</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;h1&gt;Counter Example App&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">      &lt;p&gt;count: &#123;state.count&#125;&lt;/</span>p&gt;</span><br><span class="line">      &lt;button</span><br><span class="line">        onClick=&#123;<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">          resetClickHandler();</span><br><span class="line">          onResetButtonClickHandler();</span><br><span class="line">        &#125;&#125;</span><br><span class="line">      &gt;</span><br><span class="line">        &#123;reset&#125;</span><br><span class="line">      &lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">      &lt;button onClick=&#123;increaseClickHandler&#125; ref=&#123;incrementButton&#125;&gt;</span></span><br><span class="line"><span class="regexp">        &#123;increment&#125;</span></span><br><span class="line"><span class="regexp">      &lt;/</span>button&gt;</span><br><span class="line">      &lt;button onClick=&#123;decreaseClickHandler&#125;&gt;&#123;decrement&#125;&lt;<span class="regexp">/button&gt;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>div&gt;</span><br><span class="line">  );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> CounterContext = React.createContext(&#123;</span><br><span class="line">  increment: <span class="string">"increment (+)"</span>,</span><br><span class="line">  decrement: <span class="string">"decrement (-)"</span>,</span><br><span class="line">  reset: <span class="string">"reset (0)"</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> App = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;Counter count=&#123;<span class="number">1</span>&#125; /&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  );</span></span><br><span class="line"><span class="regexp">&#125;;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">render(&lt;App /</span>&gt;, <span class="built_in">document</span>.getElementById(<span class="string">"root"</span>));</span><br></pre></td></tr></table></figure><h3 id="その他"><a href="#その他" class="headerlink" title="その他"></a>その他</h3><p>React Hooks自体は単純なStack Queueで管理されているので、Root Componentレンダリングのタイミングで各コンポーネントのHooksが発火していく。</p><p><a href="https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e" target="_blank" rel="noopener">React hooks: not magic, just arrays</a>でも言及されているようにその順序を破壊しないように使う必要がある。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Don’t call Hooks inside loops, conditions, or nested functions.</span><br></pre></td></tr></table></figure><blockquote><p><a href="https://reactjs.org/docs/hooks-rules.html" target="_blank" rel="noopener">Rules of Hooks</a> より</p></blockquote><hr><p>React Hooksの導入でアプリのリファクタリングにかける時間的なコストは下げられそう。<br>またパフォーマンスの問題などこれまでより考慮することが減りそうなのはとてもよい。</p><p><code>Suspense</code>などからもReact Componentがあたかも生物のように自律的に機能することになっていきそうな感じだな。</p><h3 id="参考にした記事"><a href="#参考にした記事" class="headerlink" title="参考にした記事"></a>参考にした記事</h3><p><a href="https://reactjs.org/docs/hooks-reference.html#usestate" target="_blank" rel="noopener">Hooks API Reference</a><br><a href="https://medium.com/the-guild/under-the-hood-of-reacts-hooks-system-eb59638c9dba" target="_blank" rel="noopener">Under the hood of React’s hooks system</a><br><a href="https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e" target="_blank" rel="noopener">React hooks: not magic, just arrays</a></p><h3 id="今回作成したサンプル"><a href="#今回作成したサンプル" class="headerlink" title="今回作成したサンプル"></a>今回作成したサンプル</h3><p><a href="https://codesandbox.io/embed/vj15lzxx4l" target="_blank" rel="noopener">https://codesandbox.io/embed/vj15lzxx4l</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;React Conf 2018 にて &lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Hooks API&lt;/a&gt;が発表された。&lt;br&gt;ざっと触った時のメモ。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>React v16.6で追加されたReact.memoとReact.lazyを眺める</title>
    <link href="http://blog.kazu69.net/2018/10/28/react_melo_and_react_lazy_supported/"/>
    <id>http://blog.kazu69.net/2018/10/28/react_melo_and_react_lazy_supported/</id>
    <published>2018-10-28T09:33:28.000Z</published>
    <updated>2018-11-04T14:11:42.000Z</updated>
    
    <content type="html"><![CDATA[<p>React v16.6で<a href="https://reactjs.org/docs/react-api.html#reactmemo" target="_blank" rel="noopener">React.memo</a>と<a href="https://reactjs.org/docs/code-splitting.html#reactlazy" target="_blank" rel="noopener">React.lazy</a>というAPIが機能が追加されていた。</p><p>該当の変更は以下を参照</p><ul><li><a href="https://github.com/facebook/react/pull/13748" target="_blank" rel="noopener">React.memo()</a></li><li><a href="https://github.com/facebook/react/pull/13885" target="_blank" rel="noopener">React.lazy()</a></li></ul><a id="more"></a><h3 id="React-memo"><a href="#React-memo" class="headerlink" title="React.memo"></a>React.memo</h3><p>PureComponentはstateを<code>shallow Equal</code>によってupdateするのかを決める機能を持っているが、class Componentでしか利用できない。</p><p>React.memoは<code>function componen</code>をラップしてPureComponentと同じく、<code>shallow Equal</code>でコンポーネントをupdateする機能を提供するAPIである。<br><a href="https://github.com/facebook/react/pull/13748/files#diff-d8411e2e9d23bdea09f0c01d01cbf389R202" target="_blank" rel="noopener">このあたりがそれっぽい</a></p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> ReactDOM <span class="keyword">from</span> <span class="string">'react-dom'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Messages = &#123;</span><br><span class="line">  messages: <span class="built_in">string</span>[]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Message = &#123;</span><br><span class="line">  message: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> SFCComponent: React.SFC&lt;Message&gt; = <span class="function"><span class="params">props</span> =&gt;</span> &lt;li&gt;&#123;props.message&#125;&lt;<span class="regexp">/li&gt;</span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ defineTtyped not support `memo`</span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ https:/</span><span class="regexp">/github.com/</span>DefinitelyTyped/DefinitelyTyped/pull/<span class="number">29990</span></span><br><span class="line"><span class="keyword">const</span> MemoComponent = (React <span class="keyword">as</span> <span class="built_in">any</span>).memo(SFCComponent)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> InputHistoryWithMemo: React.SFC&lt;Messages&gt; = <span class="function">(<span class="params">props</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;ul&gt;</span><br><span class="line">        &#123;props.messages.map( <span class="function">(<span class="params">m, i</span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> (</span><br><span class="line">              &lt;React.Fragment&gt;</span><br><span class="line">                &lt;MemoComponent key=&#123;i&#125; message=&#123;m&#125; /&gt;</span><br><span class="line">              &lt;<span class="regexp">/React.Fragment&gt;</span></span><br><span class="line"><span class="regexp">            )</span></span><br><span class="line"><span class="regexp">        &#125;</span></span><br><span class="line"><span class="regexp">        )&#125;</span></span><br><span class="line"><span class="regexp">    &lt;/u</span>l&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> InputApp <span class="keyword">extends</span> React.Component&lt;&#123;&#125;, Messages&gt; &#123;</span><br><span class="line">  <span class="keyword">constructor</span>(<span class="params">props = &#123;&#125;</span>) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props)</span><br><span class="line">    <span class="keyword">this</span>.state = &#123; messages: [] &#125;</span><br><span class="line">    <span class="keyword">this</span>._getLastMessage = <span class="keyword">this</span>._getLastMessage.bind(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>._onMessageChange = <span class="keyword">this</span>._onMessageChange.bind(<span class="keyword">this</span>)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  _getLastMessage() &#123;</span><br><span class="line">    <span class="keyword">const</span> lastMessage = <span class="keyword">this</span>.state.messages[<span class="keyword">this</span>.state.messages.length - <span class="number">1</span>]</span><br><span class="line">    <span class="keyword">return</span> lastMessage === <span class="literal">undefined</span> ? <span class="string">""</span> : lastMessage</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  _onMessageChange(event: React.ChangeEvent&lt;HTMLInputElement&gt;) &#123;</span><br><span class="line">    <span class="keyword">const</span> messages = [...this.state.messages]</span><br><span class="line">    messages.push(event.target.value)</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123; messages &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> componetnStyle = &#123;</span><br><span class="line">      display: <span class="string">'inline-block'</span>,</span><br><span class="line">      marginLeft: <span class="string">'30px'</span>,</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> inputStyle = &#123;</span><br><span class="line">      display: <span class="string">'inline-block'</span>,</span><br><span class="line">      verticalAlign: <span class="string">'top'</span>,</span><br><span class="line">      marginLeft: <span class="string">'30px'</span>,</span><br><span class="line">      top: <span class="string">'0'</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;div&gt;</span><br><span class="line">        &lt;div style=&#123;inputStyle&#125; &gt;</span><br><span class="line">          &lt;input</span><br><span class="line">            <span class="keyword">type</span>=<span class="string">"text"</span></span><br><span class="line">            value=&#123;<span class="keyword">this</span>._getLastMessage()&#125;</span><br><span class="line">            onChange=&#123;<span class="keyword">this</span>._onMessageChange&#125;</span><br><span class="line">          /&gt;</span><br><span class="line">        &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">        &lt;div style=&#123;componetnStyle&#125;&gt;</span></span><br><span class="line"><span class="regexp">          &lt;strong&gt;Memo Compoennt&lt;/</span>strong&gt;</span><br><span class="line">          &lt;br /&gt;</span><br><span class="line">          &lt;InputHistoryWithMemo messages=&#123;<span class="keyword">this</span>.state.messages&#125; /&gt;</span><br><span class="line">        &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      &lt;/</span>div&gt;</span><br><span class="line">    );</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="React-lazy"><a href="#React-lazy" class="headerlink" title="React.lazy"></a>React.lazy</h3><p>任意のComponentの動的なimportを可能にするAPI。<br>(ただし現時点ではSSRでは利用できない)<br><code>Suspense</code>でfallbackを定義できる。<br>つまり読み込みが完了してない場合にloading spinnerなどのコンテンツを配置しておくなど。</p><p>ネットワークの問題などによりComponentがうまくimportできないときを考慮して、<br><a href="https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html" target="_blank" rel="noopener">ErrorBoundy</a>でFallbackしておくことで、ユーザーにエラー通知UIも提供することができる。</p><p>以下のような感じ</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">import</span> ReactDOM <span class="keyword">from</span> <span class="string">'react-dom'</span></span><br><span class="line"><span class="comment">// https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html</span></span><br><span class="line"><span class="keyword">import</span> ErrorboundyComponent <span class="keyword">from</span> <span class="string">'./ErrorboundyComponent'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// defineTyped not support `lazy`</span></span><br><span class="line"><span class="comment">// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/30110</span></span><br><span class="line"><span class="keyword">const</span> OtherComponent = <span class="function">(<span class="params">React <span class="keyword">as</span> <span class="built_in">any</span></span>).<span class="params">lazy</span>(<span class="params">(<span class="params"></span>) =&gt; <span class="keyword">import</span>(<span class="params">'./OtherComponent'</span>)</span>);</span></span><br><span class="line"><span class="function"><span class="params">const</span> <span class="params">AnotherComponent</span> = (<span class="params">React <span class="keyword">as</span> <span class="built_in">any</span></span>).<span class="params">lazy</span>(<span class="params">(<span class="params"></span>) =&gt; <span class="keyword">import</span>(<span class="params">'./AnotherComponent'</span>)</span>);</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function">// <span class="params">defineTyped</span> <span class="params">not</span> <span class="params">support</span> `<span class="params">Suspense</span>`</span></span><br><span class="line"><span class="function"><span class="params">const</span> <span class="params">Suspense</span> = (<span class="params">React <span class="keyword">as</span> <span class="built_in">any</span></span>).<span class="params">Suspense</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="params">const</span> <span class="params">LazyComponent</span>: <span class="params">React</span>.<span class="params">SFC</span> = _ =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        <span class="comment">// ErrorBoundy でエラーの追跡とFallback UIを提供</span></span><br><span class="line">        &lt;ErrorboundyComponent&gt;</span><br><span class="line">            &#123;<span class="comment">/* 複数のlazyコンポーネントを Suspense で束ねることができる */</span>&#125;</span><br><span class="line">            &lt;Suspense fallback=&#123;&lt;div&gt;Loading...&lt;<span class="regexp">/div&gt;&#125;&gt;</span></span><br><span class="line"><span class="regexp">                &lt;OtherComponent message="Hello" /</span>&gt;</span><br><span class="line">                &lt;AnotherComponent message=<span class="string">"Hola"</span> /&gt;</span><br><span class="line">            &lt;<span class="regexp">/Suspense&gt;</span></span><br><span class="line"><span class="regexp">        &lt;/</span>ErrorboundyComponent&gt;</span><br><span class="line">    )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="デモコード"><a href="#デモコード" class="headerlink" title="デモコード"></a>デモコード</h3><p><a href="https://github.com/kazu69/Scripts_Notes/tree/master/react/memo-lazy" target="_blank" rel="noopener">memo-lazy</a></p><p>React.memoに関しては、Component, PureCompoent, memoと比較できるようにして、<br><a href="https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi" target="_blank" rel="noopener">React Developer Tools</a>の<code>Highlight Update</code>を有効にするとComponentのupdateの具合を比較できます。</p><h3 id="参考ページ"><a href="#参考ページ" class="headerlink" title="参考ページ"></a>参考ページ</h3><p><a href="https://reactjs.org/blog/2018/10/23/react-v-16-6.html" target="_blank" rel="noopener">React v16.6.0: lazy, memo and contextType</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;React v16.6で&lt;a href=&quot;https://reactjs.org/docs/react-api.html#reactmemo&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React.memo&lt;/a&gt;と&lt;a href=&quot;https://reactjs.org/docs/code-splitting.html#reactlazy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React.lazy&lt;/a&gt;というAPIが機能が追加されていた。&lt;/p&gt;
&lt;p&gt;該当の変更は以下を参照&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react/pull/13748&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React.memo()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/facebook/react/pull/13885&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React.lazy()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>React HOC(Higher-order component)をTypeScriptで使う</title>
    <link href="http://blog.kazu69.net/2018/09/22/react-hoc-with-typescript/"/>
    <id>http://blog.kazu69.net/2018/09/22/react-hoc-with-typescript/</id>
    <published>2018-09-22T02:32:13.000Z</published>
    <updated>2018-10-04T15:53:38.000Z</updated>
    
    <content type="html"><![CDATA[<p>ReactのComponentのリファクタリングなどの際に、HOC(Higher-order component)で<br>コンポーネントを拡張することがあるが、TypeScriptでの実装をするにあたりお試しした備忘録。</p><a id="more"></a><h3 id="HOCのおさらい"><a href="#HOCのおさらい" class="headerlink" title="HOCのおさらい"></a>HOCのおさらい</h3><p>HOC(Higher-order component)はcomponentを引数にとり、新しいコンポーネントを返す関数であり、コンポーネント間のコードを再利用可能にする際に利用される手法。</p><p>ざっくり以下のような感じのこと。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> composedComponent = hightOrderComponent(Component)</span><br></pre></td></tr></table></figure><p>またはコンポーネントにpropsを渡すなら</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> composedComponent = hightOrderComponent(injectProps)(Component)</span><br></pre></td></tr></table></figure><p>という感じのもの。</p><p>以下の点が確保されている必要がある。</p><ul><li>純粋関数であり参照透過性を保持することで副作用を避ける。</li><li>HOC内でprototype拡張をしない。これはHOC利用側がHOCでのprototype変更の内部実装を知る必要が出てくる。</li><li>renderメソッドないでHOCを使用しない。render内でHOCを使うことで毎回新しいコンポーネントが生成されるため、パフォーマンスに影響するため。</li></ul><hr><h3 id="TypeScriptでの実装"><a href="#TypeScriptでの実装" class="headerlink" title="TypeScriptでの実装"></a>TypeScriptでの実装</h3><h4 id="Proxy-props"><a href="#Proxy-props" class="headerlink" title="Proxy props"></a>Proxy props</h4><p>一般的なものはpropsをproxyするパターン</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// HOCに渡すProps</span></span><br><span class="line"><span class="keyword">type</span> ExternalProps = &#123;</span><br><span class="line">  age: <span class="built_in">number</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">interface</span> InjectedProps &#123;</span><br><span class="line">  name: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Options = &#123;</span><br><span class="line">  name: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> propsProxyHOC = <span class="function">(<span class="params">&#123; name = 'James' &#125;: Partial&lt;Options&gt; = &#123;&#125;</span>) =&gt;</span> &lt;OriginalProps <span class="keyword">extends</span> &#123;&#125;&gt;(</span><br><span class="line">  WrappedComponent: React.ComponentType&lt;OriginalProps &amp; InjectedProps&gt;</span><br><span class="line">) =&gt; &#123;</span><br><span class="line">  <span class="keyword">class</span> PropsProxyHOC <span class="keyword">extends</span> React.Component&lt;OriginalProps &amp; ExternalProps&gt; &#123;</span><br><span class="line">    <span class="keyword">public</span> render() &#123;</span><br><span class="line">      <span class="keyword">const</span> InjectedProps = &#123;</span><br><span class="line">        name,</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> (</span><br><span class="line">        &lt;div&gt;</span><br><span class="line">          &lt;WrappedComponent &#123;...this.props&#125; &#123;...InjectedProps&#125; /&gt;</span><br><span class="line">          &#123;<span class="keyword">this</span>.props.age&#125;</span><br><span class="line">        &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      )</span></span><br><span class="line"><span class="regexp">    &#125;</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">  if (WrappedComponent.displayName) &#123;</span></span><br><span class="line"><span class="regexp">    PropsProxyHOC.displayName = `$&#123;</span></span><br><span class="line"><span class="regexp">      WrappedComponent.displayName</span></span><br><span class="line"><span class="regexp">    &#125;WrappedPropsProxyHOC`</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">  return PropsProxyHOC</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> propsProxyHOC = <span class="function">(<span class="params">&#123; name = 'James' &#125;: Partial&lt;Options&gt; = &#123;&#125;</span>) =&gt;</span> &lt;OriginalProps <span class="keyword">extends</span> &#123;&#125;&gt;</span><br></pre></td></tr></table></figure><p>default引数を指定する。引数オブジェクトのnameメンバが省略されている場合は、default値(James)が渡される。<br>引数自体がない場合は空のオブジェクトが渡るようになる。<br><code>Partial</code> keywordを使いOptionsのパラメーターをオプショナルにする。これにより引数オブジェクトのメンバを把握しておく必要性がなくなる。</p><p>つまりPartialにより引数の型は以下のようになる。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Options = &#123;</span><br><span class="line">  name?: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>render部分では</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> render() &#123;</span><br><span class="line">  <span class="keyword">const</span> InjectedProps = &#123;</span><br><span class="line">    name,</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">      &lt;WrappedComponent &#123;...this.props&#125; &#123;...InjectedProps&#125; /&gt;</span><br><span class="line">      &#123;<span class="keyword">this</span>.props.age&#125;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">  )</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure><p>propsを追加してcomponentを拡張する。<br>また、componet自体を別要素でwrapすることもできる。</p><p>以下のようにして利用する。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> composedComponent = propsProxyHOC(&#123; name: <span class="string">'John'</span> &#125;)(Component)</span><br><span class="line"></span><br><span class="line">React.render(</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;composedComponent age=&#123;<span class="number">10</span>&#125; /&gt;</span><br><span class="line">  )</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="Inheritance"><a href="#Inheritance" class="headerlink" title="Inheritance"></a>Inheritance</h3><p>またprops proxyするだけではなく、inheritanceすることも可能である。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> ExternalProps = &#123;</span><br><span class="line">  style?: React.CSSProperties</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">interface</span> InjectedProps &#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Options = &#123;</span><br><span class="line">  message: <span class="built_in">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> inheritanceInversionHOC = <span class="function">(<span class="params">&#123; message = '<span class="keyword">default</span> message' &#125;: Partial&lt;Options&gt; = &#123;&#125;</span>) =&gt;</span></span><br><span class="line">  &lt;OriginalProps <span class="keyword">extends</span> &#123;&#125;&gt;<span class="function">(<span class="params">WrappedComponent: React.ComponentType&lt;OriginalProps &amp; InjectedProps&gt;</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">class</span> WrapedComponentHOC <span class="keyword">extends</span> WrappedComponent&lt;OriginalProps &amp; ExternalProps&gt;  &#123;</span><br><span class="line">    componentWillMount() &#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">super</span>.componentWillMount) &#123;</span><br><span class="line">        <span class="keyword">super</span>.componentWillMount()</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    render() &#123;</span><br><span class="line">     <span class="keyword">const</span> injectProps = &#123;</span><br><span class="line">       message</span><br><span class="line">     &#125;</span><br><span class="line">     <span class="keyword">const</span> elementsTree = &lt;WrappedComponent &#123;...this.props&#125; &#123;...injectProps&#125; /&gt;</span><br><span class="line">     <span class="keyword">const</span> &#123; children, ...rest &#125; = elementsTree.props</span><br><span class="line"></span><br><span class="line">     <span class="keyword">return</span> (</span><br><span class="line">       &lt;div &#123;...rest&#125;&gt;</span><br><span class="line">         &lt;h1&gt;&#123;injectProps.message&#125;&lt;<span class="regexp">/h1&gt;</span></span><br><span class="line"><span class="regexp">         &#123;children&#125;</span></span><br><span class="line"><span class="regexp">       &lt;/</span>div&gt;</span><br><span class="line">     )</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (WrappedComponent.displayName) &#123;</span><br><span class="line">    WrapedComponentHOC.displayName = <span class="string">`<span class="subst">$&#123;</span></span></span><br><span class="line"><span class="string"><span class="subst">      WrappedComponent.displayName</span></span></span><br><span class="line"><span class="string"><span class="subst">      &#125;</span>WithWrapedComponentHOC`</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> WrapedComponentHOC</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>継承することでComponentのlifecycleにアクセスできるようになる。</p><p>ということTypescriptでのReact HOCのパターンを触れてみた。</p><h3 id="参考にしたページ"><a href="#参考にしたページ" class="headerlink" title="参考にしたページ"></a>参考にしたページ</h3><p><a href="https://postd.cc/react-higher-order-components-in-depth/" target="_blank" rel="noopener">ReactのHigher Order Components詳解 : 実装の2つのパターンと、親Componentとの比較</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;ReactのComponentのリファクタリングなどの際に、HOC(Higher-order component)で&lt;br&gt;コンポーネントを拡張することがあるが、TypeScriptでの実装をするにあたりお試しした備忘録。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>Typescript React Patternを再確認</title>
    <link href="http://blog.kazu69.net/2018/09/13/reaffirming-typescript-react-pattern/"/>
    <id>http://blog.kazu69.net/2018/09/13/reaffirming-typescript-react-pattern/</id>
    <published>2018-09-13T06:58:25.000Z</published>
    <updated>2018-09-23T12:38:29.000Z</updated>
    
    <content type="html"><![CDATA[<p>TypescriptでのReactアプリケーション開発において、<br>パターン的なものを一旦個人的な備忘録としてまとめておく。<br>登録・削除機能のみの簡単なアプリを作ることにする。</p><a id="more"></a><h2 id="Redux"><a href="#Redux" class="headerlink" title="Redux"></a>Redux</h2><p>Reactを使う場合の多くにおいて状態管理はReduxを使うことが主なので、<br>まずはその周りをまとめておく。</p><h3 id="Action-Creator-Action"><a href="#Action-Creator-Action" class="headerlink" title="Action Creator, Action"></a>Action Creator, Action</h3><p>ActionTypesはEnumを使う。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Before Typescript2.4</span></span><br><span class="line"><span class="keyword">const</span> ADD_TODO = <span class="string">'ADD_TODO'</span></span><br><span class="line"><span class="keyword">const</span> REMOVE_TODO = <span class="string">'REMOVE_TODO'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Current</span></span><br><span class="line"><span class="keyword">enum</span> ActionTypes &#123;</span><br><span class="line">  ADD_TODO = <span class="string">'ADD_TODO'</span>,</span><br><span class="line">  REMOVE_TODO = <span class="string">'REMOVE_TODO'</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html" target="_blank" rel="noopener">Typescript2.4からstring Enums</a>がサポートされてるので、ActionTypesをEnumのメンバーにまとめて定義しておくことで型安全を確保できそう。</p><p>Action Creatorは単にActionと呼ばれるObject形式のデータを返す関数であることなので</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Action = ReturnType&lt;<span class="keyword">typeof</span> ActionCreator&gt;</span><br><span class="line"><span class="keyword">const</span> ActionCreator = <span class="function">(<span class="params">data: unknown</span>) =&gt;</span> (</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">type</span>: <span class="string">'ACTION_TYPE`,</span></span><br><span class="line"><span class="string">    payload: &#123; ... &#125;</span></span><br><span class="line"><span class="string">  &#125;</span></span><br><span class="line"><span class="string">)</span></span><br></pre></td></tr></table></figure><p>という感じで<a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#conditional-types" target="_blank" rel="noopener">Typescript2.8</a>からサポートされた<code>Conditional Types</code>を使って表現できる。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> AwesomeAction = ReturnType&lt;T&gt;</span><br></pre></td></tr></table></figure><p><code>ReturnType</code>は型引数<t>が関数型の場合、その戻り値を表す型となるので、<br>Action Creatorを定義し、<code>typeof</code>演算子にて各アクションの型情報を生成する。<br>こうすることで型宣言は型推論を使って実際の実装から得ることができる。</t></p><p>今回は追加と削除のAction Creatorを作成する。<br>併せてActionの共用体のaliasを作成しReducerの処理周りで使う。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> AddTodoAction = ReturnType&lt;<span class="keyword">typeof</span> addTodoActionCreator&gt;</span><br><span class="line"><span class="keyword">const</span> addTodoActionCreator = <span class="function">(<span class="params">todo: <span class="built_in">string</span></span>) =&gt;</span> (&#123;</span><br><span class="line">  <span class="keyword">type</span>: ActionTypes.ADD_TODO,</span><br><span class="line">  payload: &#123;</span><br><span class="line">    todo</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> RemoveTodoAction = ReturnType&lt;<span class="keyword">typeof</span> removeTodoActionCreator&gt;</span><br><span class="line"><span class="keyword">const</span> removeTodoActionCreator = <span class="function">(<span class="params">id: <span class="built_in">number</span></span>) =&gt;</span> (&#123;</span><br><span class="line">  <span class="keyword">type</span>: ActionTypes.REMOVE_TODO,</span><br><span class="line">  payload: &#123;</span><br><span class="line">    id</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Union Types http://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types</span></span><br><span class="line"><span class="keyword">type</span> TodoAction = AddTodoAction | RemoveTodoAction</span><br></pre></td></tr></table></figure><p>余談だが抽象的な型表現だと今回のActionの型表現は以下のようになる</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> AppActionWithPayload &lt;T <span class="keyword">extends</span> string, P <span class="keyword">extends</span> &#123;&#125; = &#123;&#125;&gt; &#123;</span><br><span class="line">  <span class="keyword">type</span>: T</span><br><span class="line">  payload: &#123; [K <span class="keyword">in</span> keyof P]: P[K] &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Reducer"><a href="#Reducer" class="headerlink" title="Reducer"></a>Reducer</h3><p>Reducerも同じくデフォルトの状態の定義から型情報を取得する。<br>またreducerの返却値は<code>Object.assign</code>でもいいが、spread operator(スプレッド演算子)も利用できる。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> defaultState: &#123; [index:<span class="built_in">string</span>]: <span class="built_in">Array</span>&lt;<span class="built_in">string</span>&gt; &#125; = &#123; todos: [] &#125;</span><br><span class="line"><span class="keyword">type</span> TodoState = <span class="keyword">typeof</span> defaultState</span><br><span class="line"><span class="comment">// Reducer</span></span><br><span class="line"><span class="keyword">const</span> reducer = (state: TodoState = defaultState, action: TodoAction): <span class="function"><span class="params">TodoState</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> todos: <span class="built_in">string</span>[];</span><br><span class="line">  <span class="keyword">switch</span> (action.type) &#123;</span><br><span class="line">    <span class="keyword">case</span> ActionTypes.ADD_TODO:</span><br><span class="line">      todos = state.todos.concat(action.payload.todo)</span><br><span class="line">      <span class="keyword">return</span> &#123;...state, todos&#125;</span><br><span class="line">    <span class="keyword">case</span> ActionTypes.REMOVE_TODO:</span><br><span class="line">      todos = state.todos.filter(<span class="function">(<span class="params">_, i</span>) =&gt;</span> i !== action.payload.id )</span><br><span class="line">      <span class="keyword">return</span> &#123;...state, todos&#125;</span><br><span class="line">    <span class="keyword">default</span>:</span><br><span class="line">      <span class="keyword">return</span> &#123;...state&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>あとは<code>Store</code>を作ればいい</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Store = createStore(reducer)</span><br></pre></td></tr></table></figure><p>ここまででredux周りとしては終わり。<br>ReactのCompnentと連結していく</p><h2 id="React"><a href="#React" class="headerlink" title="React"></a>React</h2><p>作成するアプリはイメージとして以下のようなhtml構造を期待している</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"root"</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- input field component --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"todo"</span> <span class="attr">name</span>=<span class="string">"todo"</span> <span class="attr">placeholder</span>=<span class="string">"Please input todo"</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- add button component --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">button</span>&gt;</span>Add Todo<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- todo list component --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">ul</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- todo item component --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">li</span> <span class="attr">class</span>=<span class="string">"todo-item"</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"todo-content"</span>&gt;</span>todo<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- delete button component --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">button</span> <span class="attr">id</span>=<span class="string">"0"</span>&gt;</span>Delete Todo<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><h3 id="Container-Components"><a href="#Container-Components" class="headerlink" title="Container Components"></a>Container Components</h3><p>ContainerとなるCompoentクラスの定義</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> AppProps = &#123;</span><br><span class="line">  todos: <span class="built_in">string</span>[]</span><br><span class="line">  addTodo: <span class="function">(<span class="params">event: React.MouseEvent&lt;HTMLButtonElement&gt;</span>) =&gt;</span> <span class="built_in">void</span></span><br><span class="line">  removeTodo: <span class="function">(<span class="params">event: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> <span class="built_in">void</span></span><br><span class="line">  changeHandler: <span class="function">(<span class="params">event: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> <span class="built_in">void</span></span><br><span class="line">  setTextInputFieldRef: <span class="function">(<span class="params">element: HTMLInputElement</span>) =&gt;</span> <span class="built_in">void</span></span><br><span class="line">&#125; &amp; TodoActionCreators</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> initialState = &#123;</span><br><span class="line">  input: <span class="string">''</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// &lt;1&gt;</span></span><br><span class="line"><span class="keyword">type</span> AppState = Readonly&lt;<span class="keyword">typeof</span> initialState&gt;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> App <span class="keyword">extends</span> React.Component&lt;AppProps, AppState&gt; &#123;</span><br><span class="line">  <span class="comment">// &lt;2&gt;</span></span><br><span class="line">  readonly state = initialState</span><br><span class="line">  <span class="keyword">constructor</span>(<span class="params">props: AppProps</span>) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props)</span><br><span class="line">    <span class="keyword">this</span>.addTodo = <span class="keyword">this</span>.addTodo.bind(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.removeTodo = <span class="keyword">this</span>.removeTodo.bind(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.changeHandler = <span class="keyword">this</span>.changeHandler.bind(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.textInputFieldRef = React.createRef()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> changeHandler(event: React.ChangeEvent&lt;HTMLInputElement&gt;) &#123;</span><br><span class="line">    event.preventDefault()</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123;input: event.target.value&#125;)</span><br><span class="line">  &#125; </span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> addTodo(event: React.MouseEvent&lt;HTMLButtonElement&gt;) &#123;</span><br><span class="line">    event.preventDefault()</span><br><span class="line">    <span class="keyword">this</span>.props.add(<span class="keyword">this</span>.state.input)</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123;input: <span class="string">''</span>&#125;)</span><br><span class="line">    <span class="keyword">this</span>.textInputFieldRef.current!.value = <span class="string">''</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> removeTodo(event: React.MouseEvent&lt;HTMLButtonElement&gt;) &#123;</span><br><span class="line">    event.preventDefault()</span><br><span class="line">    <span class="keyword">const</span> element = event.target <span class="keyword">as</span> HTMLElement</span><br><span class="line">    <span class="keyword">this</span>.props.remove(<span class="built_in">parseInt</span>(element.id, <span class="number">10</span>))</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123;todos&#125; = <span class="keyword">this</span>.props</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;&gt;</span><br><span class="line">        &lt;&gt;</span><br><span class="line">          &lt;TodoInputField</span><br><span class="line">            handleChange=&#123;<span class="keyword">this</span>.changeHandler&#125;</span><br><span class="line">          /&gt;</span><br><span class="line">          &lt;AddTodoButton handleClick=&#123;<span class="keyword">this</span>.addTodo&#125; /&gt;</span><br><span class="line">        &lt;<span class="regexp">/&gt;</span></span><br><span class="line"><span class="regexp">        &lt;ul&gt;</span></span><br><span class="line"><span class="regexp">          &#123;todos.map((todo:string, index: number) =&gt; &#123;</span></span><br><span class="line"><span class="regexp">            return &lt;TodoItem key=&#123;index&#125;&gt;</span></span><br><span class="line"><span class="regexp">              &lt;TodoContent todoContent=&#123;todo&#125; /</span>&gt;</span><br><span class="line">              &lt;DeleteTodoButton </span><br><span class="line">                handleClick=&#123;<span class="keyword">this</span>.removeTodo&#125;</span><br><span class="line">                id=&#123;index.toString()&#125;</span><br><span class="line">              /&gt;</span><br><span class="line">            &lt;<span class="regexp">/TodoItem&gt;</span></span><br><span class="line"><span class="regexp">          &#125;)&#125;</span></span><br><span class="line"><span class="regexp">        &lt;/u</span>l&gt;</span><br><span class="line">      &lt;<span class="regexp">/&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure><p>Container Comppnentはstatefullなので<code>initialState</code>が必要になる。<br><code>&lt;1&gt;</code>のように実際の実装から<code>typeof</code> 演算子で実装から型情報を得る。</p><p>Reactではstateは<code>setState</code>でのみ変更する。<br>そのため、stateを直接変更できないよう<code>&lt;2&gt;</code>のようにstateを<code>readonly</code>修飾子でイミュータブルにしておく。併せて型情報もreadonlyにしている。</p><h3 id="Presentational-Component"><a href="#Presentational-Component" class="headerlink" title="Presentational Component"></a>Presentational Component</h3><p>PresentationのComponentはstatelessなことが多い。これは基本的に<code>Stateless Functional Component</code>を利用する。</p><p>input filed componentに関して</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> defaultTodoInputField = &#123;</span><br><span class="line">  id: <span class="string">'todo'</span>,</span><br><span class="line">  placeholder: <span class="string">'Please input todo'</span>,</span><br><span class="line">  name: <span class="string">'todo'</span>,</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">type</span> DefaultTodoInputField = <span class="keyword">typeof</span> defaultTodoInputField</span><br><span class="line"><span class="keyword">type</span> TodoInputFieldWithDefaltProps  = &#123;</span><br><span class="line">  handleChange(event: React.ChangeEvent&lt;HTMLInputElement&gt;): <span class="built_in">void</span></span><br><span class="line">  forwardRef: React.RefObject&lt;HTMLInputElement&gt;</span><br><span class="line">&#125; &amp; DefaultTodoInputField</span><br></pre></td></tr></table></figure><p><code>defaultTodoInputField</code>の型情報はTypeScriptの型推論に任せられる。<br>実際の実装からtypeof演算子にて型情報を得ることができる。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> TodoInputField: React.SFC&lt;TodoInputFieldWithDefaltProps&gt; = <span class="function">(<span class="params">props</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123;id, name, placeholder, handleChange, forwardRef&#125; = props</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;input</span><br><span class="line">      <span class="keyword">type</span>=<span class="string">"text"</span></span><br><span class="line">      id=&#123;id&#125;</span><br><span class="line">      name=&#123;name&#125;</span><br><span class="line">      placeholder=&#123;placeholder&#125;</span><br><span class="line">      onChange=&#123;handleChange&#125;</span><br><span class="line">      ref=&#123;forwardRef&#125;</span><br><span class="line">    /&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// デフォルト値の設定</span></span><br><span class="line">TodoInputField.defaultProps = defaultTodoInputField</span><br></pre></td></tr></table></figure><h4 id="defaultProps"><a href="#defaultProps" class="headerlink" title="defaultProps"></a>defaultProps</h4><p>TypeScript3.0からはJSX syntaxで<code>defaultProps</code>プロパティを正しく利用できるようになっているので、その恩恵を受けて<code>!</code>(Non-null assertion operator)を利用する必要はない。</p><p><a href="https://github.com/facebook/react/issues/3725" target="_blank" rel="noopener">Default props in ES6 class syntax</a></p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// これまでは`defaultProps`プロパティとjsxのレンダリングの関連性を</span></span><br><span class="line"><span class="comment">// サポートしてなかったので`!`でnot-nullをasserしていた</span></span><br><span class="line"><span class="keyword">type</span> Props &#123; name?: <span class="built_in">string</span> &#125;</span><br><span class="line"><span class="keyword">class</span> Greet <span class="keyword">extends</span> React.Component&lt;Props&gt; &#123;</span><br><span class="line">  <span class="keyword">static</span> defaultProps = &#123; name: <span class="string">"world"</span>&#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      <span class="keyword">const</span> &#123; name &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">return</span> &lt;div&gt;Hello $&#123;name!.toUpperCase()&#125;&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp">const Greet: React.SFC&lt;&#123; name = "world" &#125;: Props = (props) =&gt; (</span></span><br><span class="line"><span class="regexp">  &lt;div&gt;Hello $&#123;name!.toUpperCase()&#125;&lt;/</span>div&gt;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Typescript3.0からはJSX Syntaxで`defaultProps`を正しく解釈できるので</span></span><br><span class="line"><span class="comment">// !は必要なくなった</span></span><br><span class="line"><span class="keyword">class</span> Greet <span class="keyword">extends</span> React.Component&lt;Props&gt; &#123;</span><br><span class="line">  <span class="keyword">static</span> defaultProps = &#123; name: <span class="string">"world"</span>&#125;</span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      <span class="keyword">const</span> &#123; name &#125; = <span class="keyword">this</span>.props;</span><br><span class="line">      <span class="keyword">return</span> &lt;div&gt;Hello $&#123;name.toUpperCase()&#125;&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">const Greet: React.SFC&lt;&#123; name = "world" &#125;: Props = (props) =&gt; (</span></span><br><span class="line"><span class="regexp">  &lt;div&gt;Hello $&#123;name.toUpperCase()&#125;&lt;/</span>div&gt;</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="Refs-amp-Forwarding-Refs"><a href="#Refs-amp-Forwarding-Refs" class="headerlink" title="Refs &amp; Forwarding Refs"></a>Refs &amp; Forwarding Refs</h3><p>Componentのマウントのタイミングinputフォームにフォーカスしたり、<br>フォームの追加ボタン押下後にフォームの入力値を空にするために、<br>今回はRefを使って要素にアクセスようにしたい。</p><h4 id="Forwarding-Refs"><a href="#Forwarding-Refs" class="headerlink" title="Forwarding Refs"></a>Forwarding Refs</h4><p><a href="https://reactjs.org/docs/forwarding-refs.html" target="_blank" rel="noopener">Forwarding Refs</a>はReact v16.3.0で追加されたAPIでHOC(<a href="https://reactjs.org/docs/higher-order-components.html" target="_blank" rel="noopener">Higher-order components</a>)を使って、コンポーネント経由(propsを介して)refsを渡せる。<br>またrefの作成には同じバージョンで導入された<code>createRef</code> APIを利用する。</p><p>そのために <code>App Class</code> を修正する</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> App <span class="keyword">extends</span> React.Component&lt;AppProps, AppState&gt; &#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// &lt;Forwarding Refs&gt;</span></span><br><span class="line">  <span class="keyword">private</span> textInputFieldRef: React.RefObject&lt;HTMLInputElement&gt;</span><br><span class="line"></span><br><span class="line">  readonly state = initialState</span><br><span class="line"></span><br><span class="line">  <span class="keyword">constructor</span>(<span class="params">props: AppProps</span>) &#123;</span><br><span class="line">    <span class="keyword">super</span>(props)</span><br><span class="line">    <span class="keyword">this</span>.addTodo = <span class="keyword">this</span>.addTodo.bind(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.removeTodo = <span class="keyword">this</span>.removeTodo.bind(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.changeHandler = <span class="keyword">this</span>.changeHandler.bind(<span class="keyword">this</span>)</span><br><span class="line">    <span class="keyword">this</span>.textInputFieldRef = React.createRef()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  componentDidMount() &#123;</span><br><span class="line">    <span class="comment">// &lt;Forwarding Refsを参照&gt;</span></span><br><span class="line">    <span class="keyword">this</span>.textInputFieldRef.current!.focus()</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> changeHandler(event: React.ChangeEvent&lt;HTMLInputElement&gt;) &#123;</span><br><span class="line">    event.preventDefault()</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123;input: event.target.value&#125;)</span><br><span class="line">  &#125; </span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> addTodo(event: React.MouseEvent&lt;HTMLButtonElement&gt;) &#123;</span><br><span class="line">    event.preventDefault()</span><br><span class="line">    <span class="keyword">this</span>.props.add(<span class="keyword">this</span>.state.input)</span><br><span class="line">    <span class="keyword">this</span>.setState(&#123;input: <span class="string">''</span>&#125;)</span><br><span class="line">     <span class="comment">// &lt;Forwarding Refsを参照&gt;</span></span><br><span class="line">    <span class="keyword">this</span>.textInputFieldRef.current!.value = <span class="string">''</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> removeTodo(event: React.MouseEvent&lt;HTMLButtonElement&gt;) &#123;</span><br><span class="line">    event.preventDefault()</span><br><span class="line">    <span class="keyword">const</span> element = event.target <span class="keyword">as</span> HTMLElement</span><br><span class="line">    <span class="keyword">this</span>.props.remove(<span class="built_in">parseInt</span>(element.id, <span class="number">10</span>))</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">const</span> &#123;todos&#125; = <span class="keyword">this</span>.props</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;&gt;</span><br><span class="line">        &lt;&gt;</span><br><span class="line">          &lt;TodoInputFieldWithForwardedRef</span><br><span class="line">            handleChange=&#123;<span class="keyword">this</span>.changeHandler&#125;</span><br><span class="line">            forwardRef=&#123;<span class="keyword">this</span>.textInputFieldRef&#125;</span><br><span class="line">          /&gt;</span><br><span class="line">          &lt;AddTodoButton handleClick=&#123;<span class="keyword">this</span>.addTodo&#125; /&gt;</span><br><span class="line">        &lt;<span class="regexp">/&gt;</span></span><br><span class="line"><span class="regexp">        &lt;ul&gt;</span></span><br><span class="line"><span class="regexp">          &#123;todos.map((todo:string, index: number) =&gt; &#123;</span></span><br><span class="line"><span class="regexp">            return &lt;TodoItem key=&#123;index&#125;&gt;</span></span><br><span class="line"><span class="regexp">              &lt;TodoContent todoContent=&#123;todo&#125; /</span>&gt;</span><br><span class="line">              &lt;DeleteTodoButton </span><br><span class="line">                handleClick=&#123;<span class="keyword">this</span>.removeTodo&#125;</span><br><span class="line">                id=&#123;index.toString()&#125;</span><br><span class="line">              /&gt;</span><br><span class="line">            &lt;<span class="regexp">/TodoItem&gt;</span></span><br><span class="line"><span class="regexp">          &#125;)&#125;</span></span><br><span class="line"><span class="regexp">        &lt;/u</span>l&gt;</span><br><span class="line">      &lt;<span class="regexp">/&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure><p>さらにinput fieladのコンポーネントも<code>TodoInputFieldWithForwardedRef</code>を利用する。</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> TodoInputField: React.SFC&lt;TodoInputFieldWithDefaltProps&gt; = <span class="function">(<span class="params">props</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123;id, name, placeholder, handleChange, forwardRef&#125; = props</span><br><span class="line">  <span class="keyword">return</span> (</span><br><span class="line">    &lt;input</span><br><span class="line">      <span class="keyword">type</span>=<span class="string">"text"</span></span><br><span class="line">      id=&#123;id&#125;</span><br><span class="line">      name=&#123;name&#125;</span><br><span class="line">      placeholder=&#123;placeholder&#125;</span><br><span class="line">      onChange=&#123;handleChange&#125;</span><br><span class="line">      ref=&#123;forwardRef&#125;</span><br><span class="line">    /&gt;</span><br><span class="line">  )</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// デフォルト値の設定</span></span><br><span class="line">TodoInputField.defaultProps = defaultTodoInputField</span><br><span class="line"></span><br><span class="line"><span class="comment">// RefsをForwardされたcomponentを合成</span></span><br><span class="line"><span class="keyword">type</span> TodoInputFieldWithForwardedRefProps = &#123;</span><br><span class="line">  ref?: React.RefObject&lt;TodoInputField&gt;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Todo: detect react props attribute spread types</span></span><br><span class="line"><span class="keyword">const</span> TodoInputFieldWithForwardedRef: React.RefForwardingComponent&lt;TodoInputFieldWithForwardedRefProps&gt; = React.forwardRef(<span class="function">(<span class="params">props, ref</span>) =&gt;</span> (</span><br><span class="line">    &lt;TodoInputField &#123;...props&#125; ref=&#123;ref&#125; /&gt;</span><br><span class="line">  )</span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>ここまででパターンとして新たに見直した箇所は終わり。</p><h2 id="Types-or-Interface"><a href="#Types-or-Interface" class="headerlink" title="Types or Interface"></a>Types or Interface</h2><p>一般的にはtyped aliaseではなく、interfaceのほうが拡張性あるので、使うべきであるが、Reactのプロジェクトに限れば<br><code>外部に公開されるAPI</code>や<code>サードパーティーの型情報</code>の場合は<code>interface</code>を使う感じで良さそう。</p><p>逆にReactアプリケーションデータやcomponentのprppertyやstateなどはtype aliaseを利用する。</p><p><a href="https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c" target="_blank" rel="noopener">Interface vs Type alias in TypeScript 2.7</a>にある、<br>Componentの拡張はHOCなどのパターンで行うので、interfaceでの継承などはあまり必要ない。という感じの説明がしっくりきた。<br>また、type aliaseを利用することで、意図しない型情報のマージを避けることもできる点もあるかな。</p><h3 id="参考にしたサイト"><a href="#参考にしたサイト" class="headerlink" title="参考にしたサイト"></a>参考にしたサイト</h3><ul><li><a href="https://www.typescriptlang.org/docs/home.html" target="_blank" rel="noopener">TypeScript Documentation</a></li><li><a href="https://reactjs.org/blog/2018/03/29/react-v-16-3.html" target="_blank" rel="noopener">React Blog</a></li><li><a href="https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c" target="_blank" rel="noopener">Interface vs Type alias in TypeScript 2.7</a></li></ul><h3 id="code"><a href="#code" class="headerlink" title="code"></a>code</h3><p><a href="https://github.com/kazu69/Scripts_Notes/tree/master/react/typescript-react-pattern" target="_blank" rel="noopener">https://github.com/kazu69/Scripts_Notes/tree/master/react/typescript-react-pattern</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;TypescriptでのReactアプリケーション開発において、&lt;br&gt;パターン的なものを一旦個人的な備忘録としてまとめておく。&lt;br&gt;登録・削除機能のみの簡単なアプリを作ることにする。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>Reactの新しいContext APIを学ぶ</title>
    <link href="http://blog.kazu69.net/2018/07/22/learning-react-new-context-api/"/>
    <id>http://blog.kazu69.net/2018/07/22/learning-react-new-context-api/</id>
    <published>2018-07-22T14:05:23.000Z</published>
    <updated>2018-08-19T03:52:40.000Z</updated>
    
    <content type="html"><![CDATA[<p>React v16.3ではContext APIが刷新され、コンポーネント間のデータの受け渡しが<br>バケツリレー的な手法から直接データストアから状態を取得できるようになっている。<br>ということで、まず試してみる。</p><a id="more"></a><p>まずおさらいをする。</p><h2 id="従来のデータの受け渡し"><a href="#従来のデータの受け渡し" class="headerlink" title="従来のデータの受け渡し"></a>従来のデータの受け渡し</h2><p>Reactでは親コンポーネントから子コンポーネント、孫コンポーネントにデータを渡す際には、<br>バケツリレー(Reactドリル)的な方法で参照を渡していく必要があった。</p><p>簡単なネストコンポーネントを使っておさらいしてみる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React, &#123; Component &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Data Flow</span></span><br><span class="line"><span class="comment">// Animal Component --&gt; Homo Component --&gt; Sapiens Component</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Sapiens = <span class="function"><span class="params">props</span> =&gt;</span> (</span><br><span class="line">  &lt;div className=<span class="string">"sapiens"</span>&gt;&#123;props.name&#125;&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">const Homo = props =&gt; (</span></span><br><span class="line"><span class="regexp">  &lt;div className="homo"&gt;</span></span><br><span class="line"><span class="regexp">      &lt;Sapiens name=&#123;props.values.name&#125; /</span>&gt;</span><br><span class="line">  &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">class Animal extends Component &#123;</span></span><br><span class="line"><span class="regexp">  state = &#123;</span></span><br><span class="line"><span class="regexp">    values: &#123;</span></span><br><span class="line"><span class="regexp">      name: 'Lucy'</span></span><br><span class="line"><span class="regexp">    &#125;</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">    </span></span><br><span class="line"><span class="regexp">  render() &#123;</span></span><br><span class="line"><span class="regexp">    return (</span></span><br><span class="line"><span class="regexp">      &lt;div className="animal"&gt;</span></span><br><span class="line"><span class="regexp">        state: &#123;this.state.values.name&#125;</span></span><br><span class="line"><span class="regexp">        &lt;Homo values=&#123;this.state.values&#125; /</span>&gt;</span><br><span class="line">      &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    );</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">export default Animal;</span></span><br></pre></td></tr></table></figure><p>上記の例のようにコンポーネント間でprops経由でデータを渡す必要がある。<br>これらを解決する方法として、これまで<a href="https://redux.js.org/" target="_blank" rel="noopener">Redux</a>などのライブラリを使う必要があった。</p><h2 id="新しいContext-APIを使う"><a href="#新しいContext-APIを使う" class="headerlink" title="新しいContext APIを使う"></a>新しいContext APIを使う</h2><p>React New Contetx APIで状態を管理、参照するには、まずデータストアを定義する必要があります。<br>このデータはあたかもグローバルな変数のように扱えるため、子コンポーネント、孫コンポーネントと参照を渡す必要はない。</p><h3 id="createContext"><a href="#createContext" class="headerlink" title="createContext"></a>createContext</h3><p>まずcontextを作成する。これによりProviderとConsumerのペアを生成します。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123;Provider, Consumer&#125; = React.createContext(defaultValue)</span><br></pre></td></tr></table></figure><p>今回はカスタムオブジェクトからProviderとConsumerを使います。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> HomoSapinsesContext = React.createContext()</span><br></pre></td></tr></table></figure><h3 id="Providerの設定"><a href="#Providerの設定" class="headerlink" title="Providerの設定"></a>Providerの設定</h3><p>ProviderをラップするカスタムProviderを作成します。<br>ProviderはConsumerにデータを提供する機能。<br>valueプロパティとしてstateを渡している点がそれにあたる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomoSapinsesProvider</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  state = &#123;</span><br><span class="line">    values: &#123;</span><br><span class="line">      name: <span class="string">'Lucy'</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;HomoSapinsesContext.Provider</span><br><span class="line">        value=&#123;<span class="keyword">this</span>.state.values&#125;</span><br><span class="line">      &gt;</span><br><span class="line">        &#123;<span class="keyword">this</span>.props.children&#125;</span><br><span class="line">      &lt;<span class="regexp">/HomoSapinsesContext.Provider&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure><h3 id="Consumerでコンポーネントからデータを取得する"><a href="#Consumerでコンポーネントからデータを取得する" class="headerlink" title="Consumerでコンポーネントからデータを取得する"></a>Consumerでコンポーネントからデータを取得する</h3><p>Providerの配下に子コンポーネントしてConsumerを配置することで、<br>グローバルな感じでデータを受け取ることができる。<br>このデータが変更されるたびにConsumerはレンダリングされることになる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Consumerを経由して直接データを参照できている</span></span><br><span class="line"><span class="keyword">const</span> Sapiens = <span class="function"><span class="params">()</span> =&gt;</span> (</span><br><span class="line">  &lt;div className=<span class="string">"sapiens"</span>&gt;</span><br><span class="line">    &lt;HomoSapinsesContext.Consumer&gt;</span><br><span class="line">      &#123;(context) =&gt; context.name&#125;</span><br><span class="line">    &lt;<span class="regexp">/HomoSapinsesContext.Consumer&gt;</span></span><br><span class="line"><span class="regexp">  &lt;/</span>div&gt;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 下位のコンポーネントにデータを渡してない</span></span><br><span class="line"><span class="keyword">const</span> Homo = <span class="function"><span class="params">()</span> =&gt;</span> (</span><br><span class="line">  &lt;div className=<span class="string">"homo"</span>&gt;</span><br><span class="line">    &lt;Sapiens /&gt;</span><br><span class="line">  &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">class ContextAnimal extends Component &#123;</span></span><br><span class="line"><span class="regexp">  render() &#123;</span></span><br><span class="line"><span class="regexp">    return (</span></span><br><span class="line"><span class="regexp">      &lt;HomoSapinsesProvider&gt;</span></span><br><span class="line"><span class="regexp">        &lt;div&gt;</span></span><br><span class="line"><span class="regexp">          &lt;span&gt;state: </span></span><br><span class="line"><span class="regexp">            &lt;HomoSapinsesContext.Consumer&gt;</span></span><br><span class="line"><span class="regexp">              &#123;(context) =&gt; context.name&#125;</span></span><br><span class="line"><span class="regexp">            &lt;/</span>HomoSapinsesContext.Consumer&gt;</span><br><span class="line">          &lt;<span class="regexp">/span&gt;</span></span><br><span class="line"><span class="regexp">          &lt;Homo /</span>&gt;</span><br><span class="line">        &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      &lt;/</span>HomoSapinsesProvider&gt;</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>このように上位のコンポーネントからデータを受け取らないで直接データにアクセスできる。</p><p>ここまでのコードの全体は以下のとおり。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React, &#123; Component &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> HomoSapinsesContext = React.createContext()</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomoSapinsesProvider</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  state = &#123;</span><br><span class="line">    values: &#123;</span><br><span class="line">      name: <span class="string">'Lucy'</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render() &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;HomoSapinsesContext.Provider value=&#123;<span class="keyword">this</span>.state.values&#125;&gt;</span><br><span class="line">        &#123;<span class="keyword">this</span>.props.children&#125;</span><br><span class="line">      &lt;<span class="regexp">/HomoSapinsesContext.Provider&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">const Sapiens = () =&gt; (</span></span><br><span class="line"><span class="regexp">  &lt;div className="sapiens"&gt;</span></span><br><span class="line"><span class="regexp">    &lt;HomoSapinsesContext.Consumer&gt;</span></span><br><span class="line"><span class="regexp">      &#123;(context) =&gt; context.name&#125;</span></span><br><span class="line"><span class="regexp">    &lt;/</span>HomoSapinsesContext.Consumer&gt;</span><br><span class="line">  &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">const Homo = () =&gt; (</span></span><br><span class="line"><span class="regexp">  &lt;div className="homo"&gt;</span></span><br><span class="line"><span class="regexp">    &lt;Sapiens /</span>&gt;</span><br><span class="line">  &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">class ContextAnimal extends Component &#123;</span></span><br><span class="line"><span class="regexp">  render() &#123;</span></span><br><span class="line"><span class="regexp">    return (</span></span><br><span class="line"><span class="regexp">      &lt;HomoSapinsesProvider&gt;</span></span><br><span class="line"><span class="regexp">        &lt;div&gt;</span></span><br><span class="line"><span class="regexp">          &lt;span&gt;state: </span></span><br><span class="line"><span class="regexp">            &lt;HomoSapinsesContext.Consumer&gt;</span></span><br><span class="line"><span class="regexp">              &#123;(context) =&gt; context.name&#125;</span></span><br><span class="line"><span class="regexp">            &lt;/</span>HomoSapinsesContext.Consumer&gt;</span><br><span class="line">          &lt;<span class="regexp">/span&gt;</span></span><br><span class="line"><span class="regexp">          &lt;Homo /</span>&gt;</span><br><span class="line">        &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">      &lt;/</span>HomoSapinsesProvider&gt;</span><br><span class="line">    )</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> ContextAnimal;</span><br></pre></td></tr></table></figure><h3 id="状態を変更する"><a href="#状態を変更する" class="headerlink" title="状態を変更する"></a>状態を変更する</h3><p>状態の変更にはsetStateで直接状態を変更する。<br>この辺りのやり方は定まってないため、開発者に依存しそう。</p><p>Reduxのように状態変更のためのReducerを作成し、それを利用してみることにします。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> reducer = <span class="function">(<span class="params">prevState, action</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (action.type === <span class="string">'UPDATE'</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> values = prevState.values</span><br><span class="line">    values.name = action.name</span><br><span class="line">    <span class="keyword">return</span> &#123; ...prevState, values &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>コンポーネントにはdispatchメソッドをもたせて、<br>Reducerを経由して状態を変更するようにします。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomoSapinsesProvider</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>&#123;</span><br><span class="line">  state = &#123;</span><br><span class="line">    values: &#123;</span><br><span class="line">      name: <span class="string">'Lucy'</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  dispatch(action)</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">this</span>.setState(<span class="function"><span class="params">state</span> =&gt;</span> reducer(state, action))</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  render()</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">      &lt;HomoSapinsesContext.Provider</span><br><span class="line">        value=&#123;</span><br><span class="line">          &#123;</span><br><span class="line">            name: <span class="keyword">this</span>.state.values.name,</span><br><span class="line">            dispatch: <span class="keyword">this</span>.dispatch.bind(<span class="keyword">this</span>)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;&gt;</span><br><span class="line">        &#123;<span class="keyword">this</span>.props.children&#125;</span><br><span class="line">      &lt;<span class="regexp">/HomoSapinsesContext.Provider&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">  &#125;</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure><p>Provider経由でメソッドを渡すことでConsumerから利用できる。</p><h2 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h2><p>新しいContext APIは複雑性がなく学習コストが低いため利用しやすいと感じ。<br>単純なコンポーネントやアプリケーションと比較的結合性の低いコンポーネントなどでは<br>そのままつかうことでデータの透明性を確保できるtのでいいかもしれない。<br>公式のドキュメントにもあるとおり、新しいContext APIを使うことでより容易になるパターンもある。<br>Redux的なアーキテクチャを利用しないと状態管理が複雑になりそうで、実施あの運用はハードルが高そう。<br>なので、ある程度の規模のアプリケーションや複数人での開発においてはReduxなどに乗っておくほうがいいきがする。<br>とはいえ、この変更は革新的なので今後は使い分けが出てくるのかなという気がした。</p><p><a href="https://github.com/kazu69/Scripts_Notes/tree/master/react/new-context-api" target="_blank" rel="noopener">今回使ったサンプル</a></p><h2 id="参考ページ"><a href="#参考ページ" class="headerlink" title="参考ページ"></a>参考ページ</h2><ul><li><a href="https://github.com/reactjs/rfcs/pull/2" target="_blank" rel="noopener">New version of context</a></li><li><a href="https://reactjs.org/docs/context.html" target="_blank" rel="noopener">Context</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;React v16.3ではContext APIが刷新され、コンポーネント間のデータの受け渡しが&lt;br&gt;バケツリレー的な手法から直接データストアから状態を取得できるようになっている。&lt;br&gt;ということで、まず試してみる。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>npm@6で追加されたnpm auditでpackageのセキュリティ情報をチェックする</title>
    <link href="http://blog.kazu69.net/2018/05/14/package_security_aware_with_npm_audit_added_npm_v6/"/>
    <id>http://blog.kazu69.net/2018/05/14/package_security_aware_with_npm_audit_added_npm_v6/</id>
    <published>2018-05-14T11:25:21.000Z</published>
    <updated>2018-05-14T22:49:50.000Z</updated>
    
    <content type="html"><![CDATA[<p>npm@6で新たに<a href="https://github.com/npm/npm/pull/20389" target="_blank" rel="noopener">npm audit</a>が追加されていた。<br>これは<a href="https://liftsecurity.io" target="_blank" rel="noopener">liftsecurity.io</a>という企業が持っていたセキュリティノウハウをが<a href="https://blog.npmjs.org/post/172793182214/npm-acquires-lift-security-and-node-security" target="_blank" rel="noopener">npm incが取得した</a>ことにより実現されたとのこと。</p><p>ということで自分のプロダクトでいかほどかおためしして見てみる。</p><a id="more"></a><p>ほとんど公式ドキュメントのななめ良いなんですが、ざっとまとめる。<br>npm installを実行することで自動的に実行され、セキュリティのレポートが出力される。<br>(//registry.npmjs.org/-/npm/v1/security/audits/quick あたりのAPIかな)</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># upgrade npm@6</span></span><br><span class="line">npm i -g npm@latest</span><br><span class="line"></span><br><span class="line">npm -v</span><br><span class="line">6.0.1</span><br><span class="line"></span><br><span class="line">npm i</span><br><span class="line">up to date <span class="keyword">in</span> 6.424s</span><br><span class="line">[!] 26 vulnerabilities found [2529 packages audited]</span><br><span class="line">    Severity: 16 Low | 8 Moderate | 2 High</span><br><span class="line">    Run `npm audit` <span class="keyword">for</span> more detail</span><br></pre></td></tr></table></figure><p>npm auditはインストールした<code>node_module</code>に対してセキュリティチェックを行いレポートを出力する。</p><p>詳細なレポートは<code>npm audit</code>を実施することで確認できます。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line">npm audit</span><br><span class="line"></span><br><span class="line"><span class="comment"># Run  npm install --dev nyc@11.7.3  to resolve 11 vulnerabilities</span></span><br><span class="line">┌───────────────┬──────────────────────────────────────────────────────────────┐</span><br><span class="line">│ Low           │ Prototype Pollution                                          │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Package       │ lodash                                                       │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Dependency of │ nyc [dev]                                                    │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Path          │ nyc &gt; istanbul-lib-instrument &gt; babel-generator &gt;            │</span><br><span class="line">│               │ babel-types &gt; lodash                                         │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ More info     │ https://nodesecurity.io/advisories/577                       │</span><br><span class="line">└───────────────┴──────────────────────────────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="comment"># Run  npm update fsevents --depth 3  to resolve 11 vulnerabilities</span></span><br><span class="line">┌───────────────┬──────────────────────────────────────────────────────────────┐</span><br><span class="line">│ Low           │ Regular Expression Denial of Service                         │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Package       │ debug                                                        │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Dependency of │ ava [dev]                                                    │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Path          │ ava &gt; chokidar &gt; fsevents &gt; node-pre-gyp &gt; tar-pack &gt; debug  │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ More info     │ https://nodesecurity.io/advisories/534                       │</span><br><span class="line">└───────────────┴──────────────────────────────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">┌───────────────┬──────────────────────────────────────────────────────────────┐</span><br><span class="line">│ Moderate      │ Prototype pollution                                          │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Package       │ hoek                                                         │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Dependency of │ ava [dev]                                                    │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Path          │ ava &gt; chokidar &gt; fsevents &gt; node-pre-gyp &gt; hawk &gt; boom &gt;     │</span><br><span class="line">│               │ hoek                                                         │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ More info     │ https://nodesecurity.io/advisories/566                       │</span><br><span class="line">└───────────────┴──────────────────────────────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">┌──────────────────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                                Manual Review                                 │</span><br><span class="line">│            Some vulnerabilities require your attention to resolve            │</span><br><span class="line">│                                                                              │</span><br><span class="line">│         Visit https://go.npm.me/audit-guide <span class="keyword">for</span> additional guidance          │</span><br><span class="line">└──────────────────────────────────────────────────────────────────────────────┘</span><br><span class="line">┌───────────────┬──────────────────────────────────────────────────────────────┐</span><br><span class="line">│ Low           │ Prototype Pollution                                          │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Package       │ deep-extend                                                  │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Patched <span class="keyword">in</span>    │ &gt;=0.5.1                                                      │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Dependency of │ b801b0cdd03e5b6e6c073421517282056f9d04c8220d55dfb4ca26be1e3… │</span><br><span class="line">│               │ [dev]                                                        │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Path          │ b801b0cdd03e5b6e6c073421517282056f9d04c8220d55dfb4ca26be1e3… │</span><br><span class="line">│               │ &gt; chokidar &gt; fsevents &gt; node-pre-gyp &gt; rc &gt; deep-extend      │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ More info     │ https://nodesecurity.io/advisories/612                       │</span><br><span class="line">└───────────────┴──────────────────────────────────────────────────────────────┘</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">[!] 26 vulnerabilities found - Packages audited: 2529 (2491 dev, 289 optional)</span><br><span class="line">    Severity: 16 Low | 8 Moderate | 2 High</span><br></pre></td></tr></table></figure><p>という感じで、<br>npm auditでは脆弱性の深刻度とパッケージ、path情報などがレポートされる。</p><h3 id="解決方法が提示されている場合"><a href="#解決方法が提示されている場合" class="headerlink" title="解決方法が提示されている場合"></a>解決方法が提示されている場合</h3><p>パッケージでの解決が可能な場合は解決方法が提示される。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Run  npm update fsevents --depth 3  to resolve 11 vulnerabilities</span></span><br><span class="line">┌───────────────┬──────────────────────────────────────────────────────────────┐</span><br><span class="line">│ Low           │ Regular Expression Denial of Service                         │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Package       │ debug                                                        │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Dependency of │ ava [dev]                                                    │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Path          │ ava &gt; chokidar &gt; fsevents &gt; node-pre-gyp &gt; tar-pack &gt; debug  │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ More info     │ https://nodesecurity.io/advisories/534                       │</span><br><span class="line">└───────────────┴──────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><p>この場合は <code>npm update fsevents --depth 3</code> を実施する。</p><h3 id="自動解決方法が提供されない場合"><a href="#自動解決方法が提供されない場合" class="headerlink" title="自動解決方法が提供されない場合"></a>自動解決方法が提供されない場合</h3><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">┌──────────────────────────────────────────────────────────────────────────────┐</span><br><span class="line">│                                Manual Review                                 │</span><br><span class="line">│            Some vulnerabilities require your attention to resolve            │</span><br><span class="line">│                                                                              │</span><br><span class="line">│         Visit https://go.npm.me/audit-guide <span class="keyword">for</span> additional guidance          │</span><br><span class="line">└──────────────────────────────────────────────────────────────────────────────┘</span><br><span class="line">┌───────────────┬──────────────────────────────────────────────────────────────┐</span><br><span class="line">│ Low           │ Prototype Pollution                                          │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Package       │ deep-extend                                                  │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Patched <span class="keyword">in</span>    │ &gt;=0.5.1                                                      │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Dependency of │ b801b0cdd03e5b6e6c073421517282056f9d04c8220d55dfb4ca26be1e3… │</span><br><span class="line">│               │ [dev]                                                        │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ Path          │ b801b0cdd03e5b6e6c073421517282056f9d04c8220d55dfb4ca26be1e3… │</span><br><span class="line">│               │ &gt; chokidar &gt; fsevents &gt; node-pre-gyp &gt; rc &gt; deep-extend      │</span><br><span class="line">├───────────────┼──────────────────────────────────────────────────────────────┤</span><br><span class="line">│ More info     │ https://nodesecurity.io/advisories/612                       │</span><br><span class="line">└───────────────┴──────────────────────────────────────────────────────────────┘</span><br></pre></td></tr></table></figure><p>自動で解決できない場合は、マニュアルで確認、調査することが可能。<br><a href="https://nodesecurity.io/" target="_blank" rel="noopener">nodesecurity.io</a>のリンクが提供されており、<br>CVE(Common Vulnerabilities and Exposures)情報があるので脆弱性の緩和・解決を図る。<br>該当するpackageのパスがあるので、必要に応じてパッケージの更新や修正を行う。</p><p>例に挙げているレポートでは<a href="https://nodesecurity.io/advisories/612" target="_blank" rel="noopener">Prototype Pollution</a> として<code>deep-extend@0.5.1</code> 以前のバージョンがプロトタイプ汚染に関して脆弱なためv0.5.1以上にアップデートする必要があるので、該当のパッケージ更新を行う。</p><p>またバージョンの更新ができなかったり、修正が存在してない場合は以下のいずれかの方法でた右往する。</p><ul><li>脆弱性に該当する箇所を改修し、mainteinerにプルリクエストを送り脆弱性を報告する</li><li>脆弱性を修正できない場合は該当パッケージの<code>issue tracker</code>から脆弱性の情報を報告する</li></ul><h3 id="インストール時にauditを実施ない"><a href="#インストール時にauditを実施ない" class="headerlink" title="インストール時にauditを実施ない"></a>インストール時にauditを実施ない</h3><p>またインストール時にauditを実施しないときは<code>--no-audit</code>フラグを渡してあげる。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i &lt;PACKAGE&gt; --no-audit</span><br></pre></td></tr></table></figure><p>または.npmrcで<code>audit</code>を<code>false</code>にしておくことで、すべてのインストールに対してauditは無効化できる。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm <span class="built_in">set</span> audit <span class="literal">false</span></span><br></pre></td></tr></table></figure><p>ということで、自分のpackageに対しても結構でてきたので、<br>セキュリティの意識を高めて開発していきたい。</p><h4 id="参考にしたページ"><a href="#参考にしたページ" class="headerlink" title="参考にしたページ"></a>参考にしたページ</h4><ul><li><a href="https://blog.npmjs.org/post/173719309445/npm-audit-identify-and-fix-insecure" target="_blank" rel="noopener"><code>npm audit</code>: identify and fix insecure dependencies</a></li><li><a href="https://docs.npmjs.com/getting-started/running-a-security-audit#check-for-mitigating-factors" target="_blank" rel="noopener">About security audits</a></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;npm@6で新たに&lt;a href=&quot;https://github.com/npm/npm/pull/20389&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;npm audit&lt;/a&gt;が追加されていた。&lt;br&gt;これは&lt;a href=&quot;https://liftsecurity.io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;liftsecurity.io&lt;/a&gt;という企業が持っていたセキュリティノウハウをが&lt;a href=&quot;https://blog.npmjs.org/post/172793182214/npm-acquires-lift-security-and-node-security&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;npm incが取得した&lt;/a&gt;ことにより実現されたとのこと。&lt;/p&gt;
&lt;p&gt;ということで自分のプロダクトでいかほどかおためしして見てみる。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>CSS APIを操作する (CSS Houdini)</title>
    <link href="http://blog.kazu69.net/2018/03/23/houdini-css-paint-api/"/>
    <id>http://blog.kazu69.net/2018/03/23/houdini-css-paint-api/</id>
    <published>2018-03-22T15:04:00.000Z</published>
    <updated>2018-04-01T09:52:33.000Z</updated>
    
    <content type="html"><![CDATA[<p>ここ最近のChromeのアップデートで<a href="https://github.com/w3c/css-houdini-drafts" target="_blank" rel="noopener">Houdini</a>プロジェクトで進められている機能が試すことが可能となったということでざっと眺めてみた。</p><a id="more"></a><p>HoudiniではブラウザのCSS APIを開発者に解放することを目的としている。<br>最終的にはW3Cでの標準化を目指しているプジジェクト。<br>APIを使うことで開発者はブラウザベンダーと同じく、自由にブラウザのCSSレンダリングの低レベル部分を操作できるようになり、自由な表現が可能となる。</p><h3 id="Houdiniの仕様"><a href="#Houdiniの仕様" class="headerlink" title="Houdiniの仕様"></a>Houdiniの仕様</h3><p>ドラフトによると以下のAPIで構成されている。</p><ul><li>Box Tree API 1</li><li>CSS Layout API 1</li><li>CSS Painting API 1</li><li>CSS Parser API 1</li><li>CSS Properties and Values API 1</li><li>CSS Typed OM 1</li><li>Font Metrics API 1</li><li>Worklets 1</li></ul><p>API経由で開発者の書いたコードがブラウザのCSSエンジンのフックされ、カスタムCSSを作成し実行することができる。<br>カスタムCSS機能は<strong>Worklet</strong>と呼ばれ、JavaScriptで定義されている。<br>Workletはブラウザ実行時にJavaScriptとしてブラウザにロードされる。<br>ユーザーはあたかもブラウザ組み込まれたスタイルのようにスタムCSSを利用できる。</p><p>WorkletはWebworkerに似ているが、次の部分が違う。</p><ul><li>メインスレッドの依存しないため特定のスレッドで実行するというようなことはできない</li><li>並列化のためにグローバルに複数の重複するインスタンスを生成できる</li><li>イベントAPIではなく、UserAgentに呼び出される</li></ul><h3 id="ブラウザのサポート状況-2018年3月現在"><a href="#ブラウザのサポート状況-2018年3月現在" class="headerlink" title="ブラウザのサポート状況 (2018年3月現在)"></a>ブラウザのサポート状況 (2018年3月現在)</h3><p>各ブラザベンダーの実装状況は<a href="https://ishoudinireadyyet.com/" target="_blank" rel="noopener">Is Houdini ready yet‽</a>で知ることができる。</p><h3 id="Worklet"><a href="#Worklet" class="headerlink" title="Worklet"></a>Worklet</h3><p>Houdiniに関連するWorkletは次のものがある。</p><table><thead><tr><th>name</th><th>content</th></tr></thead><tbody><tr><td><a href="https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet" target="_blank" rel="noopener">PaintWorklet</a></td><td>CSSのカスタムプロパティのレンダリングを定義</td></tr><tr><td><a href="https://wicg.github.io/animation-worklet/" target="_blank" rel="noopener">AnimationWorklet</a></td><td>カスタムのアニメーション、スクロールの定義</td></tr><tr><td><a href="https://drafts.css-houdini.org/css-layout-api-1/#layout-worklet" target="_blank" rel="noopener">LayoutWorklet</a></td><td>カスタムのレイアウトの定義</td></tr></tbody></table><p>指定されたURIにあるworkletを取得ための<strong>addModule</strong>メソッドが提供されている。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;credentials&gt; =  &apos;omit&apos; | &apos;same-origin&apos; | &apos;include&apos;</span><br><span class="line">&lt;option&gt; = &apos;credentials&apos;</span><br><span class="line"></span><br><span class="line">const option &#123;credentials:  &apos;omit&apos;&#125;</span><br><span class="line">Promise = Worklet.addModule(workletURI,  option);</span><br></pre></td></tr></table></figure><p>それぞれに関してかいつまんで見る。<br>機能は <strong>chrome：// flags</strong> で<strong>Experimental Web Platform</strong>の機能を有効にすることが必要になる。</p><h3 id="PaintWorklet"><a href="#PaintWorklet" class="headerlink" title="PaintWorklet"></a>PaintWorklet</h3><p>任意のboxに対して描画を行うAPI。CanvasのレンダリングAPIのサブセットを使い描画行う。</p><h3 id="AnimationWorklet"><a href="#AnimationWorklet" class="headerlink" title="AnimationWorklet"></a>AnimationWorklet</h3><p>以前は<strong>CompositorWorker</strong>として提案されていたもので、<br>worklet内でカスタムのアニメーションの実行を行う。<br>こちらはアニメーションのサブクラスを使いアニメーションを行い、複数のタイムラインを持つことができる。</p><h3 id="LayoutWorklet"><a href="#LayoutWorklet" class="headerlink" title="LayoutWorklet"></a>LayoutWorklet</h3><p>レイアウトやボックスモデルのサイズ、positionなどを開発者で計算ができるようになる。</p><h3 id="CSS-Typed-OM"><a href="#CSS-Typed-OM" class="headerlink" title="CSS Typed OM"></a>CSS Typed OM</h3><p>workletではないが、JavaScriptからCSS単位付きの値(px, %など)演算を行う際に<br>オーバーヘッドが発生するため、CSSの値を型付きでAPIとして公開し開発者に提供するもの。</p><p>例えばこのようなな感じで扱っていた値とユニットを</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> elem = docuemnt.queryselector(<span class="string">'#element'</span>)</span><br><span class="line"><span class="keyword">let</span> w = elem.style.width.slice(<span class="number">0</span>,<span class="number">-2</span>)</span><br><span class="line">w++</span><br><span class="line">elem.style.width = <span class="string">`<span class="subst">$&#123;w&#125;</span>px`</span>;</span><br></pre></td></tr></table></figure><p>CSSStyleValueをもちいることで以下のように扱うことができる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> elem = docuemnt.queryselector(<span class="string">'#element'</span>)</span><br><span class="line"><span class="keyword">let</span> w = element.attributeStyleMap.get(<span class="string">'width'</span>);</span><br><span class="line">w.value++;</span><br><span class="line">element.attributeStyleMap.set(<span class="string">'width'</span>, w);</span><br></pre></td></tr></table></figure><p>またユニットの違う場合の計算においても、cssのcalc関数のように取得できる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// like calc(1em + 5px)</span></span><br><span class="line"><span class="keyword">const</span> width = <span class="keyword">new</span> CSSMathSum(CSS.em(<span class="number">1</span>), CSS.px(<span class="number">5</span>));</span><br></pre></td></tr></table></figure><h3 id="Paint-APIを使ったデモ"><a href="#Paint-APIを使ったデモ" class="headerlink" title="Paint APIを使ったデモ"></a>Paint APIを使ったデモ</h3><p>ボーダースタイルの動的変更</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"ja"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Houdini<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="css">        <span class="selector-class">.paint-button</span> &#123;</span></span><br><span class="line"><span class="undefined">            display: flex;</span></span><br><span class="line"><span class="undefined">            margin: 2rem auto;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">font-size</span>: 1<span class="selector-class">.5rem</span>;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">padding</span>: <span class="selector-class">.8em</span>;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">border-radius</span>: <span class="selector-class">.25rem</span>;</span></span><br><span class="line"><span class="undefined">            border-style: solid;</span></span><br><span class="line"><span class="undefined">            border-width: 3px;</span></span><br><span class="line"><span class="undefined">            border-image-slice: 10;</span></span><br><span class="line"><span class="css">            <span class="comment">/* paint api */</span></span></span><br><span class="line"><span class="undefined">            border-image-source: paint(paint-button, orange, 10);</span></span><br><span class="line"><span class="undefined">        &#125;</span></span><br><span class="line"><span class="undefined">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span>      </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"paint-button"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">class</span>=<span class="string">"paint-button"</span>&gt;</span>button<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="javascript">        <span class="keyword">if</span> (</span></span><br><span class="line"><span class="javascript">            <span class="string">'paintWorklet'</span> <span class="keyword">in</span> CSS &amp;&amp; <span class="keyword">typeof</span> CSS.paintWorklet.addModule === <span class="string">'function'</span></span></span><br><span class="line"><span class="undefined">        ) &#123;</span></span><br><span class="line"><span class="javascript">            CSS.paintWorklet.addModule(<span class="string">'paint-button.js'</span>);</span></span><br><span class="line"><span class="javascript">        &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">console</span>.log(<span class="string">'Please enabled CSS property'</span>);</span></span><br><span class="line"><span class="undefined">        &#125;</span></span><br><span class="line"><span class="undefined">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// paint-button.js</span></span><br><span class="line">registerPaint(<span class="string">'paint-button'</span>, <span class="class"><span class="keyword">class</span> </span>&#123;</span><br><span class="line">    <span class="comment">// css paint apiに渡されるプロパティ</span></span><br><span class="line">    <span class="comment">// CSS Properties の syntax strings の type</span></span><br><span class="line">    <span class="keyword">static</span> get inputArguments() &#123;</span><br><span class="line">        <span class="keyword">return</span> [</span><br><span class="line">            <span class="string">'&lt;color&gt;'</span>,</span><br><span class="line">            <span class="string">'&lt;number&gt;'</span></span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    paint (context, geometory, properties, args) &#123;</span><br><span class="line">      <span class="keyword">const</span> color = args[<span class="number">0</span>].value;</span><br><span class="line">      <span class="keyword">const</span> lineWidth = args[<span class="number">1</span>].value</span><br><span class="line">      context.lineWidth = lineWidth;</span><br><span class="line">      context.strokeStyle = color;</span><br><span class="line">      context.strokeRect(<span class="number">0</span>, <span class="number">0</span>, geometory.width, geometory.height);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p><strong>paint(paint-button, orange, 10);</strong> でモジュール名と引数を渡す。<br>workletのcallbackはJavaScriptのclassとなり、引数は<strong>inputArguments</strong>で列挙される。<br>ここではタイプを指定して取得できる。指定できるタイプは<a href="https://www.w3.org/TR/css-properties-values-api-1/#supported-syntax-strings" target="_blank" rel="noopener">css-properties-valueのsupported-syntax-strings</a>にあるものが指定可能。<br><strong>pain()</strong>メソッドでカスタムの描画を行うことができる。</p><hr><p>Ripple Button デモ</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE html&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Houdini<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="css">        <span class="selector-class">.ripple-button</span> &#123;</span></span><br><span class="line"><span class="undefined">            --circle-radius: 0;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">--circle-color</span>: <span class="selector-id">#fff</span>;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">--bgcolor</span>: <span class="selector-id">#6eb9f7</span>;</span></span><br><span class="line"><span class="undefined">            background: var(--bgcolor);</span></span><br><span class="line"><span class="undefined">            margin: 2rem auto;</span></span><br><span class="line"><span class="undefined">            display: flex;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">color</span>: <span class="selector-id">#1b2e3d</span>;</span></span><br><span class="line"><span class="undefined">            cursor: pointer;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">border</span>: 1<span class="selector-tag">px</span> <span class="selector-tag">solid</span> <span class="selector-id">#61a5dd</span>;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">font-size</span>: 1<span class="selector-class">.5rem</span>;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">padding</span>: <span class="selector-class">.8em</span>;</span></span><br><span class="line"><span class="css">            <span class="selector-tag">border-radius</span>: <span class="selector-class">.25rem</span>;</span></span><br><span class="line"><span class="undefined">            background-image: paint(circle-ripple);</span></span><br><span class="line"><span class="undefined">            outline: none;</span></span><br><span class="line"><span class="undefined">        &#125;</span></span><br><span class="line"><span class="undefined">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span>      </span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"ripple"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">button</span> <span class="attr">class</span>=<span class="string">"ripple-button"</span>&gt;</span>button<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="javascript">        <span class="keyword">if</span> (</span></span><br><span class="line"><span class="javascript">            (<span class="string">'paintWorklet'</span> <span class="keyword">in</span> CSS &amp;&amp; <span class="keyword">typeof</span> CSS.paintWorklet.addModule === <span class="string">'function'</span>) &amp;&amp;</span></span><br><span class="line"><span class="javascript">            (<span class="string">'registerProperty'</span> <span class="keyword">in</span> CSS &amp;&amp; <span class="keyword">typeof</span> CSS.registerProperty === <span class="string">'function'</span>)</span></span><br><span class="line"><span class="undefined">        ) &#123;</span></span><br><span class="line"><span class="javascript">            CSS.paintWorklet.addModule(<span class="string">'ripple.js'</span>);</span></span><br><span class="line"><span class="javascript">            <span class="comment">// &#123;</span></span></span><br><span class="line"><span class="javascript">            <span class="comment">//     name: 変数名,</span></span></span><br><span class="line"><span class="javascript">            <span class="comment">//     syntax: 型,</span></span></span><br><span class="line"><span class="javascript">            <span class="comment">//     inherits: 継承,</span></span></span><br><span class="line"><span class="javascript">            <span class="comment">//     initialValue: 初期値</span></span></span><br><span class="line"><span class="javascript">            <span class="comment">// &#125;</span></span></span><br><span class="line"><span class="javascript">            <span class="keyword">const</span> customProps = [</span></span><br><span class="line"><span class="javascript">                &#123;<span class="attr">name</span>: <span class="string">'--ripple-x'</span>, <span class="attr">syntax</span>: <span class="string">'&lt;number&gt;'</span>, <span class="attr">initialValue</span>: <span class="literal">false</span>,  <span class="attr">initialValue</span>: <span class="string">'0'</span>&#125;,</span></span><br><span class="line"><span class="javascript">                &#123;<span class="attr">name</span>: <span class="string">'--ripple-y'</span>, <span class="attr">syntax</span>: <span class="string">'&lt;number&gt;'</span>, <span class="attr">initialValue</span>: <span class="literal">false</span>,  <span class="attr">initialValue</span>: <span class="string">'0'</span>&#125;,</span></span><br><span class="line"><span class="javascript">                &#123;<span class="attr">name</span>: <span class="string">'--ripple-radius'</span>, <span class="attr">syntax</span>: <span class="string">'&lt;number&gt;'</span>, <span class="attr">initialValue</span>: <span class="literal">false</span>,  <span class="attr">initialValue</span>: <span class="string">'0'</span>&#125;,</span></span><br><span class="line"><span class="javascript">                &#123;<span class="attr">name</span>: <span class="string">'--ripple-color'</span>, <span class="attr">syntax</span>: <span class="string">'&lt;color&gt;'</span>, <span class="attr">initialValue</span>: <span class="literal">false</span>,  <span class="attr">initialValue</span>: <span class="string">'#6eb9f7'</span>&#125;</span></span><br><span class="line"><span class="undefined">            ];</span></span><br><span class="line"><span class="undefined">            </span></span><br><span class="line"><span class="javascript">            <span class="comment">// カスタムプロパティを適用</span></span></span><br><span class="line"><span class="javascript">            customProps.forEach(<span class="function"><span class="params">prop</span> =&gt;</span> &#123;</span></span><br><span class="line"><span class="undefined">                CSS.registerProperty(prop)</span></span><br><span class="line"><span class="undefined">            &#125;);</span></span><br><span class="line"><span class="undefined">    </span></span><br><span class="line"><span class="javascript">            <span class="keyword">const</span> button = ripple.querySelector(<span class="string">'.ripple-button'</span>);</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript">            button.addEventListener(<span class="string">'click'</span>, event =&gt; &#123;</span></span><br><span class="line"><span class="javascript">                button.classList.add(<span class="string">'animating'</span>);</span></span><br><span class="line"><span class="javascript">                button.style.setProperty(<span class="string">'--ripple-x'</span>, event.offsetX);</span></span><br><span class="line"><span class="javascript">                button.style.setProperty(<span class="string">'--ripple-y'</span>, event.offsetY);</span></span><br><span class="line"><span class="undefined">            &#125;);</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript">            <span class="comment">// transitoon終了後</span></span></span><br><span class="line"><span class="javascript">            button.addEventListener(<span class="string">'transitionend'</span>, () =&gt; &#123;</span></span><br><span class="line"><span class="javascript">                button.classList.remove(<span class="string">'animating'</span>);</span></span><br><span class="line"><span class="undefined">            &#125;);</span></span><br><span class="line"><span class="javascript">        &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line"><span class="javascript">            <span class="built_in">console</span>.log(<span class="string">'Please enabled CSS property'</span>);</span></span><br><span class="line"><span class="undefined">        &#125;</span></span><br><span class="line"><span class="undefined">    </span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ripple.js</span></span><br><span class="line">registerPaint(<span class="string">'circle-ripple'</span>, <span class="class"><span class="keyword">class</span> </span>&#123;</span><br><span class="line">  <span class="comment">// アクセスできるプロパティの列挙</span></span><br><span class="line">  <span class="keyword">static</span> get inputProperties() &#123;</span><br><span class="line">    <span class="keyword">return</span> [</span><br><span class="line">      <span class="string">'--ripple-x'</span>,</span><br><span class="line">      <span class="string">'--ripple-y'</span>,</span><br><span class="line">      <span class="string">'--ripple-radius'</span>,</span><br><span class="line">      <span class="string">'--ripple-color'</span></span><br><span class="line">    ];</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">constructor</span>() &#123;</span><br><span class="line">    <span class="keyword">this</span>.prop = &#123;</span><br><span class="line">      name: <span class="string">'circle-riple'</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  paint(context, geometory, properties, args) &#123;</span><br><span class="line">    <span class="keyword">const</span> x = properties.get(<span class="string">'--ripple-x'</span>).toString();</span><br><span class="line">    <span class="keyword">const</span> y = properties.get(<span class="string">'--ripple-y'</span>).toString();</span><br><span class="line">    <span class="keyword">const</span> radius = properties.get(<span class="string">'--ripple-radius'</span>).toString();</span><br><span class="line">    <span class="keyword">const</span> color = properties.get(<span class="string">'--ripple-color'</span>).toString();</span><br><span class="line">    context.fillStyle = color;</span><br><span class="line">    context.arc(</span><br><span class="line">      x,</span><br><span class="line">      y,</span><br><span class="line">      radius,</span><br><span class="line">      <span class="number">0</span>,</span><br><span class="line">      <span class="number">2</span> * <span class="built_in">Math</span>.PI</span><br><span class="line">    );</span><br><span class="line">    context.fill();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>イベントのcallbackと組み合わせることで、tansition効果をつけたエフェクトも可能。<br>cssのカスタム変数へのアクセスは<strong>inputProperties</strong>で列挙できる。</p><h3 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h3><ul><li>ブラウザベンダーが提供してないビジュアルやエフェクトを独自で実装可能となる</li><li>複雑なDOMやCSSプロパティを使用することなく高度なビジュアルやエフェクトを実装可能</li><li>ブラウザベンダーより先に実装するを実現することができる</li><li>ビジュアルを抽象化してモジュールとして提供することができる</li></ul><p>ということで、これまでのCSSでのリッチUIの提供方法に変化がある未来がすぐそこまで来ている。<br>Webcomponentsと同じようにビジュアルのモジュール化できるので、これまでのCSS開発手法が変わってきそう。</p><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><p><a href="https://www.w3.org/TR/css-properties-values-api-1/" target="_blank" rel="noopener">css-properties-values-api-1</a><br><a href="https://github.com/w3c/css-houdini-drafts" target="_blank" rel="noopener">ss-houdini-drafts</a><br><a href="https://developers.google.com/web/updates/2016/05/houdini" target="_blank" rel="noopener">Houdini – CSS の秘密を解き明かすもの</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;ここ最近のChromeのアップデートで&lt;a href=&quot;https://github.com/w3c/css-houdini-drafts&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Houdini&lt;/a&gt;プロジェクトで進められている機能が試すことが可能となったということでざっと眺めてみた。&lt;/p&gt;
    
    </summary>
    
      <category term="web" scheme="http://blog.kazu69.net/categories/web/"/>
    
    
  </entry>
  
  <entry>
    <title>golangの関数のオプション引数を実現する</title>
    <link href="http://blog.kazu69.net/2018/02/22/golang-functional-options/"/>
    <id>http://blog.kazu69.net/2018/02/22/golang-functional-options/</id>
    <published>2018-02-22T10:53:35.000Z</published>
    <updated>2018-04-04T16:39:48.000Z</updated>
    
    <content type="html"><![CDATA[<p>Gopherなら既にご存知なのだろうけど。<br>Golangでインスタンスを作成する際に可変長引数を受け付けつつ、拡張を考慮した設計をしたかった。<br>デフォルト引数を使えればいいけど、Golangにデフォルト引数はないので、色々と探して<strong>Functional Option Pattern</strong>ということを<br>知ったのでメモ。</p><a id="more"></a><p>簡単な例を示してみてみようと思う。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> client</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Configs <span class="keyword">struct</span> &#123;</span><br><span class="line">    Port      <span class="keyword">int</span></span><br><span class="line">    Tiemout   time.Duration</span><br><span class="line">    UserAgent <span class="keyword">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Client <span class="keyword">interface</span> &#123;</span><br><span class="line">    Do(req *http.Request) (res *http.Response, err error)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>のような場合、コンストラクタは普通</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">fucn NewClient(port, timeout, ua) *Configs &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;Configs&#123;</span><br><span class="line">        Port:      port,</span><br><span class="line">        Tiemout:   timeout,</span><br><span class="line">        UserAgent: ua,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>とするの一般的。しかし毎回パラメーターを渡す必要がでてくる。<br>引数なしでインスタンス生成できにない。</p><p>そこでコンストラクタを分けるということも考えられる。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">fucn NewClient() *Configs &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;Config&#123;</span><br><span class="line">        Port:      default_port,</span><br><span class="line">        Tiemout:   default_timeout,</span><br><span class="line">        UserAgent: default_ua,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">fucn NewClientWithOption(port, timeout, ua) *Config s &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;Configs&#123;</span><br><span class="line">        Port:      port,</span><br><span class="line">        Tiemout:   timeout,</span><br><span class="line">        UserAgent: ua,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewClientWithTPort</span><span class="params">(port)</span> *<span class="title">Configs</span></span> &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;Configs&#123;</span><br><span class="line">        Port:      port,</span><br><span class="line">        Tiemout:   default_timeout,</span><br><span class="line">        UserAgent: default_ua,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewClientWithTimenout</span><span class="params">(timeout)</span> *<span class="title">Configs</span></span> &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;Configs&#123;</span><br><span class="line">        Port:      default_port,</span><br><span class="line">        Tiemout:   timeout,</span><br><span class="line">        UserAgent: default_ua,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewClientWithTPort</span><span class="params">(ua)</span> *<span class="title">Configs</span></span> &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;Configs&#123;</span><br><span class="line">        Port:      default_port,</span><br><span class="line">        Tiemout:   default_timeout,</span><br><span class="line">        UserAgent: ua,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>オプションが増えることで煩雑になる。<br>他にはあらかじめ構造体を作って渡すという手法。<br>こちらは明示的で拡張もしやすい。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> clientConfigs <span class="keyword">struct</span> &#123;</span><br><span class="line">    Port      <span class="keyword">int</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewClient</span><span class="params">(configs clientConfigs)</span> *<span class="title">Configs</span></span> &#123;</span><br><span class="line">    <span class="keyword">return</span> &amp;Config&#123;</span><br><span class="line">        Port:      configs.port,</span><br><span class="line">        Tiemout:   configs.timeout,</span><br><span class="line">        UserAgent: default_ua,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// optionが不要な場合</span></span><br><span class="line">conig := client.NewClient(&#123;&#125;)</span><br></pre></td></tr></table></figure><p>一般的なやり方としてはこうなりそう。しかし、オプションを渡す必要な場合でも<br>構造体を渡す必要がある。</p><p>そこで<strong>Functional Option Pattern</strong>を試す。</p><figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Configs <span class="keyword">struct</span> &#123;</span><br><span class="line">    Port      <span class="keyword">int</span></span><br><span class="line">    Tiemout   time.Duration</span><br><span class="line">    UserAgent <span class="keyword">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Option <span class="function"><span class="keyword">func</span><span class="params">(*Configs)</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="title">func</span> <span class="title">Port</span><span class="params">(port <span class="keyword">int</span>)</span> <span class="title">CoOptionnfig</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(args *Configs)</span></span> &#123;</span><br><span class="line">        args.Port = port</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Timeout</span><span class="params">(time time.Duration)</span> <span class="title">Option</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(args *Configs)</span></span> &#123;</span><br><span class="line">        args.Tiemout = time</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">UserAgent</span><span class="params">(ua <span class="keyword">string</span>)</span> <span class="title">Option</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">func</span><span class="params">(args *Configs)</span></span> &#123;</span><br><span class="line">        args.UserAgent = ua</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">New</span><span class="params">(options ...Option)</span> *<span class="title">Configs</span></span> &#123;</span><br><span class="line">    args := &amp;Configs &#123;</span><br><span class="line">        Port:      default_port,</span><br><span class="line">        Tiemout:   default_timeout,</span><br><span class="line">        UserAgent: default_ua,</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> _, option := <span class="keyword">range</span> options &#123;</span><br><span class="line">        option(args)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Usage</span></span><br><span class="line">conig := client.NewClient(client.Port(port), client.UserAgent(ua))</span><br></pre></td></tr></table></figure><p>オプションを設定する関数を作成し、コンストラクタに可変長引数として渡す。<br>オプション設定関数はいわゆるクロージャで、設定値を受け取り、オプションを設定するだけになる。<br>これで、可変長な引数で拡張性のあるオプション設計ができるようになりました。<br>勉強になりました。</p><h3 id="参考ページ"><a href="#参考ページ" class="headerlink" title="参考ページ"></a>参考ページ</h3><p><a href="https://commandcenter.blogspot.jp/2014/01/self-referential-functions-and-design.html" target="_blank" rel="noopener">Self-referential functions and the design of options </a><br><a href="https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis" target="_blank" rel="noopener">Functional options for friendly APIs</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Gopherなら既にご存知なのだろうけど。&lt;br&gt;Golangでインスタンスを作成する際に可変長引数を受け付けつつ、拡張を考慮した設計をしたかった。&lt;br&gt;デフォルト引数を使えればいいけど、Golangにデフォルト引数はないので、色々と探して&lt;strong&gt;Functional Option Pattern&lt;/strong&gt;ということを&lt;br&gt;知ったのでメモ。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>ProxyとReflectを使ってオブジェクトを拡張する</title>
    <link href="http://blog.kazu69.net/2017/12/24/extend-object-using-proxy/"/>
    <id>http://blog.kazu69.net/2017/12/24/extend-object-using-proxy/</id>
    <published>2017-12-24T10:24:30.000Z</published>
    <updated>2018-04-01T04:59:19.000Z</updated>
    
    <content type="html"><![CDATA[<p>Proxyオブジェクトがサポートされたことで、<br>JavaScriptにでもオブジェクト操作が柔軟に拡張できるようになった。</p><p>これにより処理を動的に拡張するメタプログラミングできる。<br>ということで、<a href="https://github.com/gergob/jsProxy" target="_blank" rel="noopener">参考になりそうな事例</a>があったので試してみた。</p><a id="more"></a><p><a href="https://caniuse.com/#search=proxy" target="_blank" rel="noopener">caniuseでProxyのサポート</a>を見るとモダンブラウザでは実装済である。<br>まず、Proxyオブジェクトをおさらいする。</p><h3 id="Proxyオブジェクト"><a href="#Proxyオブジェクト" class="headerlink" title="Proxyオブジェクト"></a>Proxyオブジェクト</h3><p>基本構文は次の通り</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// target</span></span><br><span class="line"><span class="keyword">const</span> target = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// handler</span></span><br><span class="line"><span class="keyword">const</span> handler = &#123;</span><br><span class="line">    <span class="comment">// trap</span></span><br><span class="line">    get(target, property) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, property);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br></pre></td></tr></table></figure><p>targetはProxyで拡張するオブジェクト。<br>handlerはtrapを含んだオブジェクトとなる。<br>trapとはオブジェクトのプロパティへのアクセスを提供するメソッド。<br>上記の構文例だとgetとなる。<br>getの中ででてくるReflectオブジェクトはトラップされるビルトインメソッド(処理)を提供する。</p><p>ちなみにtrapが設定されてない場合は、デフォルトのメソッドが実行される。</p><h3 id="handlerとtrap一覧"><a href="#handlerとtrap一覧" class="headerlink" title="handlerとtrap一覧"></a>handlerとtrap一覧</h3><p>ブラウザへの実装が未対応のものもあるが、handlerとtrapとの関係は以下のようになっている。</p><table><thead><tr><th>handler / trap</th><th>proxyされるビルトインメソッド</th></tr></thead><tbody><tr><td>handler.getPrototypeOf()</td><td>Object.getPrototypeOf()</td></tr><tr><td>handler.setPrototypeOf()</td><td>Object.setPrototypeOf()</td></tr><tr><td>handler.isExtensible()</td><td>Object.isExtensible()</td></tr><tr><td>handler.preventExtensions()</td><td>Object.preventExtensions()</td></tr><tr><td>handler.getOwnPropertyDescriptor()</td><td>Object.getOwnPropertyDescriptor()</td></tr><tr><td>handler.defineProperty()</td><td>Object.defineProperty()</td></tr><tr><td>handler.has()</td><td>in 演算子 (ロパティが指定されたオブジェクトにある) <br> prop in Object</td></tr><tr><td>handler.get()</td><td>プロパティ値を取得</td></tr><tr><td>handler.set()</td><td>プロパティ値を設定</td></tr><tr><td>handler.deleteProperty()</td><td>delete 演算子 <br> delete Object.prop</td></tr><tr><td>handler.ownKeys()</td><td>Object.getOwnPropertyNames()<br>Object.getOwnPropertySymbols()</td></tr><tr><td>handler.apply()</td><td>関数呼び出し</td></tr><tr><td>handler.construct()</td><td>new 演算子 <br> new Object()</td></tr></tbody></table><h3 id="いくつか試す"><a href="#いくつか試す" class="headerlink" title="いくつか試す"></a>いくつか試す</h3><h4 id="objectのプロパティ変更イベント"><a href="#objectのプロパティ変更イベント" class="headerlink" title="objectのプロパティ変更イベント"></a>objectのプロパティ変更イベント</h4><p>プロパティ変更するメソッドのtrapすることで変更イベントを検知して特定の処理を実施できる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> onChangeObject = <span class="function">(<span class="params">target, func</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> handler = &#123;</span><br><span class="line">        set(target, property, value) &#123;</span><br><span class="line">            <span class="keyword">const</span> currentValue = target[property];</span><br><span class="line">            func(target, property, value, currentValue);</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">Reflect</span>.set(target, property, value);</span><br><span class="line">        &#125;,</span><br><span class="line">        defineProperty(target, property, descriptor) &#123;</span><br><span class="line">func(target, property, descriptor, <span class="literal">undefined</span>);</span><br><span class="line"><span class="keyword">return</span> <span class="built_in">Reflect</span>.defineProperty(target, property, descriptor);</span><br><span class="line">        &#125;,</span><br><span class="line">        deleteProperty(target, property) &#123;</span><br><span class="line">            <span class="keyword">const</span> currentValue = target[property];</span><br><span class="line">            func(target, property, <span class="literal">undefined</span>, currentValue);</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">Reflect</span>.deleteProperty(target, property);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> Animal = &#123;</span><br><span class="line">    name: <span class="string">'Pochi'</span>,</span><br><span class="line">    kind: <span class="string">'dog'</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> handle = <span class="function">(<span class="params">target, property, value, currentValue</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(target);</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">`property - <span class="subst">$&#123;property&#125;</span>, new value - <span class="subst">$&#123;value&#125;</span>, current value - <span class="subst">$&#123;currentValue&#125;</span>`</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> watchedObject = onChangeObject.apply(<span class="literal">null</span>, [Animal, handle]);</span><br><span class="line">watchedObject.name = <span class="string">'Taro'</span>; <span class="comment">// property - name, new value - Taro, current value - Pochi</span></span><br><span class="line"><span class="keyword">delete</span> watchedObject.name; <span class="comment">// property - name, new value - undefined, current value - Taro</span></span><br></pre></td></tr></table></figure><h4 id="enum"><a href="#enum" class="headerlink" title="enum"></a>enum</h4><p>enumもできる</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> createEnum = <span class="function"><span class="params">target</span> =&gt;</span>&#123;</span><br><span class="line">    <span class="keyword">const</span> handler = &#123;</span><br><span class="line">        set() &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'Enum is read only'</span>);</span><br><span class="line">        &#125;,</span><br><span class="line">        setPrototypeOf() &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'Enum is read only'</span>);</span><br><span class="line">        &#125;,</span><br><span class="line">        defineProperty() &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'Enum is read only'</span>);</span><br><span class="line">        &#125;,</span><br><span class="line">        deleteProperty() &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'Enum is read only'</span>);</span><br><span class="line">        &#125;,</span><br><span class="line">        get(target, property) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!target.hasOwnProperty(property)) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">ReferenceError</span>(<span class="string">`Unknown enum key "<span class="subst">$&#123;property&#125;</span>"`</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, property);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> object = &#123;</span><br><span class="line">    ja: <span class="string">'Japan'</span>,</span><br><span class="line">    us: <span class="string">'United States of America'</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> customEnum = createEnum.apply(<span class="literal">null</span>, [object]);</span><br><span class="line"><span class="built_in">console</span>.log(customEnum.ja); <span class="comment">// Japan</span></span><br><span class="line"><span class="built_in">console</span>.log(customEnum.us); <span class="comment">// United States of America</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    customEnum.uk</span><br><span class="line">&#125; <span class="keyword">catch</span>(error) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(error.message); <span class="comment">// Unknown enum key "uk"</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    customEnum.uk = <span class="string">'United Kingdom'</span>;</span><br><span class="line">&#125; <span class="keyword">catch</span>(error) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(error.message); <span class="comment">// Enum is read only</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Object.freeze を使う</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> freesedObject = <span class="built_in">Object</span>.freeze(object);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(freesedObject.ja); <span class="comment">// Japan</span></span><br><span class="line"><span class="built_in">console</span>.log(freesedObject.us); <span class="comment">// United States of America</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fail = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="meta">    'use strict'</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        freesedObject.uk = <span class="string">'United Kingdom'</span>;</span><br><span class="line">    &#125; <span class="keyword">catch</span>(error) &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(error.message); <span class="comment">// Cannot add property uk, object is not extensible</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)();</span><br></pre></td></tr></table></figure><h4 id="cached-object"><a href="#cached-object" class="headerlink" title="cached object"></a>cached object</h4><p>一定時間キャッシュ可能なオブジェクト</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> objectCache = <span class="function">(<span class="params">target, ttl</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> data = &#123;&#125;;</span><br><span class="line">    <span class="keyword">const</span> handler = &#123;</span><br><span class="line">        set(target, property, value) &#123;</span><br><span class="line">            data[property] = <span class="keyword">new</span> <span class="built_in">Date</span>().getTime() + ttl;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">Reflect</span>.set(target, property, value);</span><br><span class="line">        &#125;,</span><br><span class="line">        get(target, property) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, property);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">deleteCache</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(key <span class="keyword">in</span> data) &#123;</span><br><span class="line">            <span class="keyword">if</span> (data[key] &lt; <span class="keyword">new</span> <span class="built_in">Date</span>().getTime()) &#123;</span><br><span class="line">                <span class="keyword">delete</span> target[key];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    setInterval(deleteCache, ttl);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cachedPbject = objectCache.apply(<span class="literal">null</span>, [&#123;&#125;, <span class="number">1000</span>]);</span><br><span class="line"></span><br><span class="line">cachedPbject.test = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(cachedPbject); <span class="comment">// &#123;test: 1&#125;</span></span><br><span class="line"></span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(cachedPbject.test);</span><br><span class="line">&#125;, <span class="number">1001</span>); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure><h4 id="object-property-validation"><a href="#object-property-validation" class="headerlink" title="object property validation"></a>object property validation</h4><p>objectのプロパティ設定値をvalidateする。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> withValidate = <span class="function"><span class="params">target</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> regexp = <span class="built_in">RegExp</span>(<span class="string">'[0-9]'</span>);</span><br><span class="line">    <span class="keyword">const</span> handler = &#123;</span><br><span class="line">        set(target, property, value) &#123;</span><br><span class="line">            <span class="keyword">if</span> (regexp.test(value)) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="built_in">Reflect</span>.set(target, property, value);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">`<span class="subst">$&#123;value&#125;</span> is not correct type`</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> withValidationObject = withValidate.apply(<span class="literal">null</span>, [&#123;&#125;]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    withValidationObject.int = <span class="string">'a'</span>;</span><br><span class="line">    <span class="built_in">console</span>.log(withValidationObject.int);</span><br><span class="line">&#125; <span class="keyword">catch</span>(error) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(error.message); <span class="comment">// a is not correct type</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    withValidationObject.int = <span class="number">1</span>;</span><br><span class="line">    <span class="built_in">console</span>.log(withValidationObject.int);</span><br><span class="line">&#125; <span class="keyword">catch</span>(error) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(error.message);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="singleton"><a href="#singleton" class="headerlink" title="singleton"></a>singleton</h4><p>シングルトンパターンを実現する</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> createSingleton = <span class="function"><span class="params">target</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> instance;</span><br><span class="line">    <span class="keyword">const</span> handler = &#123;</span><br><span class="line">        construct(target, argumentsList) &#123;</span><br><span class="line">            <span class="keyword">if</span> (!instance) &#123;</span><br><span class="line">                instance = <span class="built_in">Reflect</span>.construct(target, argumentsList);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> instance;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Person = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">''</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> person1 = <span class="keyword">new</span> Person();</span><br><span class="line"><span class="keyword">const</span> person2 = <span class="keyword">new</span> Person();</span><br><span class="line"></span><br><span class="line">person1.name = <span class="string">'Taro'</span>;</span><br><span class="line">person2.name = <span class="string">'Jiro'</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person1.name); <span class="comment">// Taro</span></span><br><span class="line"><span class="built_in">console</span>.log(person2.name); <span class="comment">// Jiro</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">`Not singleton <span class="subst">$&#123;person1.name == person2.name&#125;</span>`</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> SingletonPerson = createSingleton.apply(<span class="literal">null</span>, [Person])</span><br><span class="line"><span class="keyword">const</span> person3 = <span class="keyword">new</span> SingletonPerson();</span><br><span class="line">person3.name = <span class="string">'Taro'</span>;</span><br><span class="line"><span class="keyword">const</span> person4 = <span class="keyword">new</span> SingletonPerson();</span><br><span class="line">person3.name = <span class="string">'Jiro'</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(person3.name); <span class="comment">// Jiro</span></span><br><span class="line"><span class="built_in">console</span>.log(person4.name); <span class="comment">// Jiro</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">`Is singleton <span class="subst">$&#123;person3.name == person4.name&#125;</span>`</span>);</span><br></pre></td></tr></table></figure><h3 id="参考にしたページ"><a href="#参考にしたページ" class="headerlink" title="参考にしたページ"></a>参考にしたページ</h3><p><a href="https://github.com/gergob/jsProxy" target="_blank" rel="noopener">jsProxy</a><br><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy" target="_blank" rel="noopener">Proxy</a><br><a href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Reflect" target="_blank" rel="noopener">Reflect</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Proxyオブジェクトがサポートされたことで、&lt;br&gt;JavaScriptにでもオブジェクト操作が柔軟に拡張できるようになった。&lt;/p&gt;
&lt;p&gt;これにより処理を動的に拡張するメタプログラミングできる。&lt;br&gt;ということで、&lt;a href=&quot;https://github.com/gergob/jsProxy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;参考になりそうな事例&lt;/a&gt;があったので試してみた。&lt;/p&gt;
    
    </summary>
    
      <category term="web" scheme="http://blog.kazu69.net/categories/web/"/>
    
    
  </entry>
  
  <entry>
    <title>Node.jsのアドオンをN-APIで実装する</title>
    <link href="http://blog.kazu69.net/2017/11/19/nodejs-native-add-on-with-napi/"/>
    <id>http://blog.kazu69.net/2017/11/19/nodejs-native-add-on-with-napi/</id>
    <published>2017-11-19T10:53:35.000Z</published>
    <updated>2017-11-26T12:56:36.000Z</updated>
    
    <content type="html"><![CDATA[<p>Node.js 8.0からは<a href="https://nodejs.org/api/n-api.html" target="_blank" rel="noopener">N-API</a>というものが試験的にサポートされている。<br>これはNode.js APIのABI(Application Binary Interface)-stableな中間レイヤでこれを使うことでネティブアドオンの開発が効率的になる。</p><a id="more"></a><h2 id="概要"><a href="#概要" class="headerlink" title="概要"></a>概要</h2><p>Node.jsをネイティブ拡張するには<a href="https://github.com/nodejs/nan" target="_blank" rel="noopener">NAN</a>や<a href="https://github.com/v8/v8" target="_blank" rel="noopener">V8</a>に依存しているため、<br>その内部実装に関しての知識が少なからず必要になってくるということが問題となっている。</p><p>また、これらはバージョンごとでAPIは安定性が保証されてないので、<br>ネイティブな機能のリリースには各バージョンでのコンパイルが必要となり、<br>開発者の負担のかかる状況とのこと。</p><p>そこでNode.js8.0からABI-stableなN-APIが試験的に追加されている。</p><p>N-APIをつかうことで、一度コンパイルしているモジュールはN-APIをサポートしているNode.jsのどのバージョンでも利用できる。<br>(現在は8.0以降のサポートだけど、今後は会のバージョンへの移植も検討されている)</p><p>N-APIを使うことでV8に依存していたネイティブアドオンをプラットフォームやアーキテクチャごとにビルドする必要がなくなる。</p><p>とういうことで、V8とN-APIでネイティブアドオンを作ってみた。</p><h3 id="node-gyp"><a href="#node-gyp" class="headerlink" title="node-gyp"></a>node-gyp</h3><p>まずはじめに<a href="https://github.com/nodejs/node-gyp" target="_blank" rel="noopener">node-gyp</a>を使うのでインストールしておく必要がある。<br>ChromiumやV8同様にGYP(Generate Your Projects)をつかってプロジェクトファイルを生成し、アドオンを作ります。<br>(もちろん利用するにはpython &lt;= 2.7も必要)</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm i -g node-gyp</span><br></pre></td></tr></table></figure><p>今回使うC++プログラム。</p><figure class="highlight h"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> _PRIME_NUMBER_H_</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> _PRIME_NUMBER_H_</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PrimeNumber</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="comment">// accessor</span></span><br><span class="line">    <span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">isPrime</span><span class="params">(<span class="keyword">int</span> number)</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span> <span class="comment">// _PRIME_NUMBER_H_</span></span></span><br></pre></td></tr></table></figure><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdbool.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"prime_number.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// function definition</span></span><br><span class="line"><span class="keyword">int</span> PrimeNumber::isPrime(<span class="keyword">int</span> number)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">bool</span> result = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">int</span> i;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">2</span>; i &lt; number; ++i) &#123;</span><br><span class="line">        <span class="keyword">if</span> (number % i == <span class="number">0</span>) &#123;</span><br><span class="line">            result = <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">break</span>;            </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最終的に以下のようにJavaScriptから呼び出せれば良い。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// test.js</span></span><br><span class="line"><span class="keyword">const</span> primeNumber = <span class="built_in">require</span>(<span class="string">'./build/Release/primeNumber.node'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(primeNumber.isPrimeNumber(<span class="number">5</span>)); <span class="comment">// =&gt; true</span></span><br><span class="line"><span class="built_in">console</span>.log(primeNumber.isPrimeNumber(<span class="number">6</span>)); <span class="comment">// =&gt; false</span></span><br></pre></td></tr></table></figure><h3 id="V8をそのまま使う"><a href="#V8をそのまま使う" class="headerlink" title="V8をそのまま使う"></a>V8をそのまま使う</h3><p>binding.gyp を作成</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">'targets'</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">'target_name'</span>: <span class="string">'primeNumber'</span>,</span><br><span class="line">      <span class="string">'sources'</span>: [<span class="string">'primeNumber.cpp'</span>, <span class="string">'../prime_number/prime_number.cpp'</span>],</span><br><span class="line">      <span class="string">'conditions'</span>: [</span><br><span class="line">        [</span><br><span class="line">          <span class="string">'OS=="linux"'</span>, </span><br><span class="line">          &#123;</span><br><span class="line">            <span class="string">"defines"</span>: [ <span class="string">"_GNU_SOURCE"</span> ],</span><br><span class="line">            <span class="string">"cflags"</span>: [ <span class="string">"-g"</span>, <span class="string">"-O2"</span>, <span class="string">"-std=c++11"</span>, ],</span><br><span class="line">          &#125;</span><br><span class="line">        ],</span><br><span class="line">        [</span><br><span class="line">          <span class="string">'OS=="win"'</span>, </span><br><span class="line">          &#123;</span><br><span class="line">            <span class="string">'libraries'</span>: [</span><br><span class="line">              <span class="string">'dbghelp.lib'</span>,</span><br><span class="line">              <span class="string">'Netapi32.lib'</span></span><br><span class="line">            ],</span><br><span class="line">            <span class="string">'dll_files'</span>: [</span><br><span class="line">              <span class="string">'dbghelp.dll'</span>,</span><br><span class="line">              <span class="string">'Netapi32.dll'</span></span><br><span class="line">            ],</span><br><span class="line">          &#125;</span><br><span class="line">        ],</span><br><span class="line">        [</span><br><span class="line">          <span class="string">'OS=="mac"'</span>,</span><br><span class="line">          &#123;</span><br><span class="line">            <span class="string">'xcode_settings'</span>: &#123;</span><br><span class="line">              <span class="string">'CLANG_CXX_LIBRARY'</span>: <span class="string">'libc++'</span>,</span><br><span class="line">              <span class="string">'CLANG_CXX_LANGUAGE_STANDARD'</span>:<span class="string">'c++11'</span>,</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        ],</span><br><span class="line">      ]</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>V8をそのまま使っている。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;node.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;v8.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"../prime_number/prime_number.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Method</span><span class="params">(<span class="keyword">const</span> v8::FunctionCallbackInfo&lt;v8::Value&gt;&amp; args)</span> </span>&#123;</span><br><span class="line">  v8::Isolate* isolate = args.GetIsolate();</span><br><span class="line"></span><br><span class="line">  <span class="keyword">double</span> number = args[<span class="number">0</span>]-&gt;NumberValue();</span><br><span class="line">  <span class="keyword">bool</span> data;</span><br><span class="line">  PrimeNumber primeNumber;</span><br><span class="line"></span><br><span class="line">  data = primeNumber.isPrime(number);</span><br><span class="line">  v8::Local&lt;v8::Boolean&gt; result = v8::Boolean::New(isolate, data); </span><br><span class="line"></span><br><span class="line">  args.GetReturnValue().Set(result);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">init</span><span class="params">(v8::Local&lt;v8::Object&gt; exports)</span> </span>&#123;</span><br><span class="line">  NODE_SET_METHOD(exports, <span class="string">"isPrimeNumber"</span>, Method);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">NODE_MODULE(NODE_GYP_MODULE_NAME, init)</span><br></pre></td></tr></table></figure><p>プロジェクト作成とビルド</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">node-gyp configure</span><br><span class="line">node-gyp build</span><br></pre></td></tr></table></figure><p>実行する</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node test.js</span><br></pre></td></tr></table></figure><h3 id="N-API"><a href="#N-API" class="headerlink" title="N-API"></a>N-API</h3><p>同じくbinding.gyp を作成</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">"targets"</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">"target_name"</span>: <span class="string">"primeNumber"</span>,</span><br><span class="line">      <span class="string">"sources"</span>: [ <span class="string">"primeNumber.cpp"</span>, <span class="string">"../prime_number/prime_number.cpp"</span> ]</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>そして、N-APIを使う。</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;node_api.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;assert.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">"../prime_number/prime_number.hpp"</span></span></span><br><span class="line"></span><br><span class="line"><span class="function">napi_value <span class="title">Method</span><span class="params">(napi_env env, napi_callback_info info)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  napi_status status;</span><br><span class="line">  <span class="keyword">size_t</span> argc = <span class="number">1</span>;</span><br><span class="line">  napi_value args[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line">  status = napi_get_cb_info(env, info, &amp;argc, args, <span class="literal">nullptr</span>, <span class="literal">nullptr</span>);</span><br><span class="line">  assert(status == napi_ok);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 引数の数をチェック</span></span><br><span class="line">  <span class="keyword">if</span> (argc == <span class="number">0</span>) &#123;</span><br><span class="line">    napi_throw_type_error(env, <span class="string">"Wrong number of arguments"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 引数の型をチェック</span></span><br><span class="line">  napi_valuetype valuetype;</span><br><span class="line">  status = napi_typeof(env, args[<span class="number">0</span>], &amp;valuetype);</span><br><span class="line">  <span class="keyword">if</span> (valuetype != napi_number) &#123;</span><br><span class="line">    napi_throw_type_error(env, <span class="string">"Wrong arguments"</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">int32_t</span> number;</span><br><span class="line">  status = napi_get_value_int32(env, args[<span class="number">0</span>], &amp;number);</span><br><span class="line">  assert(status == napi_ok);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">bool</span> data;</span><br><span class="line">  PrimeNumber primeNumber;</span><br><span class="line">  data = primeNumber.isPrime((<span class="keyword">int</span>)number);</span><br><span class="line"></span><br><span class="line">  napi_value result;</span><br><span class="line">  status = napi_get_boolean(env, data, &amp;result);</span><br><span class="line">  assert(status == napi_ok);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">Init</span><span class="params">(napi_env env, napi_value exports, napi_value <span class="keyword">module</span>, <span class="keyword">void</span>* priv)</span> </span>&#123;</span><br><span class="line">  napi_status status;</span><br><span class="line">  napi_value fn;</span><br><span class="line">  status =  napi_create_function(env, <span class="literal">NULL</span>, Method, <span class="literal">NULL</span>, &amp;fn);</span><br><span class="line">  <span class="keyword">if</span> (status != napi_ok) <span class="keyword">return</span>;</span><br><span class="line">  status = napi_set_named_property(env, exports, <span class="string">"isPrimeNumber"</span>, fn);</span><br><span class="line">  <span class="keyword">if</span> (status != napi_ok) <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">NAPI_MODULE(isPrimeNumber, Init)</span><br></pre></td></tr></table></figure><p>後は同じく、プロジェクト作成してビルド。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">node-gyp configure</span><br><span class="line">node-gyp build</span><br></pre></td></tr></table></figure><p>実行する際にはオプションが必要。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node --napi-modules test.js</span><br></pre></td></tr></table></figure><h3 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h3><p>実際に仕事でこんなことないんですが、サービスによっては複数の言語で動いているとかや、<br>すでにC/C++で完成している機能をJS側から呼び出したいというとき。<br>またC/C++のプロジェクトの移植でモジュール開発する際などで使う際はN-APIサポートしておくと捗りそう。</p><p><a href="https://github.com/kazu69/nodejs-addon-example" target="_blank" rel="noopener">今回作成したサンプル</a></p><h3 id="参考にしたページ"><a href="#参考にしたページ" class="headerlink" title="参考にしたページ"></a>参考にしたページ</h3><p><a href="https://nodejs.org/api/n-api.html" target="_blank" rel="noopener">N-API</a><br><a href="https://medium.com/the-node-js-collection/n-api-next-generation-node-js-apis-for-native-modules-169af5235b06" target="_blank" rel="noopener">N-API: Next generation Node.js APIs for native modules</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Node.js 8.0からは&lt;a href=&quot;https://nodejs.org/api/n-api.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;N-API&lt;/a&gt;というものが試験的にサポートされている。&lt;br&gt;これはNode.js APIのABI(Application Binary Interface)-stableな中間レイヤでこれを使うことでネティブアドオンの開発が効率的になる。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>brewのHOMEBREW_ENV_FILTERINGフラグを知った</title>
    <link href="http://blog.kazu69.net/2017/10/20/filter_environment_variables_brew/"/>
    <id>http://blog.kazu69.net/2017/10/20/filter_environment_variables_brew/</id>
    <published>2017-10-20T01:49:32.000Z</published>
    <updated>2017-10-28T08:21:45.000Z</updated>
    
    <content type="html"><![CDATA[<p>brewが実行のたびにcurlエラーという状況になり、しばしはまったのでメモ。</p><a id="more"></a><p>以下のよう現象になった。<br>brewのたびにエラーが起きるので何もできない。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ brew</span><br><span class="line">==&gt; Downloading https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz</span><br><span class="line"></span><br><span class="line">curl: (51) SSL: certificate verification failed (result: 5)</span><br><span class="line">Error: Download failed: https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz</span><br><span class="line">Error: Failed to install vendor Ruby.</span><br></pre></td></tr></table></figure><p>エラー内容から<strong>CURL_CA_BUNDLE</strong> あたりを疑い、パスとcurlのバージョン、certあたりを確認し、<strong>SSL_CERT_FILE</strong>が設定されていることがわかり、unsetするもいっこうに解消しない。</p><p>他の環境変数が影響しているのか、結局原因がわからないままになったが、同様の現象で<a href="https://github.com/Homebrew/brew/issues/3246" target="_blank" rel="noopener">issue</a>があり、<strong>HOMEBREW_ENV_FILTERING</strong>で環境変数をフィルタリングすることを知り試してみると、無事に解消した。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ HOMEBREW_ENV_FILTERING=1 brew</span><br><span class="line">==&gt; Downloading https://homebrew.bintray.com/bottles-portable/portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz</span><br><span class="line"><span class="comment">######################################################################## 100.0%</span></span><br><span class="line">==&gt; Pouring portable-ruby-2.3.3.leopard_64.bottle.1.tar.gz</span><br></pre></td></tr></table></figure><p><a href="https://github.com/Homebrew/brew/pull/1753" target="_blank" rel="noopener">HOMEBREW_ENV_FILTERINGに関してはv1.11</a>で追加されており、実装は以下の通り。</p><p><a href="https://github.com/Homebrew/brew/blob/b2e2e4b917805b8cf0a86bbc4a1517146a1f0d33/bin/brew#L61-L71" target="_blank" rel="noopener">https://github.com/Homebrew/brew/blob/b2e2e4b917805b8cf0a86bbc4a1517146a1f0d33/bin/brew#L61-L71</a></p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> [[ -n <span class="string">"<span class="variable">$HOMEBREW_ENV_FILTERING</span>"</span> ]]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">  PATH=<span class="string">"/usr/bin:/bin:/usr/sbin:/sbin"</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  FILTERED_ENV=()</span><br><span class="line">  <span class="keyword">for</span> VAR <span class="keyword">in</span> HOME SHELL PATH TERM LOGNAME USER <span class="string">"<span class="variable">$&#123;!HOMEBREW_@&#125;</span>"</span></span><br><span class="line">  <span class="keyword">do</span></span><br><span class="line">    FILTERED_ENV+=( <span class="string">"<span class="variable">$&#123;VAR&#125;</span>=<span class="variable">$&#123;!VAR&#125;</span>"</span> )</span><br><span class="line">  <span class="keyword">done</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  /usr/bin/env -i <span class="string">"<span class="variable">$&#123;FILTERED_ENV[@]&#125;</span>"</span> /bin/bash <span class="string">"<span class="variable">$HOMEBREW_LIBRARY</span>/Homebrew/brew.sh"</span> <span class="string">"<span class="variable">$@</span>"</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">source</span> <span class="string">"<span class="variable">$HOMEBREW_LIBRARY</span>/Homebrew/brew.sh"</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><p>で実際に手元でやってみると以下のようになる。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">$ HOMEBREW_ENV_FILTERING=1 brew config</span><br><span class="line">HOMEBREW_VERSION: 1.3.6</span><br><span class="line">ORIGIN: https://github.com/Homebrew/brew</span><br><span class="line">HEAD: 270b752f5d9d218bfbed6fe85b6974fa653fb25f</span><br><span class="line">Last commit: 10 days ago</span><br><span class="line">Core tap ORIGIN: https://github.com/Homebrew/homebrew-core</span><br><span class="line">Core tap HEAD: b6e963c1ce72391194f9b81126afc400885f1bc5</span><br><span class="line">Core tap last commit: 25 minutes ago</span><br><span class="line">HOMEBREW_PREFIX: /usr/<span class="built_in">local</span></span><br><span class="line">HOMEBREW_REPOSITORY: /usr/<span class="built_in">local</span>/Homebrew</span><br><span class="line">HOMEBREW_CELLAR: /usr/<span class="built_in">local</span>/Cellar</span><br><span class="line">HOMEBREW_BOTTLE_DOMAIN: https://homebrew.bintray.com</span><br><span class="line">CPU: quad-core 64-bit broadwell</span><br><span class="line">Homebrew Ruby: 2.3.3 =&gt; /usr/<span class="built_in">local</span>/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.3/bin/ruby</span><br><span class="line">Clang: 8.0 build 800</span><br><span class="line">Git: 2.10.1 =&gt; /Applications/Xcode.app/Contents/Developer/usr/bin/git</span><br><span class="line">Perl: /usr/bin/perl</span><br><span class="line">Python: /usr/bin/python</span><br><span class="line">Ruby: /usr/bin/ruby =&gt; /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby</span><br><span class="line">Java: 1.6.0_65-b14-468</span><br><span class="line">macOS: 10.11.6-x86_64</span><br><span class="line">Xcode: 8.2.1</span><br><span class="line">CLT: 8.2.0.0.1.1480973914</span><br><span class="line">X11: 2.7.9 =&gt; /opt/X11</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">$ brew config</span><br><span class="line">HOMEBREW_VERSION: 1.3.6</span><br><span class="line">ORIGIN: https://github.com/Homebrew/brew</span><br><span class="line">HEAD: 270b752f5d9d218bfbed6fe85b6974fa653fb25f</span><br><span class="line">Last commit: 10 days ago</span><br><span class="line">Core tap ORIGIN: https://github.com/Homebrew/homebrew-core</span><br><span class="line">Core tap HEAD: b6e963c1ce72391194f9b81126afc400885f1bc5</span><br><span class="line">Core tap last commit: 44 minutes ago</span><br><span class="line">HOMEBREW_PREFIX: /usr/<span class="built_in">local</span></span><br><span class="line">HOMEBREW_REPOSITORY: /usr/<span class="built_in">local</span>/Homebrew</span><br><span class="line">HOMEBREW_CELLAR: /usr/<span class="built_in">local</span>/Cellar</span><br><span class="line">HOMEBREW_BOTTLE_DOMAIN: https://homebrew.bintray.com</span><br><span class="line">CPU: quad-core 64-bit broadwell</span><br><span class="line">Homebrew Ruby: 2.3.3 =&gt; /usr/<span class="built_in">local</span>/Homebrew/Library/Homebrew/vendor/portable-ruby/2.3.3/bin/ruby</span><br><span class="line">Clang: 8.0 build 800</span><br><span class="line">Git: 2.14.3 =&gt; /usr/<span class="built_in">local</span>/bin/git</span><br><span class="line">Perl: /usr/bin/perl</span><br><span class="line">Python: /usr/bin/python</span><br><span class="line">Ruby: /Users/XXX/.rbenv/shims/ruby =&gt; /Users/XXX/.rbenv/versions/2.3.3/bin/ruby</span><br><span class="line">Java: 1.6.0_65-b14-468</span><br><span class="line">macOS: 10.11.6-x86_64</span><br><span class="line">Xcode: 8.2.1</span><br><span class="line">CLT: 8.2.0.0.1.1480973914</span><br><span class="line">X11: 2.7.9 =&gt; /opt/X11</span><br></pre></td></tr></table></figure><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">15c15</span><br><span class="line">&lt; Git: 2.14.3 =&gt; /usr/local/bin/git</span><br><span class="line"><span class="comment">---</span></span><br><span class="line">&gt; Git: 2.10.1 =&gt; /Applications/Xcode.app/Contents/Developer/usr/bin/git</span><br><span class="line">18c18</span><br><span class="line">&lt; Ruby: /Users/XXX/.rbenv/shims/ruby =&gt; /Users/XXX/.rbenv/versions/2.3.3/bin/ruby</span><br><span class="line"><span class="comment">---</span></span><br><span class="line">&gt; Ruby: /usr/bin/ruby =&gt; /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby</span><br></pre></td></tr></table></figure><p>というように手元の環境をフィルタリングしてデフォルトのものを使う感じになのかな。<br>まずbrewがトラブった場合は試してみるとよさそう。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;brewが実行のたびにcurlエラーという状況になり、しばしはまったのでメモ。&lt;/p&gt;
    
    </summary>
    
      <category term="web" scheme="http://blog.kazu69.net/categories/web/"/>
    
    
  </entry>
  
  <entry>
    <title>稼働中のapacheプロセスやphpエラーgdbで追う</title>
    <link href="http://blog.kazu69.net/2017/09/11/running-apache-process-php-error-gdb/"/>
    <id>http://blog.kazu69.net/2017/09/11/running-apache-process-php-error-gdb/</id>
    <published>2017-09-11T01:40:10.000Z</published>
    <updated>2017-09-18T23:31:02.000Z</updated>
    
    <content type="html"><![CDATA[<p>稼働中のサービスにおいてapacheのプロセスが閾値を超える事象が頻発したり、<br>phpのバグを踏んだりした際に。なにが原因なのか究明のために行ったときの備忘録。</p><a id="more"></a><p>必ずSegmentation Faultが起きるようにphpを拡張して、デバッグしていく。</p><h3 id="php-extentionsを作成"><a href="#php-extentionsを作成" class="headerlink" title="php extentionsを作成"></a>php extentionsを作成</h3><p>まずSegmentation Faultとなるようにphpを拡張する。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># php取得</span></span><br><span class="line">curl -s -O http://jp2.php.net/distributions/php-5.6.31.tar.gz</span><br><span class="line">tar -zxvf php-5.6.31.tar.gz</span><br><span class="line"><span class="built_in">cd</span> php-5.6.31/ext</span><br><span class="line"></span><br><span class="line"><span class="comment"># 拡張を作成するためのスケルトンを作成</span></span><br><span class="line">./ext_skel --extname=example</span><br><span class="line">Creating directory example</span><br><span class="line">Creating basic files: config.m4 config.w32 .gitignore example.c php_example.h CREDITS EXPERIMENTAL tests/001.phpt example.php [<span class="keyword">done</span>].</span><br><span class="line"></span><br><span class="line">To use your new extension, you will have to execute the following steps:</span><br><span class="line"></span><br><span class="line">1.  $ <span class="built_in">cd</span> ..</span><br><span class="line">2.  $ vi ext/example/config.m4</span><br><span class="line">3.  $ ./buildconf</span><br><span class="line">4.  $ ./configure --[with|<span class="built_in">enable</span>]-example</span><br><span class="line">5.  $ make</span><br><span class="line">6.  $ ./sapi/cli/php -f ext/example/example.php</span><br><span class="line">7.  $ vi ext/example/example.c</span><br><span class="line">8.  $ make</span><br><span class="line"></span><br><span class="line">Repeat steps 3-6 until you are satisfied with ext/example/config.m4 and</span><br><span class="line">step 6 confirms that your module is compiled into PHP. Then, start writing</span><br><span class="line">code and repeat the last two steps as often as necessary.</span><br><span class="line"></span><br><span class="line"><span class="comment"># 表示された手順に従い拡張を作成していく</span></span><br><span class="line"><span class="built_in">cd</span> example</span><br><span class="line"></span><br><span class="line">vi config.m4</span><br></pre></td></tr></table></figure><p>生成されたconfig.m4を編集する。<br>以下の箇所をコメントインする</p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">/* config.m4 */</span><br><span class="line"><span class="deletion">- dnl PHP_ARG_WITH(example, for example support,</span></span><br><span class="line"><span class="deletion">- dnl Make sure that the comment is aligned:</span></span><br><span class="line"><span class="deletion">- dnl [  --with-example             Include example support])</span></span><br><span class="line"><span class="addition">+ PHP_ARG_WITH(example, for example support,</span></span><br><span class="line"><span class="addition">+ Make sure that the comment is aligned:</span></span><br><span class="line"><span class="addition">+ [  --with-example             Include example support])</span></span><br></pre></td></tr></table></figure><p>拡張モジュールをビルドのためにphpizeする</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line">phpize</span><br><span class="line"></span><br><span class="line">Configuring <span class="keyword">for</span>:</span><br><span class="line">PHP Api Version:         20121113</span><br><span class="line">Zend Module Api No:      20121212</span><br><span class="line">Zend Extension Api No:   220121212</span><br><span class="line"></span><br><span class="line">./configure</span><br><span class="line">...</span><br><span class="line">creating libtool</span><br><span class="line">appending configuration tag <span class="string">"CXX"</span> to libtool</span><br><span class="line">configure: creating ./config.status</span><br><span class="line">config.status: creating config.h</span><br><span class="line"></span><br><span class="line">make</span><br><span class="line"></span><br><span class="line">...</span><br><span class="line">----------------------------------------------------------------------</span><br><span class="line">Libraries have been installed <span class="keyword">in</span>:</span><br><span class="line">   /Users/kazu/Dropbox/workspace/debug-gdb/php-5.6.31/ext/example/modules</span><br><span class="line"></span><br><span class="line">If you ever happen to want to link against installed libraries</span><br><span class="line"><span class="keyword">in</span> a given directory, LIBDIR, you must either use libtool, and</span><br><span class="line">specify the full pathname of the library, or use the `-LLIBDIR<span class="string">'</span></span><br><span class="line"><span class="string">flag during linking and do at least one of the following:</span></span><br><span class="line"><span class="string">   - add LIBDIR to the `DYLD_LIBRARY_PATH'</span> environment variable</span><br><span class="line">     during execution</span><br><span class="line"></span><br><span class="line">See any operating system documentation about shared libraries <span class="keyword">for</span></span><br><span class="line">more information, such as the ld(1) and ld.so(8) manual pages.</span><br><span class="line">----------------------------------------------------------------------</span><br><span class="line"></span><br><span class="line">Build complete.</span><br><span class="line">Don<span class="string">'t forget to run '</span>make <span class="built_in">test</span><span class="string">'.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">php -d extension=modules/example.so example.php</span></span><br><span class="line"><span class="string">Functions available in the test extension:</span></span><br><span class="line"><span class="string">confirm_example_compiled</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">Congratulations! You have successfully modified ext/example/config.m4. Module example is now compiled into PHP.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">vi example.c</span></span><br></pre></td></tr></table></figure><p>example.cに拡張を追加していく。<br>拡張を実装する。</p><figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line">/* example.c */</span><br><span class="line"></span><br><span class="line"> const zend_function_entry example_functions[] = &#123;</span><br><span class="line"> PHP_FE(confirm_example_compiled,NULL)/* For testing,  remove later. */</span><br><span class="line">     /* 新しいphpメソッドを追加する */</span><br><span class="line"><span class="addition">+ PHP_FE(down_method, NULL)</span></span><br><span class="line"> PHP_FE_END/* Must be the last line in example_functions[] */</span><br><span class="line"> &#125;;</span><br><span class="line"></span><br><span class="line">PHP_FUNCTION(confirm_example_compiled)</span><br><span class="line">&#123;</span><br><span class="line">char *arg = NULL;</span><br><span class="line">int arg_len, len;</span><br><span class="line">char *strg;</span><br><span class="line"></span><br><span class="line">if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &amp;arg, &amp;arg_len) == FAILURE) &#123;</span><br><span class="line">return;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">len = spprintf(&amp;strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "example", arg);</span><br><span class="line">RETURN_STRINGL(strg, len, 0);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="addition">+ /* NULLアドレスの参照で必ず落ちる */</span></span><br><span class="line"><span class="addition">+ PHP_FUNCTION(down_method)</span></span><br><span class="line"><span class="addition">+ &#123;</span></span><br><span class="line"><span class="addition">+ char *ptr = NULL ;</span></span><br><span class="line"><span class="addition">+     strcpy(ptr, "down") ;</span></span><br><span class="line"><span class="addition">+ &#125;</span></span><br><span class="line"></span><br><span class="line">zend_module_entry example_module_entry = &#123;</span><br><span class="line">STANDARD_MODULE_HEADER,</span><br><span class="line">"example",</span><br><span class="line">example_functions,</span><br><span class="line"><span class="deletion">-PHP_MINIT(example),</span></span><br><span class="line"><span class="deletion">-PHP_MSHUTDOWN(example),</span></span><br><span class="line"><span class="deletion">-PHP_RINIT(example),/* Replace with NULL if there's nothing to do at request start */</span></span><br><span class="line"><span class="deletion">-PHP_RSHUTDOWN(example),/* Replace with NULL if there's nothing to do at request end */</span></span><br><span class="line"><span class="deletion">-PHP_MINFO(example),</span></span><br><span class="line"><span class="deletion">-PHP_EXAMPLE_VERSION,</span></span><br><span class="line">STANDARD_MODULE_PROPERTIES</span><br><span class="line">&#125;;</span><br><span class="line">zend_module_entry example_module_entry = &#123;</span><br><span class="line"> STANDARD_MODULE_HEADER,</span><br><span class="line"> "example",</span><br><span class="line"> example_functions,</span><br><span class="line"><span class="addition">+NULL,</span></span><br><span class="line"><span class="addition">+NULL,</span></span><br><span class="line"><span class="addition">+NULL,/* Replace with NULL if there's nothing to do at request start */</span></span><br><span class="line"><span class="addition">+NULL,/* Replace with NULL if there's nothing to do at request end */</span></span><br><span class="line"><span class="addition">+NULL,</span></span><br><span class="line"><span class="addition">+NO_VERSION_YET,</span></span><br><span class="line"> STANDARD_MODULE_PROPERTIES</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>makeする</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">make</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>一応これで必ずSegmentation Faultふがおきるphpのメソッドができた。</p><h3 id="コンテナの起動"><a href="#コンテナの起動" class="headerlink" title="コンテナの起動"></a>コンテナの起動</h3><p>いろいろとコアな変更もしたいので <strong>privileged</strong> でコンテナを起動。</p><p>コアファイルへのアクセスやプロセスのtraceなどを行うため、コンテナ起動時にはcapabilityの追加の追加をしておく。</p><p>またcoreファイルのシステムリソールを変更する(unlimitedではエラーになるので十分に大きな数字を渡す)。</p><p>コンテナ内のシステムコールへのアクセスがセキュリティ的にだめなので、<strong>security-opt</strong> オプションを追加。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># build and run container</span></span><br><span class="line">docker build -t kazu69/debug_apache_php_with_gdb .</span><br><span class="line"></span><br><span class="line"><span class="comment"># run container</span></span><br><span class="line">docker run -d \</span><br><span class="line">           -p 8080:80 \</span><br><span class="line">           --privileged \</span><br><span class="line">           --<span class="built_in">ulimit</span> core=9999999999 \</span><br><span class="line">           --<span class="built_in">cap</span>-add=SYS_PTRACE \</span><br><span class="line">           --security-opt seccomp:unconfined \</span><br><span class="line">           kazu69/debug_apache_php_with_gdb</span><br></pre></td></tr></table></figure><h3 id="straceでシステムコールを眺める"><a href="#straceでシステムコールを眺める" class="headerlink" title="straceでシステムコールを眺める"></a>straceでシステムコールを眺める</h3><p>デバッグの際によく使われるのがstrace。<br>プロセスがどの動きをしているのか(カーネルのシステムコール)をtraceする。</p><p>今回はphpで <strong>gethostbyaddr</strong> を実行してそれをtraceしてみる。<br>わかりやすいようにapacheのコプロセスを1つにしている。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># -T 各システムコールにかかった時間</span></span><br><span class="line"><span class="comment"># t timestampを出力</span></span><br><span class="line"><span class="comment"># ff forkしたプロセスのシステムコールもトレース</span></span><br><span class="line"><span class="comment"># p プロセスID</span></span><br><span class="line"><span class="comment"># o 指定したファイルに出力</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># strace -T -t -ff -o &lt;path-to-log-file&gt; -p &lt;pid&gt;</span></span><br><span class="line"></span><br><span class="line">pstree -p</span><br><span class="line">apache2(1)---apache2(44)</span><br><span class="line"></span><br><span class="line">strace -p 44</span><br><span class="line">Process 44 attached</span><br><span class="line">accept4(3, &#123;sa_family=AF_INET, sin_port=htons(55964), sin_addr=inet_addr(<span class="string">"172.17.0.1"</span>)&#125;, [16], SOCK_CLOEXEC) = 11</span><br><span class="line">gettimeofday(&#123;1505745624, 9024&#125;, NULL)  = 0</span><br><span class="line">getsockname(11, &#123;sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr(<span class="string">"172.17.0.2"</span>)&#125;, [16]) = 0</span><br><span class="line">fcntl(11, F_GETFL)                      = 0x2 (flags O_RDWR)</span><br><span class="line">fcntl(11, F_SETFL, O_RDWR|O_NONBLOCK)   = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9284&#125;, NULL)  = 0</span><br><span class="line">mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f11695fc000</span><br><span class="line">gettimeofday(&#123;1505745624, 9440&#125;, NULL)  = 0</span><br><span class="line">mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f11695fa000</span><br><span class="line"><span class="built_in">read</span>(11, <span class="string">"GET / HTTP/1.1\r\nHost: localhost:"</span>..., 8000) = 503</span><br><span class="line">gettimeofday(&#123;1505745624, 9617&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9679&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9739&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9771&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9803&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9836&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9870&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9920&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9947&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 9986&#125;, NULL)  = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 10038&#125;, NULL) = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 10198&#125;, NULL) = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 10257&#125;, NULL) = 0</span><br><span class="line"><span class="built_in">stat</span>(<span class="string">"/var/www/html/"</span>, &#123;st_mode=S_IFDIR|0755, st_size=4096, ...&#125;) = 0</span><br><span class="line">open(<span class="string">"/var/www/.htaccess"</span>, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)</span><br><span class="line">open(<span class="string">"/var/www/html/.htaccess"</span>, O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)</span><br><span class="line">mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f11695f8000</span><br><span class="line">mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f11695f6000</span><br><span class="line"><span class="built_in">stat</span>(<span class="string">"/var/www/html/index.php"</span>, &#123;st_mode=S_IFREG|0644, st_size=44, ...&#125;) = 0</span><br><span class="line">setitimer(ITIMER_PROF, &#123;it_interval=&#123;0, 0&#125;, it_value=&#123;30, 0&#125;&#125;, NULL) = 0</span><br><span class="line">rt_sigaction(SIGPROF, &#123;0x7f11652f0190, [PROF], SA_RESTORER|SA_RESTART, 0x7f11688f20e0&#125;, &#123;SIG_DFL, [], 0&#125;, 8) = 0</span><br><span class="line">rt_sigprocmask(SIG_UNBLOCK, [PROF], NULL, 8) = 0</span><br><span class="line">getcwd(<span class="string">"/var/www/html"</span>, 4095)           = 14</span><br><span class="line"><span class="built_in">chdir</span>(<span class="string">"/var/www/html"</span>)                  = 0</span><br><span class="line">lstat(<span class="string">"/var/www/html/index.php"</span>, &#123;st_mode=S_IFREG|0644, st_size=44, ...&#125;) = 0</span><br><span class="line">lstat(<span class="string">"/var/www/html"</span>, &#123;st_mode=S_IFDIR|0755, st_size=4096, ...&#125;) = 0</span><br><span class="line">lstat(<span class="string">"/var/www"</span>, &#123;st_mode=S_IFDIR|0755, st_size=4096, ...&#125;) = 0</span><br><span class="line">lstat(<span class="string">"/var"</span>, &#123;st_mode=S_IFDIR|0755, st_size=4096, ...&#125;) = 0</span><br><span class="line">open(<span class="string">"/var/www/html/index.php"</span>, O_RDONLY) = 12</span><br><span class="line">fstat(12, &#123;st_mode=S_IFREG|0644, st_size=44, ...&#125;) = 0</span><br><span class="line">fstat(12, &#123;st_mode=S_IFREG|0644, st_size=44, ...&#125;) = 0</span><br><span class="line">fstat(12, &#123;st_mode=S_IFREG|0644, st_size=44, ...&#125;) = 0</span><br><span class="line">mmap(NULL, 44, PROT_READ, MAP_SHARED, 12, 0) = 0x7f11695f5000</span><br><span class="line">munmap(0x7f11695f5000, 44)              = 0</span><br><span class="line">close(12)                               = 0</span><br><span class="line">open(<span class="string">"/etc/hosts"</span>, O_RDONLY|O_CLOEXEC)  = 12</span><br><span class="line">fstat(12, &#123;st_mode=S_IFREG|0644, st_size=174, ...&#125;) = 0</span><br><span class="line">mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f11695f5000</span><br><span class="line"><span class="built_in">read</span>(12, <span class="string">"127.0.0.1\tlocalhost\n::1\tlocalhos"</span>..., 4096) = 174</span><br><span class="line"><span class="built_in">read</span>(12, <span class="string">""</span>, 4096)                      = 0</span><br><span class="line">close(12)                               = 0</span><br><span class="line">munmap(0x7f11695f5000, 4096)            = 0</span><br><span class="line">open(<span class="string">"/etc/ld.so.cache"</span>, O_RDONLY|O_CLOEXEC) = 12</span><br><span class="line">fstat(12, &#123;st_mode=S_IFREG|0644, st_size=15545, ...&#125;) = 0</span><br><span class="line">mmap(NULL, 15545, PROT_READ, MAP_PRIVATE, 12, 0) = 0x7f11695f2000</span><br><span class="line">close(12)                               = 0</span><br><span class="line">access(<span class="string">"/etc/ld.so.nohwcap"</span>, F_OK)      = -1 ENOENT (No such file or directory)</span><br><span class="line">open(<span class="string">"/lib/x86_64-linux-gnu/libnss_dns.so.2"</span>, O_RDONLY|O_CLOEXEC) = 12</span><br><span class="line"><span class="built_in">read</span>(12, <span class="string">"\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0&gt;\0\1\0\0\0\0\21\0\0\0\0\0\0"</span>..., 832) = 832</span><br><span class="line">fstat(12, &#123;st_mode=S_IFREG|0644, st_size=22952, ...&#125;) = 0</span><br><span class="line">mmap(NULL, 2117896, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 12, 0) = 0x7f115f1fe000</span><br><span class="line">mprotect(0x7f115f203000, 2093056, PROT_NONE) = 0</span><br><span class="line">mmap(0x7f115f402000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 12, 0x4000) = 0x7f115f402000</span><br><span class="line">close(12)                               = 0</span><br><span class="line">mprotect(0x7f115f402000, 4096, PROT_READ) = 0</span><br><span class="line">munmap(0x7f11695f2000, 15545)           = 0</span><br><span class="line"><span class="built_in">stat</span>(<span class="string">"/etc/resolv.conf"</span>, &#123;st_mode=S_IFREG|0644, st_size=153, ...&#125;) = 0</span><br><span class="line">socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 12</span><br><span class="line">connect(12, &#123;sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr(<span class="string">"192.168.65.1"</span>)&#125;, 16) = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 15912&#125;, NULL) = 0</span><br><span class="line">poll([&#123;fd=12, events=POLLOUT&#125;], 1, 0)   = 1 ([&#123;fd=12, revents=POLLOUT&#125;])</span><br><span class="line">sendto(12, <span class="string">"\307\302\1\0\0\1\0\0\0\0\0\0\0018\0018\0018\0018\7in-addr\4arp"</span>..., 38, MSG_NOSIGNAL, NULL, 0) = 38</span><br><span class="line">poll([&#123;fd=12, events=POLLIN&#125;], 1, 5000) = 1 ([&#123;fd=12, revents=POLLIN&#125;])</span><br><span class="line">ioctl(12, FIONREAD, [82])               = 0</span><br><span class="line">recvfrom(12, <span class="string">"\307\302\201\200\0\1\0\1\0\0\0\0\0018\0018\0018\0018\7in-addr\4arp"</span>..., 1024, 0, &#123;sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr(<span class="string">"192.168.65.1"</span>)&#125;, [16]) = 82</span><br><span class="line">close(12)                               = 0</span><br><span class="line">mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f11695f4000</span><br><span class="line"><span class="built_in">chdir</span>(<span class="string">"/var/www/html"</span>)                  = 0</span><br><span class="line">setitimer(ITIMER_PROF, &#123;it_interval=&#123;0, 0&#125;, it_value=&#123;0, 0&#125;&#125;, NULL) = 0</span><br><span class="line">setitimer(ITIMER_PROF, &#123;it_interval=&#123;0, 0&#125;, it_value=&#123;0, 0&#125;&#125;, NULL) = 0</span><br><span class="line">mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f11695f2000</span><br><span class="line">mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f11695f0000</span><br><span class="line">gettimeofday(&#123;1505745624, 54797&#125;, NULL) = 0</span><br><span class="line"><span class="built_in">read</span>(11, 0x7f11695fa048, 8000)          = -1 EAGAIN (Resource temporarily unavailable)</span><br><span class="line">gettimeofday(&#123;1505745624, 56175&#125;, NULL) = 0</span><br><span class="line">writev(11, [&#123;<span class="string">"HTTP/1.1 200 OK\r\nDate: Mon, 18 S"</span>..., 230&#125;, &#123;<span class="string">"google-public-dns-a.google.com"</span>, 30&#125;], 2) = 260</span><br><span class="line">gettimeofday(&#123;1505745624, 57097&#125;, NULL) = 0</span><br><span class="line">write(8, <span class="string">"172.17.0.1 - - [18/Sep/2017:14:4"</span>..., 197) = 197</span><br><span class="line"><span class="built_in">times</span>(&#123;tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0&#125;) = 4298231454</span><br><span class="line">gettimeofday(&#123;1505745624, 58609&#125;, NULL) = 0</span><br><span class="line">gettimeofday(&#123;1505745624, 58935&#125;, NULL) = 0</span><br><span class="line">poll([&#123;fd=11, events=POLLIN&#125;], 1, 5000) = 0 (Timeout)</span><br><span class="line">gettimeofday(&#123;1505745629, 68368&#125;, NULL) = 0</span><br><span class="line">gettimeofday(&#123;1505745629, 68817&#125;, NULL) = 0</span><br><span class="line">gettimeofday(&#123;1505745629, 69508&#125;, NULL) = 0</span><br><span class="line">gettimeofday(&#123;1505745629, 70214&#125;, NULL) = 0</span><br><span class="line">shutdown(11, SHUT_WR)                   = 0</span><br><span class="line">poll([&#123;fd=11, events=POLLIN&#125;], 1, 2000) = 0 (Timeout)</span><br><span class="line">close(11)                               = 0</span><br><span class="line"><span class="built_in">read</span>(4, 0x7ffd1b7c265f, 1)              = -1 EAGAIN (Resource temporarily unavailable)</span><br><span class="line">gettimeofday(&#123;1505745631, 75664&#125;, NULL) = 0</span><br></pre></td></tr></table></figure><p>なんとなく TCPで53ポートで通信して、<br>HTTP/1.1でレスポンスが来ていることなどもわかる。<br>プロセスの動きをトレースすることでプログラムとカーネル側かの問題を切り分けることができる。</p><h3 id="gdbを使って原因を追う"><a href="#gdbを使って原因を追う" class="headerlink" title="gdbを使って原因を追う"></a>gdbを使って原因を追う</h3><p>最初に使った必ずエラーとなるphpを実行してSegmentation Faultを起こし、<br>coreファイルを出力してgdbでデバッグしてみる。</p><p>coreファイルはダンプした時点でのメモリ内容をそのまま記録しているので、そこから原因を特定できる。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># php moduleが読込まれていること確認</span></span><br><span class="line">php -m | grep example</span><br><span class="line">example</span><br><span class="line"></span><br><span class="line"><span class="comment"># Segmentation faultになる</span></span><br><span class="line">/usr/<span class="built_in">local</span>/bin/php /var/www/html/error.php</span><br><span class="line">Segmentation fault (core dumped)</span><br><span class="line"></span><br><span class="line"><span class="comment"># プロセス確認</span></span><br><span class="line"><span class="comment"># child processは1つにしておく</span></span><br><span class="line">pstree -p</span><br><span class="line">apache2(1)---apache2(16)</span><br><span class="line"></span><br><span class="line">ps -e -o ppid,pid,<span class="built_in">stat</span>,<span class="built_in">command</span> | grep apache2</span><br><span class="line">    0     1 Ss   apache2 -DFOREGROUND</span><br><span class="line">    1    16 S    apache2 -DFOREGROUND</span><br></pre></td></tr></table></figure><p>apacheなどのプロセスが長時間実行されている場合などは、<br>gcore(generate-core-file)でcoreダンプファイルを作成してみる。</p><p>gdbでプロセスにアタッチすることでデバッグはできるが、gdbがプロセス制御を奪うため、next, step, run, continueなどでプログラムを実行しないと停止している状態となるため稼働中のapcheのプロセスなどには使えない。</p><p>gcoreを使った場合はcoreダンプファイルを作成している間だけattachされるため、プログラムの停止を最小に止めることができる。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">gcore 16</span><br><span class="line"></span><br><span class="line">[Thread debugging using libthread_db enabled]</span><br><span class="line">Using host libthread_db library <span class="string">"/lib/x86_64-linux-gnu/libthread_db.so.1"</span>.</span><br><span class="line">0x00007f8754335190 <span class="keyword">in</span> __write_nocancel () at ../sysdeps/unix/syscall-template.S:84</span><br><span class="line">84../sysdeps/unix/syscall-template.S: No such file or directory.</span><br><span class="line">warning: target file /proc/15/cmdline contained unexpected null characters</span><br><span class="line">Saved corefile core.16</span><br><span class="line"></span><br><span class="line"><span class="comment"># core ファイルができている</span></span><br><span class="line">ls</span><br><span class="line">core.16</span><br><span class="line"></span><br><span class="line"><span class="comment"># coreファイルにアタッチする</span></span><br><span class="line">gdb /usr/sbin/apache2 -c core.16</span><br><span class="line">GNU gdb (Debian 7.12-6) 7.12.0.20161007-git</span><br><span class="line">Copyright (C) 2016 Free Software Foundation, Inc.</span><br><span class="line">License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;</span><br><span class="line">This is free software: you are free to change and redistribute it.</span><br><span class="line">There is NO WARRANTY, to the extent permitted by law.  Type <span class="string">"show copying"</span></span><br><span class="line">and <span class="string">"show warranty"</span> <span class="keyword">for</span> details.</span><br><span class="line">This GDB was configured as <span class="string">"x86_64-linux-gnu"</span>.</span><br><span class="line">Type <span class="string">"show configuration"</span> <span class="keyword">for</span> configuration details.</span><br><span class="line">For bug reporting instructions, please see:</span><br><span class="line">&lt;http://www.gnu.org/software/gdb/bugs/&gt;.</span><br><span class="line">Find the GDB manual and other documentation resources online at:</span><br><span class="line">&lt;http://www.gnu.org/software/gdb/documentation/&gt;.</span><br><span class="line">For <span class="built_in">help</span>, <span class="built_in">type</span> <span class="string">"help"</span>.</span><br><span class="line">Type <span class="string">"apropos word"</span> to search <span class="keyword">for</span> commands related to <span class="string">"word"</span>...</span><br><span class="line">Reading symbols from /usr/sbin/apache2...(no debugging symbols found)...<span class="keyword">done</span>.</span><br><span class="line">[New LWP 16]</span><br><span class="line">[Thread debugging using libthread_db enabled]</span><br><span class="line">Using host libthread_db library <span class="string">"/lib/x86_64-linux-gnu/libthread_db.so.1"</span>.</span><br><span class="line">Core was generated by `apache2<span class="string">'.</span></span><br><span class="line"><span class="string">#0  0x00007f9a03637690 in __poll_nocancel () at ../sysdeps/unix/syscall-template.S:84</span></span><br><span class="line"><span class="string">84../sysdeps/unix/syscall-template.S: No such file or directory.</span></span><br></pre></td></tr></table></figure><p>またSegmentation Faultが起きる場合はプロセスいなくなるので、あらかじめcoreファイルを出力するように設定しておくこともある。<br>この場合、エラーが発生すると所定のcoreファイルが出力される。</p><p>今回はphpで実際にSegmentation Faultを起こしてcoreファイルを吐き出す。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">curl http://localhost:8080/error.php</span><br><span class="line"></span><br><span class="line">ls /tmp</span><br><span class="line">core.cc0a7a572fde.apache2.1505406197</span><br></pre></td></tr></table></figure><p>gdbでcoreファイルをデバッグ</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">gdb /usr/sbin/apache2 -c core.cc0a7a572fde.apache2.1505406197</span><br><span class="line"></span><br><span class="line">GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1</span><br><span class="line">Copyright (C) 2014 Free Software Foundation, Inc.</span><br><span class="line">License GPLv3+: GNU GPL version 3 or later &lt;http://gnu.org/licenses/gpl.html&gt;</span><br><span class="line">This is free software: you are free to change and redistribute it.</span><br><span class="line">There is NO WARRANTY, to the extent permitted by law.  Type <span class="string">"show copying"</span></span><br><span class="line">and <span class="string">"show warranty"</span> <span class="keyword">for</span> details.</span><br><span class="line">This GDB was configured as <span class="string">"x86_64-linux-gnu"</span>.</span><br><span class="line">Type <span class="string">"show configuration"</span> <span class="keyword">for</span> configuration details.</span><br><span class="line">For bug reporting instructions, please see:</span><br><span class="line">&lt;http://www.gnu.org/software/gdb/bugs/&gt;.</span><br><span class="line">Find the GDB manual and other documentation resources online at:</span><br><span class="line">&lt;http://www.gnu.org/software/gdb/documentation/&gt;.</span><br><span class="line">For <span class="built_in">help</span>, <span class="built_in">type</span> <span class="string">"help"</span>.</span><br><span class="line">Type <span class="string">"apropos word"</span> to search <span class="keyword">for</span> commands related to <span class="string">"word"</span>...</span><br><span class="line">Reading symbols from /usr/sbin/apache2...(no debugging symbols found)...<span class="keyword">done</span>.</span><br><span class="line">[New LWP 31]</span><br><span class="line">[Thread debugging using libthread_db enabled]</span><br><span class="line">Using host libthread_db library <span class="string">"/lib/x86_64-linux-gnu/libthread_db.so.1"</span>.</span><br><span class="line">Core was generated by `apache2 -DFOREGROUND<span class="string">'.</span></span><br><span class="line"><span class="string">Program terminated with signal SIGSEGV, Segmentation fault.</span></span><br><span class="line"><span class="string">#0  zif_down_method (ht=0, return_value=0x7f70ac52b210, return_value_ptr=0x7f70ac4f9088, this_ptr=0x0, return_value_used=0) at /tmp/extensions/example/example.c:72</span></span><br><span class="line"><span class="string">72    strcpy(ptr, "down") ;</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># バックトレース</span></span><br><span class="line"><span class="string">(gdb) bt</span></span><br><span class="line"><span class="string">#0  zif_down_method (ht=0, return_value=0x7f70ac52b210, return_value_ptr=0x7f70ac4f9088, this_ptr=0x0, return_value_used=0) at /tmp/extensions/example/example.c:72</span></span><br><span class="line"><span class="string">#1  0x00007f70a8241173 in zend_do_fcall_common_helper_SPEC () from /usr/lib/apache2/modules/libphp5.so</span></span><br><span class="line"><span class="string">#2  0x00007f70a81ca360 in execute_ex () from /usr/lib/apache2/modules/libphp5.so</span></span><br><span class="line"><span class="string">#3  0x00007f70a81929d0 in zend_execute_scripts () from /usr/lib/apache2/modules/libphp5.so</span></span><br><span class="line"><span class="string">#4  0x00007f70a812dfc0 in php_execute_script () from /usr/lib/apache2/modules/libphp5.so</span></span><br><span class="line"><span class="string">#5  0x00007f70a82428ca in php_handler () from /usr/lib/apache2/modules/libphp5.so</span></span><br><span class="line"><span class="string">#6  0x000055798cdc3880 in ap_run_handler ()</span></span><br><span class="line"><span class="string">#7  0x000055798cdc3dc9 in ap_invoke_handler ()</span></span><br><span class="line"><span class="string">#8  0x000055798cdd9ca2 in ap_process_async_request ()</span></span><br><span class="line"><span class="string">#9  0x000055798cdd9e40 in ap_process_request ()</span></span><br><span class="line"><span class="string">#10 0x000055798cdd6742 in ?? ()</span></span><br><span class="line"><span class="string">#11 0x000055798cdcd130 in ap_run_process_connection ()</span></span><br><span class="line"><span class="string">#12 0x00007f70a8c757ba in ?? () from /usr/lib/apache2/modules/mod_mpm_prefork.so</span></span><br><span class="line"><span class="string">#13 0x00007f70a8c75a01 in ?? () from /usr/lib/apache2/modules/mod_mpm_prefork.so</span></span><br><span class="line"><span class="string">#14 0x00007f70a8c76667 in ?? () from /usr/lib/apache2/modules/mod_mpm_prefork.so</span></span><br><span class="line"><span class="string">#15 0x000055798cda784e in ap_run_mpm ()</span></span><br><span class="line"><span class="string">#16 0x000055798cda0673 in main ()</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># エラーが発生したファイル</span></span><br><span class="line"><span class="string">print (char *)executor_globals.active_op_array-&gt;filename</span></span><br><span class="line"><span class="string">$1 = 0x7f70ac529bc8 "/var/www/html/error.php"</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string"># エラーの発生した行数</span></span><br><span class="line"><span class="string">print executor_globals.current_execute_data.opline-&gt;lineno</span></span><br><span class="line"><span class="string">$2=2</span></span><br></pre></td></tr></table></figure><p>上記の内容から想定通りexample.c 72行目で落ちていることがわかる。</p><h3 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h3><p>普段アプリケーションレイヤーを開発している人からすると、だいたいこの辺かなという感じで問題点を追うことができるが、はっきりとして原因を掴むために、<br>straceやgdbなどで追求できるようにしておくことは良いことだと思われた。</p><p>低レイヤーでの開発している人にとっては目新しいことではないでしょうが…</p><h3 id="今回使ったサンプル"><a href="#今回使ったサンプル" class="headerlink" title="今回使ったサンプル"></a>今回使ったサンプル</h3><p><a href="https://github.com/kazu69/example-debug-apache-service-operating" target="_blank" rel="noopener">example-debug-apache-service-operating</a></p><h3 id="参考にしたページ"><a href="#参考にしたページ" class="headerlink" title="参考にしたページ"></a>参考にしたページ</h3><p><a href="https://techblog.yahoo.co.jp/tips/php/" target="_blank" rel="noopener">PHPでのデバッグ方法</a><br><a href="https://net-newbie.com/phpext/" target="_blank" rel="noopener">PHP Extension 開発入門</a><br><a href="https://httpd.apache.org/dev/debugging.html#gdb" target="_blank" rel="noopener">Apache HTTPD Debugging Guide - The Apache HTTP Server Project</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;稼働中のサービスにおいてapacheのプロセスが閾値を超える事象が頻発したり、&lt;br&gt;phpのバグを踏んだりした際に。なにが原因なのか究明のために行ったときの備忘録。&lt;/p&gt;
    
    </summary>
    
      <category term="web" scheme="http://blog.kazu69.net/categories/web/"/>
    
      <category term="memo" scheme="http://blog.kazu69.net/categories/web/memo/"/>
    
    
  </entry>
  
  <entry>
    <title>Goのメモリ管理を眺めてみた</title>
    <link href="http://blog.kazu69.net/2017/08/20/memory-management-go/"/>
    <id>http://blog.kazu69.net/2017/08/20/memory-management-go/</id>
    <published>2017-08-19T15:28:05.000Z</published>
    <updated>2017-08-27T23:23:17.000Z</updated>
    
    <content type="html"><![CDATA[<p>Golangでツールやアプリケーションを作ったりしてるが、<br>メモリ管理がどのようになっているのか気になったので眺めてみた。</p><a id="more"></a><h3 id="メモリ領域"><a href="#メモリ領域" class="headerlink" title="メモリ領域"></a>メモリ領域</h3><p>ざっくり復習のためメモリ領域についてまとめる。</p><p>メモリ領域は</p><table><thead><tr><th>メモリ領域</th><th>内容</th></tr></thead><tbody><tr><td>プログラム領域</td><td>マシン語に変換されたプログラムが格納される。この機械語を1行ずつ実行することで、プログラムが実行される。</td></tr><tr><td>静的領域</td><td>グローバル変数や静的変数などが入る。プログラム実行時にメモリ領域が確保される。メモリサイズは固定される。</td></tr><tr><td>ヒープ領域</td><td>塊という意味。プログラム実行時に確保されるが、実行時にしかメモリサイズがわからないので任意のサイズとなる。C言語のmalloc関数やnew演算子などで確保・管理される。新たにデータ猟奇が必要になると、未使用のメモリ領域を(飛び飛びになってしまうこともある)統合し、ノードに還元する。あらかじめ大きな確保する場合などに使われる。</td></tr><tr><td>スタック領域</td><td>積み重ねるという意味。一時変数、関数の引数、返り値などが一時的に格納される。メモリのサイズは固定で最後に積まれたメモリから最初に解放される(後入れ先だし: FILO)に則る。変数を定義しすぎたりするとメモリ領域を超えてオーバーフローする(スタックオーバーフロー)。現在のスコープで必要としている領域だけ確保できれば良い。</td></tr></tbody></table><p>ヒープで確保されたメモリが不要になった場合、プログラム側で開放できてないと猟奇が確保されたままになる(メモリリーク)。プログラムで開放するのではなく自動で開放する仕組みを持っているものもある。<br>この機能をガベージ・コレクトという。</p><h3 id="Golangのメモリ管理"><a href="#Golangのメモリ管理" class="headerlink" title="Golangのメモリ管理"></a>Golangのメモリ管理</h3><p>Golangのメモリ管理を見てみる。</p><p>goではビルド時に <a href="https://github.com/golang/go/wiki/CompilerOptimizations#escape-analysis-and-inlining" target="_blank" rel="noopener">-gcflags -mフラグを渡すことでコードを解析</a>できる。</p><p>ということで試してみる。</p><p>まずローカル変数を返すだけ</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span> <span class="title">int</span></span> &#123;</span><br><span class="line">v := <span class="number">1</span></span><br><span class="line"><span class="keyword">return</span> v</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:7: can inline Test</span><br><span class="line">./main.go:3: can inline main</span><br><span class="line">./main.go:4: inlining call to Test</span><br></pre></td></tr></table></figure><p>この場合はスタックを使っている。</p><p>つづいてメモリのアドレスを返してみる。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span> *<span class="title">int</span></span> &#123;</span><br><span class="line">v := <span class="number">1</span></span><br><span class="line"><span class="keyword">return</span> &amp;v</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:7: can inline Test</span><br><span class="line">./main.go:3: can inline main</span><br><span class="line">./main.go:4: inlining call to Test</span><br><span class="line">./main.go:4: main &amp;v does not escape</span><br><span class="line">./main.go:9: &amp;v escapes to heap</span><br><span class="line">./main.go:8: moved to heap: v</span><br></pre></td></tr></table></figure><p>この場合は、ヒープに置かれた。<br>アドレスからポインタの中身にアクセスする場合</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span> <span class="title">int</span></span> &#123;</span><br><span class="line">v := <span class="number">1</span></span><br><span class="line">p := &amp;v</span><br><span class="line"><span class="keyword">return</span> *p</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:7: can inline Test</span><br><span class="line">./main.go:3: can inline main</span><br><span class="line">./main.go:4: inlining call to Test</span><br><span class="line">./main.go:4: main &amp;v does not escape</span><br><span class="line">./main.go:9: Test &amp;v does not escape</span><br></pre></td></tr></table></figure><p>この場合はスタックに置かれる。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span> *<span class="title">int</span></span> &#123;</span><br><span class="line">v := <span class="built_in">new</span>(<span class="keyword">int</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> v</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:11: can inline Test</span><br><span class="line">./main.go:7: can inline main</span><br><span class="line">./main.go:8: inlining call to Test</span><br><span class="line">./main.go:8: main new(int) does not escape</span><br><span class="line">./main.go:12: new(int) escapes to heap</span><br></pre></td></tr></table></figure><p>ポインタを返すのでヒープに置かれる。<br>new演算子でメモリを確保してポインタを返さない場合</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span></span> &#123;</span><br><span class="line">v := <span class="built_in">new</span>(<span class="keyword">int</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> v == <span class="literal">nil</span> &#123;</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:11: can inline Test</span><br><span class="line">./main.go:7: can inline main</span><br><span class="line">./main.go:8: inlining call to Test</span><br><span class="line">./main.go:8: main new(int) does not escape</span><br><span class="line">./main.go:12: Test new(int) does not escape</span><br></pre></td></tr></table></figure><p>関数内でのみ使われるだけなので、スタックに置かれている。<br>ローカル変数でも他の関数に渡すと</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span></span> &#123;</span><br><span class="line">v := <span class="number">1</span></span><br><span class="line">fmt.Println(v)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:11: v escapes to heap</span><br><span class="line">./main.go:11: Test ... argument does not escape</span><br></pre></td></tr></table></figure><p>ヒープに置かれている。</p><p>続いて構造体を使ってみる。<br>シンプルに構造体を返すだけ</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Human <span class="keyword">struct</span> &#123;</span><br><span class="line">Name <span class="keyword">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span> <span class="title">Human</span></span> &#123;</span><br><span class="line">human := Human&#123;</span><br><span class="line">Name: <span class="string">"Bob"</span>,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> human</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:11: can inline Test</span><br><span class="line">./main.go:7: can inline main</span><br><span class="line">./main.go:8: inlining call to Test</span><br></pre></td></tr></table></figure><p>スタックに置かれている。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span> *<span class="title">Human</span></span> &#123;</span><br><span class="line">human := <span class="built_in">new</span>(Human)</span><br><span class="line">human.Name = <span class="string">"Bob"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> human</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:11: can inline Test</span><br><span class="line">./main.go:7: can inline main</span><br><span class="line">./main.go:8: inlining call to Test</span><br><span class="line">./main.go:8: main new(Human) does not escape</span><br><span class="line">./main.go:12: new(Human) escapes to heap</span><br></pre></td></tr></table></figure><p>ポインタを返すのでヒープに置かれている。<br>new演算子を使っても関数内で処理が終わってる場合</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span></span> &#123;</span><br><span class="line">human := <span class="built_in">new</span>(Human)</span><br><span class="line">human.Name = <span class="string">"Bob"</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:11: can inline Test</span><br><span class="line">./main.go:7: can inline main</span><br><span class="line">./main.go:8: inlining call to Test</span><br><span class="line">./main.go:8: main new(Human) does not escape</span><br><span class="line">./main.go:12: Test new(Human) does not escape</span><br></pre></td></tr></table></figure><p>変数はスタックに置かれている。<br>ポインタを保存した変数を返す場合</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">Test()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Test</span><span class="params">()</span> *<span class="title">Human</span></span> &#123;</span><br><span class="line">human := &amp;Human&#123;<span class="string">"Tom"</span>&#125;</span><br><span class="line"><span class="keyword">return</span> human</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># command-line-arguments</span></span><br><span class="line">./main.go:11: can inline Test</span><br><span class="line">./main.go:7: can inline main</span><br><span class="line">./main.go:8: inlining call to Test</span><br><span class="line">./main.go:8: main &amp;Human literal does not escape</span><br><span class="line">./main.go:12: &amp;Human literal escapes to heap</span><br></pre></td></tr></table></figure><p>ヒープに置かれる。</p><h3 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h3><p>Goの場合は、基本的にスタック領域を使うよう試みる。<br>ローカルの変数でも外部関数に渡されたり、ポインタを返す場合はヒープ領域が使われる。<br>これはローカル変数がスコープとなる関数の処理終了後も参照される可能性があるためらしい。</p><p>new演算子によるメモリの割り当ては必ずしもヒープを使うわけではない。</p><p>ということで、関数内で参照のみされる変数は実態を渡すことで、<br>アプリケーションとしてのパフォーマンスが良くなるということがわかった。</p><p>GCに関してはまた別で調べてみたい。</p><h3 id="参考にしたページ"><a href="#参考にしたページ" class="headerlink" title="参考にしたページ"></a>参考にしたページ</h3><p><a href="http://golang.jp/effective_go#allocation_new" target="_blank" rel="noopener">実践Go言語 - golang.jp</a><br><a href="http://ascii.jp/elem/000/001/496/1496211/" target="_blank" rel="noopener">Go言語のメモリ管理</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Golangでツールやアプリケーションを作ったりしてるが、&lt;br&gt;メモリ管理がどのようになっているのか気になったので眺めてみた。&lt;/p&gt;
    
    </summary>
    
      <category term="memo" scheme="http://blog.kazu69.net/categories/memo/"/>
    
    
  </entry>
  
  <entry>
    <title>React Roter v4 (RRv4) を使用した時の備忘録</title>
    <link href="http://blog.kazu69.net/2017/08/10/using-react-router-v4/"/>
    <id>http://blog.kazu69.net/2017/08/10/using-react-router-v4/</id>
    <published>2017-08-10T09:21:11.000Z</published>
    <updated>2017-08-27T23:21:40.000Z</updated>
    
    <content type="html"><![CDATA[<p>react app を使う際にルーティングとして利用する<a href="https://github.com/ReactTraining/react-router" target="_blank" rel="noopener">react-router</a>が<a href="https://github.com/ReactTraining/react-router/releases/tag/v4.0.0" target="_blank" rel="noopener">v4</a>になっていたので、ざっくりと触ってみた時の備忘録。</p><a id="more"></a><p>react-routerはv3と大きく違うため、正式なドキュメントを参考にしないとv4以前のものと混同してしまっておそらくはまってしうんじゃないかな。</p><h3 id="パッケージ"><a href="#パッケージ" class="headerlink" title="パッケージ"></a>パッケージ</h3><p>まず、routerは大きく3つのパッケージに分割されている。</p><table><thead><tr><th>package</th><th>description</th></tr></thead><tbody><tr><td>react-router</td><td>core package</td></tr><tr><td>react-router-dom</td><td>react-routerとdomをbinding</td></tr><tr><td>react-router-native</td><td>react-routerとreact-nativeをbinding</td></tr></tbody></table><p>今回はウェブアプリケーションを利用するため <code>react-router-dom</code> を使ってみた。</p><p>このpackageには <code>BrowserRouter</code> と <code>HashRouter</code> 、 <code>Link</code> 、<code>NavLink</code> コンポーネントがある。</p><p>アプリケーションに動的なURIが含まれる場合は <code>BrowserRouter</code>を利用する。こちらはbrowserの<a href="https://developer.mozilla.org/ja/docs/Web/API/History" target="_blank" rel="noopener">History API</a>をサポートしている。<br><code>HashRouter</code> はwindow.location.hashを使ってルーティングと同期している。<br><code>MemoryRouter</code> (URLは変更しないで履歴をメモリ管理するルーター) などcore packageでザポートされているrouterも利用は可能っぽい。</p><h3 id="routerコンポーネント作成"><a href="#routerコンポーネント作成" class="headerlink" title="routerコンポーネント作成"></a>routerコンポーネント作成</h3><p><code>BrowserRouter</code> を使う場合</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; BrowserRouter, Route &#125; <span class="keyword">from</span> <span class="string">'react-router-dom'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> App = <span class="function"><span class="params">()</span> =&gt;</span> (</span><br><span class="line">    &lt;div className=<span class="string">"layout"</span>&gt;</span><br><span class="line">        &lt;Header /&gt;</span><br><span class="line">        &lt;Main /&gt;</span><br><span class="line">        &lt;Footer /&gt;</span><br><span class="line">    &lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">)</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">ReactDOM.render（（</span></span><br><span class="line"><span class="regexp">  &lt;BrowserRouter&gt; </span></span><br><span class="line"><span class="regexp">    &lt;App /</span>&gt; </span><br><span class="line">  &lt;<span class="regexp">/ BrowserRouter&gt;</span></span><br><span class="line"><span class="regexp">), document.getElementById（ 'root'））</span></span><br></pre></td></tr></table></figure><h3 id="route-と-path"><a href="#route-と-path" class="headerlink" title="route と path"></a>route と path</h3><p>v4ではrouteで指定したpathと<code>localtion.pathname</code>が一致したcomponetをレンダリングする。</p><p>例えば以下ような場合に</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;Switch&gt;</span><br><span class="line">  &lt;Route　 path=<span class="string">'/'</span> component=&#123;HomeComponent&#125;/&gt;</span><br><span class="line">  &lt;Route path=<span class="string">'/hoge'</span> component=&#123;HogeComponent&#125;/&gt;</span><br><span class="line">  &lt;Route path=<span class="string">'/fuga'</span> component=&#123;FugaComponent&#125;/&gt;</span><br><span class="line">&lt;<span class="regexp">/Switch&gt;</span></span><br></pre></td></tr></table></figure><p><code>/hoge</code> へのアクセスで</p><ul><li><code>/</code> がマッチするため <code>HomeComponent</code> をレンダリング</li><li><code>/hoge</code> がマッチするため <code>HogeComponent</code> をレンダリング</li><li>マッチしないため <code>FugaComponent</code> はレンダリングされない</li></ul><p>となる。</p><p>pathnameなのでurlのパラメーターなどは関係しない。<br>パスのマッチングには <a href="https://github.com/pillarjs/path-to-regexp" target="_blank" rel="noopener">path-to-regexp</a> パッケージが使われている。</p><p>### routeコンポーネント作成</p><p>実際のルーティング部分を作成する。先ほどの例では <code>&lt;App /&gt;</code> コンポーネントの中のいずれかのコンポーネント内で実際のルーティング処理を記述することになる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;Switch&gt;</span><br><span class="line">  &lt;Route exact path=<span class="string">'/'</span> component=&#123;HomeComponent&#125;/&gt;</span><br><span class="line">  &lt;Route path=<span class="string">'/add'</span> component=&#123;AddComponent&#125;/&gt;</span><br><span class="line">  &lt;Route path=<span class="string">'/edit/:id'</span> component=&#123;EditComponent&#125;/&gt;</span><br><span class="line">&lt;<span class="regexp">/Switch&gt;</span></span><br></pre></td></tr></table></figure><p><code>exact</code> プロパティは <code>location.pathname</code> と値が正確に一致した場合のみの適用される。</p><h3 id="routeのレンダリング"><a href="#routeのレンダリング" class="headerlink" title="routeのレンダリング"></a>routeのレンダリング</h3><p>Routeコンポーネントのレンダリングには3つの方法がある。</p><h4 id="component"><a href="#component" class="headerlink" title="component"></a>component</h4><p>指定されたコンポーネントを<code>React.createElement</code>で作成するため、新しいコンポーネントを作成します。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;Route path=<span class="string">"/user/:username"</span> component=&#123;User&#125;/&gt;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> User = <span class="function">(<span class="params">&#123; match &#125;</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="xml"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello &#123;match.params.username&#125;!<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="render"><a href="#render" class="headerlink" title="render"></a>render</h4><p>comonentをマウントしないで、インラインでレンダリングすることができる。<br>ただし component が優先されるらしいので同じ <code>Route</code> で両者を使用するのは避けたほうがいい。</p><p><strong>レンダリングするコンポーネントへpropsを渡す時などはこちらを使うとやりやすい感じ。</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// convenient inline rendering</span></span><br><span class="line">&lt;Route path=<span class="string">"/home"</span> render=&#123;() =&gt; <span class="xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span>Home<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>&#125;/&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment">// wrapping/composing</span></span><br><span class="line"><span class="keyword">const</span> RouteWrapper = <span class="function">(<span class="params">&#123; component: Component, ...rest &#125;</span>) =&gt;</span> (</span><br><span class="line">  &lt;Route &#123;...rest&#125; render=&#123; props =&gt; (</span><br><span class="line">    &lt;SomeCompoment&gt;</span><br><span class="line">      &lt;Component &#123;...props&#125;/&gt;</span><br><span class="line">    &lt;<span class="regexp">/SomeCompoment&gt;</span></span><br><span class="line"><span class="regexp">  )&#125;/</span>&gt;</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h4 id="children"><a href="#children" class="headerlink" title="children"></a>children</h4><p>pathが条件に一致するかどうかをレンダリングしたい場合などは、<code>children</code> propを使う。<br>pathと一致するしないに関わらず常にレンダリングされる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&lt;ul&gt;</span><br><span class="line">  &lt;ItemLink to=<span class="string">"/somewhere"</span>/&gt;</span><br><span class="line">&lt;<span class="regexp">/ul&gt;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">const ItemLink = (&#123; to, ...rest &#125;) =&gt; (</span></span><br><span class="line"><span class="regexp">  &lt;Route path=&#123;to&#125; children=&#123;(&#123; match &#125;) =&gt; (</span></span><br><span class="line"><span class="regexp">    &lt;li className=&#123;match ? 'active' : ''&#125;&gt;</span></span><br><span class="line"><span class="regexp">      &lt;Link to=&#123;to&#125; &#123;...rest&#125;/</span>&gt;</span><br><span class="line">    &lt;<span class="regexp">/li&gt;</span></span><br><span class="line"><span class="regexp">  )&#125;/</span>&gt;</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="params"><a href="#params" class="headerlink" title="params"></a>params</h3><p>動的なURIの場合、パラメーターはコンポーネントで取得する必要がある。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;Switch&gt;</span><br><span class="line">  &lt;Route exact path=<span class="string">'/'</span> component=&#123;HomeComponent&#125;/&gt;</span><br><span class="line">  &lt;Route path=<span class="string">'/add'</span> component=&#123;AddComponent&#125;/&gt;</span><br><span class="line">  &lt;Route path=<span class="string">'/edit/:id'</span> component=&#123;EditComponent&#125;/&gt;</span><br><span class="line">&lt;<span class="regexp">/Switch&gt;</span></span><br></pre></td></tr></table></figure><p>上記のような場合、<code>/edit/:id</code> で <code>id</code> をコンポーネントで受け取る場合。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> EditComponent = <span class="function"><span class="params">props</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">const</span> id = <span class="built_in">parseInt</span>(props.match.params.id, <span class="number">10</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> (</span><br><span class="line">        &lt;div&gt;id: &#123;id&#125;&lt;<span class="regexp">/div&gt;</span></span><br><span class="line"><span class="regexp">    )</span></span><br><span class="line"><span class="regexp">&#125;</span></span><br></pre></td></tr></table></figure><p><code>props.match.params</code> で取得できる。</p><h3 id="Link"><a href="#Link" class="headerlink" title="Link"></a>Link</h3><p>最後にページ遷移を行う場合に<code>Link</code> コンポーネントを利用する。<br>これによりURLが更新されて、ページをリロードすることなくコンポーネントがレンダリングすることができる。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; Link &#125; <span class="keyword">from</span> <span class="string">'react-router-dom'</span></span><br><span class="line"></span><br><span class="line">&lt;Link to=<span class="string">"/hoge"</span>&gt;Hoge&lt;<span class="regexp">/Link&gt;</span></span><br><span class="line"><span class="regexp">&lt;Link to=&#123;&#123;</span></span><br><span class="line"><span class="regexp">  pathname: '/</span>search<span class="string">',</span></span><br><span class="line"><span class="string">  search: '</span>?q=string<span class="string">',</span></span><br><span class="line">  hash: '#hash-query',</span><br><span class="line">  state: &#123; <span class="attr">examplestate</span>: <span class="literal">true</span> &#125;</span><br><span class="line">&#125;&#125;/&gt;</span><br></pre></td></tr></table></figure><p>URLの変更を検知して、popstateを発火しているので、ページ移動のたびにコンポーネントを再描画できるっぽい。<br>(<a href="https://github.com/ReactTraining/history" target="_blank" rel="noopener">history.listen</a>を利用)</p><p>また、ナビゲーションなど該当ページのURLとリンク先URLがマッチした時にスタイルを適用するとかの処理が必要な場合は、<code>NavLink</code> コンポーネントを使うといい感じ。</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">import &#123; NavLink &#125; from <span class="string">'react-router-dom'</span></span><br><span class="line">&lt;NavLink to=<span class="string">"/hoge"</span>&gt;Hoge&lt;/NavLink&gt;</span><br><span class="line">&lt;NavLink</span><br><span class="line">  to=<span class="string">"/fuga"</span></span><br><span class="line">  activeClassName=<span class="string">"current-page"</span></span><br><span class="line">&gt;Fuga&lt;/NavLink&gt;</span><br></pre></td></tr></table></figure><h3 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h3><p>v3からワイルドに変更されていることで有名なんですが、<br>Roterが小さなコンポーネントで分割構成されていることで、Reactを使っていると、ルーティング周りは実装しやすい。</p><p>ルーター経由でコンポーネントにpropsを渡す時にrenderすることが最適解なのか未だにわかっていない感がある。</p><h3 id="作成したサンプル"><a href="#作成したサンプル" class="headerlink" title="作成したサンプル"></a>作成したサンプル</h3><p><a href="https://github.com/kazu69/react-redux-router-ssr-example" target="_blank" rel="noopener">react-redux-router-ssr-example</a></p><h3 id="参考にしたページ"><a href="#参考にしたページ" class="headerlink" title="参考にしたページ"></a>参考にしたページ</h3><p><a href="https://reacttraining.com/react-router/" target="_blank" rel="noopener">React Router: Declarative Routing for React.js</a><br><a href="https://github.com/ReactTraining/react-router" target="_blank" rel="noopener">ReactTraining/react-router</a></p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;react app を使う際にルーティングとして利用する&lt;a href=&quot;https://github.com/ReactTraining/react-router&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;react-router&lt;/a&gt;が&lt;a href=&quot;https://github.com/ReactTraining/react-router/releases/tag/v4.0.0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;v4&lt;/a&gt;になっていたので、ざっくりと触ってみた時の備忘録。&lt;/p&gt;
    
    </summary>
    
      <category term="memo" scheme="http://blog.kazu69.net/categories/memo/"/>
    
    
  </entry>
  
</feed>
