<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Server Side Include &#8211; Mach3.laBlog</title>
	<atom:link href="https://blog.mach3.jp/tag/server-side-include/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.mach3.jp</link>
	<description></description>
	<lastBuildDate>Fri, 30 Oct 2015 09:03:49 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>
	<item>
		<title>SSIを手元の環境で再現してみる実験</title>
		<link>https://blog.mach3.jp/2015/10/30/easy-ssi.html</link>
		
		<dc:creator><![CDATA[mach3]]></dc:creator>
		<pubDate>Fri, 30 Oct 2015 09:03:49 +0000</pubDate>
				<category><![CDATA[Laboratory]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[Server]]></category>
		<category><![CDATA[Server Side Include]]></category>
		<category><![CDATA[SSI]]></category>
		<guid isPermaLink="false">http://blog.mach3.jp/?p=4149</guid>

					<description><![CDATA[SSI（サーバーサイドインクルード）を使用したページの制作時、 いちいち Apache を立ち上げて動作確認するのが面倒だと感じる事はないでしょうか。 今回は手元の環境で簡単な似非SSIを再現してみる実験の話です。 SS [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>SSI（サーバーサイドインクルード）を使用したページの制作時、 いちいち Apache を立ち上げて動作確認するのが面倒だと感じる事はないでしょうか。 今回は手元の環境で簡単な似非SSIを再現してみる実験の話です。</p>



<figure class="wp-block-image"><img decoding="async" src="https://lh3.googleusercontent.com/-_FFonSWfMyg/VjMxaQZ7O4I/AAAAAAAADA0/YB0H4qobLbA/s400-Ic42/2015-1030-00.png" alt="SSIを手元の環境で再現してみる実験"/></figure>



<p></p>



<span id="more-4149"></span>



<h2 class="wp-block-heading">SSIについて</h2>



<p>SSIそのものについては周知の事と思いますが、 HTMLのコメントノードを使ってサーバ側でコンテンツをインクルードする仕組みです。 例えば、HTML内に次のようなコメントを書いておきます。</p>


<pre class="wp-block-code"><span><code class="hljs language-xml"><span class="hljs-comment">&lt;!--#include virtual="/common/header.html"--&gt;</span>
</code></span></pre>


<p>そうすると、サーバ側で自動的に <strong>/common/header.html</strong> をその部分に読み込んだHTMLを吐き出してくれます。</p>



<p>Apache等のSSIに対応したWebサーバで閲覧すれば良いだけの話ですが、 その為にApacheを用意するのもいささか面倒に感じる事もあるでしょう。</p>



<h2 class="wp-block-heading">GruntでSSIしてみる</h2>



<p>まずは <a href="">Grunt</a> の <a href="">grunt-contrib-connect</a> を使ってSSIを再現してみます。 <strong>middleware</strong> オプションを使ってサーバ側で置換してしまいます。</p>


<pre class="wp-block-code"><span><code class="hljs language-css"><span class="hljs-selector-tag">grunt</span><span class="hljs-selector-class">.initConfig</span>({
    <span class="hljs-attribute">connect</span>: {
        dev: {
            options: {
                base: <span class="hljs-string">"html"</span>,
                port: <span class="hljs-number">8080</span>,
                keepalive: true,
                <span class="hljs-comment">/* ↓ これを使います */</span>
                middleware: <span class="hljs-built_in">function</span>(){ ... }
            }
        }
    }
});
</code></span></pre>


<p>まず必要なモジュールを読み込んでおきます。</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-keyword">var</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>),
    path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>),
    url = <span class="hljs-built_in">require</span>(<span class="hljs-string">"url"</span>);
</code></span></pre>


<p>middleware の実装は以下のようにしました。</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">middleware: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">connect, options, middlewares</span>)</span>{
    middlewares.unshift(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, res, next</span>)</span>{
        <span class="hljs-keyword">var</span> pattern, root, file, html;

        pattern = <span class="hljs-regexp">/&lt;!--#include\s+?(virtual|file)="(.+?)".*?--&gt;/g</span>;
        root = options.base&#91;<span class="hljs-number">0</span>];
        file = path.join(root, url.parse(req.url).pathname).replace(<span class="hljs-regexp">/\/$/</span>, <span class="hljs-string">"/index.html"</span>);

        <span class="hljs-keyword">if</span>(<span class="hljs-regexp">/\.s?html$/</span>.test(file) &amp;&amp; fs.existsSync(file)){
            html = fs.readFileSync(file, <span class="hljs-string">"utf-8"</span>).replace(pattern, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a, key, value</span>)</span>{
                <span class="hljs-keyword">var</span> inc = (key === <span class="hljs-string">"virtual"</span>) ? path.join(root, value)
                : path.join(file.replace(<span class="hljs-regexp">/&#91;^\/]+$/</span>, <span class="hljs-string">""</span>), value);
                <span class="hljs-keyword">return</span> fs.existsSync(inc) ? fs.readFileSync(inc) : <span class="hljs-string">""</span>;
            });
            <span class="hljs-keyword">return</span> res.end(html);
        }
        <span class="hljs-keyword">return</span> next();
    });
    <span class="hljs-keyword">return</span> middlewares;
}
</code></span></pre>


<ol class="wp-block-list">
<li>拡張子が <strong>.html</strong> あるいは <strong>.shtml</strong> の時だけ処理します</li>



<li>SSIのパターンを見つけたら、該当ファイルを読み込んで差し替えます</li>



<li>該当ファイルがなければなにもせずに静的ファイルを吐き出します</li>
</ol>



<p>試してませんが、Gulpでも同じ事が出来るでしょう。</p>



<p>なお、<strong>#include</strong> のみ処理し、<strong>#set</strong> や <strong>#echo</strong> 等の他のコマンドは全スルーしております。 これらも使いたい場合は <a href="https://github.com/67726e/node-ssi">node-ssi</a> 等を使ってmiddleware内でパースすると良いと思います。 （さすがに <strong>exec</strong> コマンドはサポートしてなさそうですが…）</p>



<p>SSIが見られるのは主に大きめのWebサイトのテンプレートですが、 経験上 <strong>#include</strong> 以外が使われているケースはほとんど見られません。</p>



<h2 class="wp-block-heading">フロントでSSIしてみる</h2>



<p>すでにSSIじゃなくなってますが、フロント側（JavaScript）でSSIを再現してみます。 むりやり名前をつけるならばFEI（フロントエンドインクルード）とかになるんでしょうか…。 とはいえ、XHRを使う都合上なんらかのサーバを通して確認する事になります。</p>



<p>先に申し上げておきますと、全くおすすめはできません。 特にJavaScriptも絡んだページ制作では、かなりの悪影響が予想されます。 当然本番で使用するのはもってのほかなので、ただの実験の記録として読み流してください。</p>



<h3 class="wp-block-heading">パターン1: innerHTMLで強引に</h3>



<p>ページ末尾でこのようなコードを実行してみました。</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">var</span> body = <span class="hljs-built_in">document</span>.getElementsByTagName(<span class="hljs-string">"body"</span>).item(<span class="hljs-number">0</span>);
    body.innerHTML.replace(
        <span class="hljs-regexp">/&lt;!--#include.+?(?:file|virtual)="(.+?)".*?--&gt;/g</span>,
        <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">key, value</span>)</span>{
            <span class="hljs-keyword">var</span> xhr = <span class="hljs-keyword">new</span> XMLHttpRequest();
            xhr.open(<span class="hljs-string">"GET"</span>, value);
            xhr.addEventListener(<span class="hljs-string">"readystatechange"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{
                <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.readyState === <span class="hljs-number">4</span> &amp;&amp; <span class="hljs-keyword">this</span>.status &lt; <span class="hljs-number">400</span>){
                    body.innerHTML = body.innerHTML.replace(key, <span class="hljs-keyword">this</span>.responseText);
                }
            });
            xhr.send();
        }
    );
}());
</code></span></pre>


