Nuxt.jsでOGPをSSRする。

バックエンドJavaでフロントがVue.jsのSPAなコンテンツを作っている時に、 APIで取ってきた値でOGPの値をバインドしてもSNSでクロールできず悲しい問題にあたってしまった。 ※こちらの記事でとてもわかりやすく現象がまとめられていた。

cly7796.net

色々な手段がありそうでない中、そもそもSSRすれば確実でしょうなってことで検証がてらトライ。
せっかくVue.jsで構成されているフロントだったので、Nuxt.jsをチョイスしました。

Nuxt.js - ユニバーサル Vue.js アプリケーション

検証環境準備

npm, vue-cli入れる辺りは省略… ※nodeがv6.* 以上でないとnpm run dev でコケるのでnodeのversion要確認。
Running in Node v5.12.0 doesn't work · Issue #1276 · nuxt/nuxt.js · GitHub

公式で用意されているsampleを使ってやります。

$ vue init nuxt/starter nuxxxt
? Project name nuxxxt
? Project description Nuxt.js project
? Author pdo99 pdo@example.jp

   vue-cli · Generated "nuxxxt".

   To get started:
     cd nuxxxt
     npm install # Or yarn
     npm run dev

↑通りやっていきましょう。
起動

$ npm run dev
DONE  Compiled successfully in 6390ms                                                                                                                23:44:28
OPEN  http://localhost:3000

f:id:pdo99:20170913013932p:plain 起動できました。

vue-metaをつかってメタ情報をvueから書き換える

Nuxt.jsではnuxt.config.jsにこのアプリケーション共通の設定が追加できる。とのこと
例を参考にとりあえずtitleは「page名 - site名」か、くらいなのでそこだけ修正。

  head: {
    title: '%s - nuxxxt',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: 'Nuxt.js project' }
    ],

ここで予めproperty: 'og:title'みたいなのは特に追加せずとも,vue側で

export default {
  head: {
    title: 'TEST',
    meta: [
      { property: 'og:title', content: 'Home page description' },
      { hid: 'description', name: 'description', content: 'Home page description' }
    ]
  }
}

といきなり突っ込んでみてもちゃんと

<head>
    <meta data-n-head="true" charset="utf-8"/>
    <meta data-n-head="true" name="viewport" content="width=device-width, initial-scale=1"/>
    <meta data-n-head="true" property="og:title" content="Home page description"/>
    <meta data-n-head="true" data-hid="description" name="description" content="Home page description"/>
    <title data-n-head="true">TEST</title>
...

と入れてくれていました。

コレでやるとpages/以下のvueすべてで↑みたいなのやらなきゃいけないので、head用のcomponentsを作って
:metaInfo=hogehogeして呼び出すのがいいかなっておもいました。

まとめ

ローンチ前に知りたかった。

…とは言え、自分含め他のメンバーが学習コストを割いてまで採択するかはなんとも言えない感じです。 個人的にはVuejsに慣れてきたこともあってVueVue!フー!って感じで楽しいんですけどね。 SSRは勿論vue-metaやルーティングがいい感じだったり、 特に設定なくファイルの変更でリローディングしてくれたりと気が利く感じはビシバシ感じました、そういうの好きです!

まあこんなところです、こちらからは以上です。

Vue-infinite-loadingがとても便利だった話

ウェッブフロントではありがちなスクロールして無限にコンテンツを読み込んでいくアレを作る時にあっというまに実現できたのでメモ。 ReactjsやろうやろうとしてたらVuejsになっていました(?)

peachscript.github.io

導入

npm install vue-infinite-loading --save

あとはimportしてレッツトライ

import Vue from 'vue'
...
import InfiniteLoading from 'vue-infinite-loading';

動かす

html

<infinite-loading :on-infinite="onInfinite" ref="infiniteLoading">
    <span slot="no-more">
        no more contents dorobow.
    </span>
</infinite-loading>

HTML内の下部にこんな感じに設置。
この要素の下部にロードするイベントのラインがあるようで、 スライダーだの位置だのとりあえずは気にになくても動くものが書けちゃうのはかなりいいなって思います。

js

window.onload = () => {
    vue = createVue();
};