<p>innerHTML にはコメントも含まれているので、それらを強引に文字列置換してしまって差し替えています。 やさしさがかけらもない、ひどいコードです。 このコードの最も大きなデメリット（いや、デメリットしかありませんが）は、 要素に結びつけたJavaScriptが全く動かなくなる点でしょう。</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript"><span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"my-button"</span>)
.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"click"</span>);
});
</code></span></pre>


<p>このような処理が書いてあったとしても、これは全く動きません。 innerHTML を書き換えると同時にDOMを破壊してしまい、上で参照している <strong>#my-button</strong> という要素とのつながりが断ち切られてしまう為です。</p>



<h3 class="wp-block-heading">パターン2: DOMインターフェースを使って</h3>



<p>ページの末尾で次のようなコードを実行します。</p>


<pre class="wp-block-code"><span><code class="hljs language-javascript">(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">var</span> each = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">list, callback</span>)</span>{
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Array</span>.prototype.forEach.call(list, callback);
    };

    <span class="hljs-keyword">var</span> parseSSI = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">nodes</span>)</span>{
        each(nodes, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">node</span>)</span>{
            <span class="hljs-keyword">var</span> match, xhr;

            <span class="hljs-keyword">if</span>(node.childNodes.length){ <span class="hljs-keyword">return</span> parseSSI(node.childNodes); }
            <span class="hljs-keyword">if</span>(node.nodeType !== <span class="hljs-number">8</span>){ <span class="hljs-keyword">return</span>; }

            match = node.nodeValue.match(<span class="hljs-regexp">/#include.+?(?:virtual|file)="(.+?)"/</span>);

            <span class="hljs-keyword">if</span>(match){
                xhr = <span class="hljs-keyword">new</span> XMLHttpRequest();
                xhr.open(<span class="hljs-string">"GET"</span>, match&#91;<span class="hljs-number">1</span>]);
                xhr.addEventListener(<span class="hljs-string">"readystatechange"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
                    <span class="hljs-keyword">if</span>(<span class="hljs-keyword">this</span>.readyState !== <span class="hljs-number">4</span> || <span class="hljs-keyword">this</span>.status &gt;= <span class="hljs-number">400</span>){ <span class="hljs-keyword">return</span>; }
                    <span class="hljs-keyword">var</span> tmp = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"div"</span>);
                    tmp.innerHTML = <span class="hljs-keyword">this</span>.responseText;
                    each(tmp.childNodes, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">child</span>)</span>{
                        node.parentNode.insertBefore(child, node);
                    });
                });
                xhr.send();
            }
        });
    };

    parseSSI(<span class="hljs-built_in">document</span>.getElementsByTagName(<span class="hljs-string">"body"</span>));
}());
</code></span></pre>