const createVue = () => {
    return new Vue({
        el: '#list-listtt',
        data: {
            contents: [],
        },
        created() {
            //初期表示分はここで
            hogeAPI.GET_CONTENTS_INDEX()
                .then( res => {
                    this.contents = res
                }
        },
        components: {
            //↑でimportしたやつです
            InfiniteLoading,
        },
        methods: {
            //画面下部に行った時の処理はここに。
            onInfinite() {
                hogeAPI.GET_CONTENTS_MORE()
                    .then((res) => {
                        if(res.length > 0) {
                            this.articles.push(...res['posts']);
                            //記事読み込み終了する
                            this.$refs.infiniteLoading.$emit('$InfiniteLoading:loaded');
                        } else { 
                            //記事がなかったらロード自体終了する                          
                            this.$refs.infiniteLoading.$emit('$InfiniteLoading:complete');
                        }
                    })
                    .catch(( err) => {                    
                            this.$refs.infiniteLoading.$emit('$InfiniteLoading:complete');
                            console.log(err);
                    )}
            }
        },
    });
};

ここでは使っていませんがcompleteでイベントを終わらせたとしても、$InfiniteLoading:resetとすればまた下部のボーダーを超えると読み込みます。 こんな感じでロード終了時の出力を簡単にカスタムできたり、ロード中のくるくるもカスタマイズができます。

ここで使っているAPIのレスポンスがwordpressで作られたHTMLタグ混合のなかなかテキストだったけれど
v-htmlで簡単に表示できて感動したという話はまたいつか。

Vue.jsでボタン制御

Vue.js memo

computedに無限の可能性を感じている今日。

vue.js 試し書き

シンプルに取得したベーコン画像をバインドする。

spanタグとかで作られた枠内に表示するって時に:style="hogehoge として、 computedで'background-image': `url(${this.image})`を返すのもできた。

Riot.js(v3.6.0)でtableタグと格闘したmemo。

仕事でRiot.js、家ではReactというjavascriptにまみれてます。
Riot.jsのカスタムタグを使ってHTMLに動的な表示を実現させる稼業で今後も使いそうなのでmemo.

HTML側ではデータを元にtableを使ってこんな感じに表示させたい。 f:id:pdo99:20170712184938p:plain

至って普通のテーブルですが、左端のrowspanがちょっと工夫のいるところ。

Riot.jsを組み込む前のHTML

<table> 
      <thead> 
          <tr> 
              <th>会場</th> 
              <th>時間</th> 
              <th>研修会名</th> 
              <th>担当者</th> 
          </tr> 
      </thead> 
      <tbody> 
          <tr> 
              <td rowspan="2">****</th> 
              <td class="time">10:00~</td> 
              <td class="work"><a href="#">****</a></td> 
              <td class="name">****</td> 
         </tr> 
         <tr> 
             <td class="time">10:00~</td> 
             <td class="work"><a href="#">****</a></td> 
             <td class="name">****</td> 
        </tr> 
        <tr> 
            <td rowspan="1">¥¥¥¥</th> 
            <td class="time">10:00~</td> 
            <td class="work"><a href="#">¥¥¥¥</a></td> 
            <td class="name">¥¥¥¥</td>
        </tr>
    </tbody>
</table> 

だいたいデザイナさんからこんな感じのHTMLがもらえます。

Riot.js組み込み

表示するデータは会場単位のobjectの中にイベントの詳細が入っている形を作ります。 Javascriptで配列内の要素をキーに分割するmemo

<table class="training"> 
    <thead> 
        <tr> 
            <th>会場</th> 
            <th>時間</th> 
            <th>研修会名</th> 
            <th>担当者</th> 
        </tr> 
    </thead> 
    <tbody each={ schedule, key in schedules }> 
        <tr each={ val,i in schedule }> 
            <td if={ i < 1 } rowspan={ schedule.length }>{ key }</td> 
            <td class="time">{ val.time}~</td> 
            <td class="work"><a href="/hoge.html?hoge={ val.id }">{ val.displayName }</a></td> 
            <td class="name">{ val.staff }</td> 
        </tr> 
    </tbody> 
</table> 

会場単位のループをtbodyで行って、その中でイベント詳細のループをさらに回すという感じ。 rowspanはループカウントiで判定して表示を制御した。

参考

qiita.com

qiita.com

Javascriptで配列内の要素をキーに分割するmemo

レスポンスを自分で変更できないので、javascript側でレスポンスデータをブレイクしたmemo.

データ

  • before
[
{
    id:1
    venue:AAAA
    name: -
    date: -
    staff: - 
},
{
    id:2
    venue:AAAA
    name: -
    date: -
    staff: -
},
{
    id:3
    venue:CCCC
    name: -
    date: -
    staff: -
}
]

今回はvenue単位の連想配列にしたい次第。
* after

[
{
    AAAA: [
        {
            id:1
            venue:AAAA
            name: -
            date: -
            staff: - 
        },
        {
            id:2
            venue:AAAA
            name: -
            date: -
            staff: -
        }
    ], 
    CCCC: [
        {
            id:3
            venue:CCCC
            name: -
            date: -
            staff: -
        }
    ]

break

var splitData = {}; 
for (var i = 0; i < data.length; i++) { 
    if (splitData.hasOwnProperty(data[i]['venue'])) { 
        splitData[data[i]['venue']].push(data[i]); 
    } else { 
        splitData[data[i]['venue']] = new Array(data[i]); 
    } 
}

hasOwnPropertyでキーの値の存在確認しながら作るor追加しました。

参考

developer.mozilla.org

cookieの有効期限を今日中でsetする。

js-cookie使って当日有効のcookieをセットするmemo.

var now = new Date();
var limit = new Date( now.getFullYear(), now.getMonth(), now.getDate()+1);
now.setTime(now.getTime() + (limit - now));
Cookies.set(‘yomei’, '今日で私の命も尽きるでしょう。', { expires: now });

f:id:pdo99:20170629172405p:plain

ごくたまに使うかもしれないので、その日を心待ちにメモ。
こちらからは以上です。