<p>body配下にぶら下がっている要素をすべて走査して、 コメントノード（<strong>nodeType === 8</strong>）だった場合にパターンと照合して処理します。</p>



<p>こちらのパターンはDOMを破壊しませんが、不完全なHTMLをモジュールとして読み込んだ場合に、それを補完してしまう欠点があります。 例えば次のような部品を読み込んだ場合。 （古き掲示板CGIのテンプレートを彷彿とさせる構成ですね）</p>


<pre class="wp-block-code"><span><code class="hljs language-xml"><span class="hljs-comment">&lt;!-- header.html --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
<span class="hljs-comment">&lt;!-- /header.html --&gt;</span>

ここにコンテンツがはいります。

<span class="hljs-comment">&lt;!-- footer.html --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>...<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
<span class="hljs-comment">&lt;!-- /footer.html --&gt;</span>
</code></span></pre>


<p>不完全な要素が補完されてしまう為、想定と異なる構成になってしまい、当然見た目も崩れてしまうでしょう。</p>



<p>このような使い方をしなければ、SSIを模した形のモジュールライブラリとして使い様があるか…？とも考えましたが、 DOMを全走査しなければならない為、コスト的にあまり良策とは言い難いですね。 改善策はなくもなさそうですが、「そこまでやる必要がどこにあるのか」と考えてしまったのでそこで放棄いたしました。</p>



<h2 class="wp-block-heading">まとめ</h2>



<p>SSIの環境が必要になるケースは今後もまだなくなりはしないと思います。 そういった案件に限って1ページのみの制作という事もよくある話なので、 出来るだけ手元の環境でさくっと再現してしまいたいですね。</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
