20200708のvue.jsに関する記事は13件です。

Vue.jsで条件に合わせて属性を変更する

例えばCSSフレームワークbulmaでボタンをクリックできなくする属性disabledを条件によって分岐したい場合でやってみる。
disabled === true のときはボタンが押せなくなり、falseのときは押下可能。
参考: https://bulma.io/documentation/elements/button/

<template>
  <div>
    <button class="button" v-bind="buttonAttr"></button>
  </div>
</template>

computedでif文や三項演算子で条件分岐してreturnすればよい。
例えばbtnFlgというdataを用意して、それのbool値で判断する場合。

export default {
  computed: {
    buttonAttr() {
      return this.btnFlg
        ? { disabled: true }
        : { disabled: false }
    }
  }

もちろん、よく使うclassやhrefなどどんな属性でもこんな風に書けるから結構便利。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jquery世代の元デザイナー同僚に捧ぐVue.js

人に読んで頂くシンプルな記事…ひとりよがりな長文…ぶつぶつ…
反省中のyagrushです。
こんばんは。

jquery世代の元デザイナー同僚に、
Vue.jsを紹介したらちょっと臆されてしまったので、この記事を捧げます。

Vue.js

Vue.js公式サイト

こちらのimage.pngを押すと流れる動画は分かりやすくて好きなのですが、元デザイナーにはイマイチなみたいでした…

Vue.js はあくまでJavaScriptオンリーです

元デザイナーはPHPやASP.NETコード混ぜこぜのHTMLをメンテナンスしてきた経験があってか、はたまたVue.jsの(jquery時代からすると)奇妙なJavaScriptコードのせいか、あとJSONを知らないそうで、ちょっと臆されてしまいました。

「いや~やっぱ無理かも。。。 どうせプログラミング要る(からデザイナーには無理)なんでしょ?」

いいえ。
Vue.jsはあくまでJavaScriptです。
HTMLの中で↓のようにリンクするだけですぐ使えます。

vue-test.html
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div>ここに何か値を挿入したい。</div>
</body>
</html>

(注)
このVue.js公式が用意してくれている直リンクは、勉強や試作でのみ使うよう言われています。
ビジネスなどで本格的に使うなら、一旦ダウンロードして自分のサーバーに置いて、それにリンクしましょう。

サンプルコード

例)div#sample-div の要素を "ダイナマイッ!" に書き換える。

vue-test.html
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="sample-div">{{ dainamai }}</div>
<script>
  myDiv = new Vue({
    el: "#sample-div",
    data: {
      "dainamai": "ダイナマイッ!", 
    }
  })
</script>
</body>
</html>

image.png

もう少し具体的に説明

    el: "#sample-div",

これにより <div id="sample-div"> を特定して、

    data: {
      "dainamai": "ダイナマイッ!", 
    }

<div id="sample-div"></div> の中の {{ dainamai }} の値を具体指示している。

デザイン(=CSS)を適用する

Vue.jsを使ったからって、デザインまでも新たな言語でなにか書かきゃいけないことはありません。
ID(#sample-div)でタグを指定して、CSSをぶち込めばいいだけです。

vue-test.html
<html>

  <head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
    #sample-div {
      color: white;
      background: linear-gradient(#0000ff, #EE9235);
    }
    </style>
  </head>

  <body>
    <div id="sample-div">{{ dainamai }}</div>
    <script>
      myDiv = new Vue({
        el: "#sample-div",
        data: {
          "dainamai": "ダイナマイッ!", 
        }
      })
    </script>
  </body>

</html>

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsのイメージが全く浮かばない方に超超簡単な紹介をする

人に読んで頂くシンプルな記事…ひとりよがりな長文…ぶつぶつ…
反省中のyagrushです。
こんにちは。

jquery世代の元デザイナー同僚に、
Vue.jsを紹介したらちょっと臆されてしまったので、この記事を捧げます。

Vue.js

Vue.js公式サイト

こちらのimage.pngを押すと流れる動画は分かりやすくて好きなのですが、イマイチ響かないみたいでした。

Vue.js はあくまでJavaScriptオンリーです

元デザイナーはPHPやASP.NETコード混ぜこぜのHTMLをメンテナンスしてきた経験があってか、はたまたVue.jsの(jquery時代からすると)奇妙なJavaScriptコードのせいか、あとJSONを知らないそうで、ちょっと臆されてしまいました。

「いや~やっぱ無理かも。。。 どうせプログラミング要る(からデザイナーには無理)なんでしょ?」

いいえ。
Vue.jsはあくまでJavaScriptです。
HTMLの中で↓のようにリンクするだけですぐ使えます。

vue-test.html
<html>
  <head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div>ここに何か値を挿入したい。</div>
  </body>
</html>

(注)
このVue.js公式が用意してくれている直リンクは、勉強や試作でのみ使うよう言われています。
ビジネスなどで本格的に使うなら、一旦ダウンロードして自分のサーバーに置いて、それにリンクしましょう。

サンプルコード

例)div#sample-div の要素を "ダイナマイッ!" に書き換える。

vue-test.html
<html>
  <head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  </head>
  <body>
    <div id="sample-div">{{ dainamai }}</div>
    <script>
      myDiv = new Vue({
        el: "#sample-div",
        data: {
          "dainamai": "ダイナマイッ!", 
        }
      })
    </script>
  </body>
</html>

image.png

もう少し具体的に説明

    el: "#sample-div",

これにより <div id="sample-div"> を特定して、

    data: {
      "dainamai": "ダイナマイッ!", 
    }

<div id="sample-div"></div> の中の {{ dainamai }} の値を具体指示している。

デザイン(=CSS)を適用する

Vue.jsを使ったからって、デザインまでも新たな言語でなにか書かきゃいけないことはありません。
ID(#sample-div)でタグを指定して、CSSをぶち込めばいいだけです。

vue-test.html
<html>
  <head>
    <meta charset="UTF-8">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      #sample-div {
        color: white;
        background: linear-gradient(#0000ff, #EE9235);
      }
    </style>
  </head>
  <body>
    <div id="sample-div">{{ dainamai }}</div>
    <script>
      myDiv = new Vue({
        el: "#sample-div",
        data: {
          "dainamai": "ダイナマイッ!", 
        }
      })
    </script>
  </body>
</html>

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Speech to Text APIを使ってブラウザでリアルタイム文字起こしする

TD;TL

  • Google Speech to Text APIとWeb Speech APIを併用することで実現する
  • 音声検出のみWeb Speech APIを使い、文字起こし自体はGoogle Speech to Text APIを使うことで、ブラウザ文字起こしにおいてリアルタイム感と精度の高さを両立する

発端

現在開発中のプロダクトの中で、Speech to Textの仕組みを導入するために様々な方法を調べていました。
オンライン会議中の会話を文字起こししたり、アジェンダや議事録を一括で管理できるサービス「Telelogger」というサービスなのですが、コアとなる機能が会議中の会話の文字起こしです。
サービスはWebアプリケーションとして提供するため、ブラウザでの文字起こしを想定しています。
対象ブラウザをGoogle Chromeに絞った上で、最初はWeb Speech APIを試していましたが、ところどころ文字変換の精度がきになる箇所があり、さらに精度高く文字起こしできるサービスを諸々試していた中で、Gogole Speech to Text APIにたどり着きました。

個人的にWeb Speech APIとGogole Speech to Text API(以下GoogleAPI)を比較した図が下記になります。
スクリーンショット 2020-07-07 9.07.47.png

あくまで個人的な主観ですが、GoogleAPIの方がWebSpeechAPIよりも文字起こしの精度が高く感じます。
また、GoogleAPIはREST形式なので、どのプラットフォームでも使うことができます。
そしてgRPC版であれば、GoogleAPIもストリーミング入力に対応しているのでリアルタイム性ある文字起こしができそうなのですが、gRPC版は現状ブラウザには対応していないようです。
ブラウザでのリアルタイム性についてはWebSpeechAPIに軍配が上がります。

GoogleAPIを使ったブラウザでのリアルタイム文字起こし

どうにかGoogleAPIの文字起こし精度を使いつつ、ブラウザでリアルタイムな文字起こしを行いたいと色々と試行錯誤した結果たどり着いたやり方が、音声検出(Voice Activity Detection)を用いるやり方です。
GoogleAPIはREST形式であり、音声ファイルをリクエストすると文字起こしテキストがレスポンスされます。
通常多いのは、「録音開始」ボタンを押して録音し、「録音終了」ボタンを押した際に録音終了しつつ、作成された音声ファイルをAPIに投げ、受け取ったテキストデータを表示する、というやり方だと思います。

スクリーンショット 2020-07-07 9.37.33.png

この録音開始・録音終了のトリガーを、ボタンではなく音声検出イベントをトリガーにすることで、リアルタイム感ある文字起こしを実現します。

スクリーンショット 2020-07-07 9.40.35.png

voice-activity-detectionというライブラリがあるため、これを使いつつ文字起こししてみた結果が下記になります。

スクリーンショット 2020-07-07 9.45.35.png

上が原文、下の枠の中に書かれているのが実際に文字起こしされたテキストになるのですが、最初の文字だけ文字起こしがうまくいっていません。
これは、音声検出が発話を始めてから2~3ワード後に開始されているからです。「OK,Google」のようなウェイクワードがないため、人間の発話タイミングに音声検出が追いついていないため、このような現象がおきます。

Web Speech APIとの併用でうまくいった!

voice-activity-detectionライブラリの中には、音声検出のための様々なパラメータがあるため、それらをいじりながら試行錯誤していたのですが、ふと「音声検出の部分はWeb Speech APIが優れているわけだし、そこと併用できないか?」という考えに思い至り、試しに組んでみたらうまくいきました!

スクリーンショット 2020-07-08 9.33.09.png

上記の通り、話し始めもバッチリ文字起こしできています。
実装したソースコードは下記のようなものになります。(今回はVueで書いてます)

SpeechToText.vue
<template>
  <div class="wrapper">
    <!-- header -->
    <header class="header">
      <button outlined @click="mediaRecordStart">音声認識開始</button>
      <button outlined @click="stopDetection">音声認識終了</button>
    </header>
    <!-- body -->
  </div>
</template>
<script>
export default {
  asyncData() {
    return {
      recognition: {},
      recorder: null,
      status: 'init',
      stream: null
    };
  },
  methods: {
    mediaRecordStart () {
      const _this = this
      navigator.mediaDevices.getUserMedia({ audio: {
        echoCancellation: true,
        echoCancellationType: 'system',
        noiseSuppression: false
      }})
        .then(stream => {
          _this.stream = stream
          this.recorder = new MediaRecorder(_this.stream)
          this.RunSpeechRecog(_this.stream)
          this.recorder.addEventListener('dataavailable', e => {
            this.audioData.push(e.data)
          })
          this.recorder.addEventListener('stop', () => {
            const audioBlob = new Blob(this.audioData)
            let reader = new FileReader()
            // reader.readAsDataURL(audioBlob)
            reader.onload = function () {
              let result = new Uint8Array(reader.result)
              let file = _this.arrayBufferToBase64(result)

              // 文字起こし
              const content = {
                'config': {
                  'language_code': 'ja-JP',
                  'sample_rate_hertz': 44100,
                  'encoding': 'MP3',
                  'enable_automatic_punctuation': true,
                  'model': 'default',
                  'enableWordTimeOffsets': false
                },
                //'audio': {'uri': file}
                'audio': {'content': file}
              }
              const apiKey = 'YOUR-API-KEY'

              fetch('https://speech.googleapis.com/v1p1beta1/speech:recognize?key='+apiKey, {
                method: 'POST',
                headers: {
                  'Content-Type': 'application/json; charset=utf-8'
                },
                body: JSON.stringify(content)
              }).then(function (response) {
                return response.text()
              }).then(function (text) {
                const resultJson = JSON.parse(text)
                if (resultJson.results[0].alternatives[0].transcript !== '') {
                  console.log('result')
                  console.log(resultJson.results[0].alternatives[0].transcript)
                }
              }).catch(function (error) {
                console.log('error1')
                console.log(error)
              })
            }
            reader.readAsArrayBuffer(audioBlob)
          })
          this.status = 'ready'
        })
    },
    RunSpeechRecog (stream) {
      var flagSpeech = 0
      var _this = this
      // window.SpeechRecognition = window.SpeechRecognition || webkitSpeechRecognition
      this.recognition = new window.webkitSpeechRecognition(stream)
      this.recognition.lang = 'ja'
      this.recognition.interimResults = true
      this.recognition.continuous = true

      this.recognition.onaudiostart = function () {
        // console.log('認識中')
        _this.startRecording()
      }
      this.recognition.onnomatch = function () {
        // console.log('もう一度試してください')
      }
      this.recognition.onerror = function () {
        // console.log('エラー')
        if (flagSpeech === 0) {
          _this.RunSpeechRecog(stream)
        }
      }
      this.recognition.onsoundend = function () {
        // console.log('停止中')
        _this.RunSpeechRecog(stream)
      }

      this.recognition.onresult = function (event) {
        var results = event.results
        for (var i = event.resultIndex; i < results.length; i++) {
          if (results[i].isFinal) {
            _this.stopRecording()
            _this.RunSpeechRecog(stream)
          } else {
            // console.log('途中経過' + results[i][0].transcript)
            flagSpeech = 1
          }
        }
      }
      flagSpeech = 0
      this.recognition.start()
    },
    stopDetection() {
      this.stopVoiceDetection()
      this.destroyRecording()
    },
    stopVoiceDetection () {
      this.recognition.stop()
    },
    startRecording () {
      this.status = 'recording'
      this.audioData = []
      this.recorder.start()
    },
    stopRecording () {
      this.recorder.stop()
      this.status = 'ready'
    },
    destroyRecording () {
      this.stream.getTracks()[0].stop()
    },
    arrayBufferToBase64 (buffer) {
      let binary = ''
      let bytes = new Float32Array(buffer)
      let len = bytes.byteLength
      for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i])
      }
      return window.btoa(binary)
    }
  }
}
</script>

↑雑モックなのでところどころエラー出ますが許してください・・・(改善コメント募集です)
ポイントは、WebSpeechAPIのonaudiostartメソッドを利用する部分です。
WebSpeechAPIにはもう一つ、onsoundstartというメソッドがありますが、こちらは音声が鳴った後、それが認識可能な音声かどうかAPI側で判断して発火するもののようで、こっちだと最初の音声認識のタイムラグは改善されませんでした。
onaudiostartであれば、音声が発せられた瞬間を検知し発火できるので、話し始めの録音に間に合います。

まとめ

これでリアルタイムかつ高精度な文字起こしを、ブラウザで実現できそうです。
ただWebSpeechAPIを使っている以上、今の所Chromeのみ対応となる点は注意です。
昨今オンラインでのやりとりが増えてきている中で、文字起こしの技術はそれを手助けするのに相性のいい技術ですので、今後こういった技術がもっと世の中に浸透するといいですね!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vuex StoreのModule分割

はじめに

Vuex便利ですよね!Vueでサービス作る上で欠かせない機能だと思います。
ただ規模が大きくなるにつれ肥大化が否めません。
そこでstoreをmoduleという単位で分割する方法、さらにそれらを別ファイル管理する方法を記載します。

module管理

まずmoduleで区切っていないストアの例を見てみましょう

src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    userId: '',
    username: '',
    introduction: '',
    postArticles: [],
    notices: [],
    articles: [],
    articleId: '',
    popularArticles: [],
    searchAricles: [],
    followUsers: [],
    star: 0,
    help: [],
    topCarousel: [],
  },
  mutations: {
    saveUserInfo(state, userInfo) {
       ...
    },
    saveArticles(state, articles) {
       ...
    },
    saveStar(state, star) {
       ...
    },
    ...
  },
  actions: {
    loadUserInfo(context, authInfo) {
       ...
    },
    loadArticles(context) {
       ...
    },
    postArticles(context) {
       ...
    }
    ...
  },
  modules: {
  }
})

いかがでしょうか?
現状ユーザー情報, 記事情報, その他資材情報が入り乱れている状況です。
まだなんとなく追えますが、さらに機能追加された場合確実に訳が分からなくなります。

そこでModule区分です、上記ストアを機能事に分離できます。
分離することでどこになんの機能があるのか一発でわかるためコードのみやすさが格段に上がります。
また1ファイルあたりのコード量が増える懸念もあるので、Moduleごとにファイルを分けることもオススメです。

以下のような感じです

// ファイル構成
src/store/index.js // modulesの読み込み
src/store/modules //modulesの設置
src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import { user, article, material } from './modules';

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    user,
    article,
    material,
  },
});
src/store/modules/index.js
import user from './user';
import article from './article';
import material from './material';

export {
  user,
  article,
  material,
};
src/store/modules/user.js
export default {
  namespaced: true,
  state: {
    userId: '',
    username: '',
    introduction: '',
    followUsers: [],
    star: 0,
  },
  mutations: {
   ...
  },
  actions: {
   ...
  },
};
src/store/modules/article.js
export default {
  namespaced: true,
  state: {
    postArticles: [],
    articles: [],
    articleId: '',
    popularArticles: [],
    searchAricles: [],
  },
  mutations: {
   ...
  },
  actions: {
   ...
  },
};
src/store/modules/material.js
export default {
  namespaced: true,
  state: {
    help: [],
    topCarousel: [],
    notices: [],
  },
  mutations: {
   ...
  },
  actions: {
   ...
  },
};

このように区分することで今後のストアの増加にも対応できます。
現時点で必要なさそうでも導入は早めが楽です、是非導入のご検討いかがでしょうか?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【第1回_もくもく会】Vue.jsを学習する_インストールや準備

Vue.js

JavaScriptのフレームワークです。
もくもく会をしていたので、環境を整えて学習していきます。

Vue.jsのいいとこは?

vue.js公式サイトに記載のある通り、「親しみやすく」、「融通が効いて」、「高性能」とあります。
フロントエンド開発においてデータを表示と分離して開発する事が行えるので開発しやすいのがメリットです。
また、vueは様々なライブラリが充実しています。
昨今では、気軽にjQueryの実装ができるために、開発者に依存した書き方で、分かりづらい面があります。
vue.jsなら、書き方の決まりによって、自分も他者も分かり易くなります。

NPMを用いたVueのインストール(Mac OS)

NPM(Node Package Manager)を利用すると、とても便利です。
NPMでVueをインストールするのに、Node.jsがインストールされてる必要があります。

Node.jsのバージョンを確認

$ node -v

もしNode.jsがインストールされていなければ....

Homebrewのインストールをしてから、インストールをしましょう。
Homebrewがインストールされていたら、nodebrewからインストールしてください。

(インストールしてなければ)Homebrewのインストール

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Homebrewのバージョンを確認

$ brew -v

brewコマンドが使えていなければ、Homebrewはインストールできていないです。

Node.jsのバージョン管理のためにnodebrewをインストール

$ brew install nodebrew

nodebrewコマンドで確認

$ nodebrew -v

Node.js安定版のインストール

node.jsをインストールする時、最新版や安定版など、バージョン指定でインストールが可能ですが、ここでは安定ばんのインストールを行います。

nodebrew install-binary stable

Pathを通す(nodeコマンドが使えるようにします)

$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile

vue-cliのインストール

Vue.jsを使う環境を準備するためのコマンドラインインターフェースをインストールします。

npm install -g @vue/cli

プロジェクトを作成する

vue create practice-app

vs codeを立ち上げる

vue create my-project

サーバーを立ち上げる

npm run serve

ローカルで見れるようになります。
http://localhost:8080/
image.png

次回以降、学習していきます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【第1回_もくもく会_超初心者】Vue.jsを学習する_インストールや準備

Vue.js

JavaScriptのフレームワークです。
もくもく会をしていたので、環境を整えて学習していきます。

Vue.jsのいいとこは?

vue.js公式サイトに記載のある通り、「親しみやすく」、「融通が効いて」、「高性能」とあります。まさにこれがいいとこだと思います。
フロントエンド開発においてデータを表示と分離して開発する事が行えるので開発しやすいことはメリットです。

また、vueは様々なライブラリが充実しています。
昨今では、気軽にjQueryの実装ができるために、開発者に依存した書き方で、物によってはコードが分かりづらい時があります。
vue.jsなら、書き方の決まりによって、自分も他者も分かり易くなります。

環境の準備をしたいと思います。

NPMを用いたVueのインストール(Mac OS)

NPM(Node Package Manager)を利用すると、とても便利です。
NPMでVueをインストールするのに、Node.jsがインストールされてる必要があります。

Node.jsのバージョンを確認

$ node -v

もしNode.jsがインストールされていなければ....

Homebrewのインストールをしてから、インストールをしましょう。
Homebrewがインストールされていたら、nodebrewからインストールしてください。

(インストールしてなければ)Homebrewのインストール

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Homebrewのバージョンを確認

$ brew -v

brewコマンドが使えていなければ、Homebrewはインストールできていないです。

Node.jsのバージョン管理のためにnodebrewをインストール

$ brew install nodebrew

nodebrewコマンドで確認

$ nodebrew -v

Node.js安定版のインストール

node.jsをインストールする時、最新版や安定版など、バージョン指定でインストールが可能ですが、ここでは安定版のインストールを行います。

nodebrew install-binary stable

Pathを通す(nodeコマンドが使えるようにします)

$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile

vue-cliのインストール

Vue.jsを使う環境を準備するためのコマンドラインインターフェースをインストールします。

npm install -g @vue/cli

プロジェクトを作成する

vue create practice-app

vs codeを立ち上げる

code .

サーバーを立ち上げる

npm run serve

ローカルで見れるようになります。
http://localhost:8080/
image.png

次回以降、Vue.jsの中身について学習していきます(>_<)!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue Router 直接URLでアクセスするとエラーが起きる

Vue.jsを使っていて、通常の画面遷移のルーティングは問題無いがURLを直打ちすると

Uncaught SyntaxError: Unexpected token <

というエラーになってしまう現象が起きた。

:bomb: index.html内のパスの設定が原因

これを

<script type="text/javascript" src="js/app.js"></script>

このように変えてやるだけ

<script type="text/javascript" src="/js/app.js"></script>

上のパターンだと直接URLを叩いた時とリンクから画面遷移した時で見ている場所が変わってしまう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gridsome,WP REST APIでビルド時に401エラーが出る

概要

GridsomeでWP REST APIをソースとして使おうとしたら躓いたので備忘録

起こったこと

ビルド時に401が返ってきてしまいコンテンツが取得できない

gridsome.config.js
module.exports = {
  siteName: "Gridsome",
  plugins: [
    {
      use: "@gridsome/source-wordpress",
      options: {
        baseUrl: "https://*****.com", // required
        typeName: "blog",
        apiBase: "wp-json",
        perPage: 10,
        concurrent: 1,
      },
    },
  ],
  templates: {
    BlogPost: "/:year/:month/:day",
  },
};
$gridsome develop
Gridsome v0.7.18

Initializing plugins...
Loading data from https://*****.com
Error: Status 401 - wp/v2/types
Error: Status 401 - wp/v2/users
Error: Status 401 - wp/v2/taxonomies
Load sources - 1.66s
︙

原因

WordPress側でREST APIのアクセスに制限がかかっていた

解決策

REST APIへのアクセス設定をデフォルトアクセスに設定する

WordPressのダッシュボードからセキュリティに移動

スクリーンショット 2020-07-08 18.34.06.png

WordPressの微調整の項目の設定からREST APIをデフォルトアクセスに変更

スクリーンショット 2020-07-08 18.35.55.png

再度ビルド

$gridsome develop
Gridsome v0.7.18

Initializing plugins...
Loading data from https://*****.com
︙
Load sources - 295.88s
Create GraphQL schema - 1.01s
Create pages and templates - 0.15s
Generate temporary code - 0.08s
Bootstrap finish - 298.35s


 DONE  Compiled successfully in 4009ms              18:42:34


  Site running at:                                         
  - Local:                 http://localhost:8080/          
  - Network:               http://192.168.10.8:8080/       

  Explore GraphQL data at: http://localhost:8080/___explore

今度はエラーを吐かずにビルドができました
これでWP REST APIからコンテンツを取得することができるようになりました

終わりに

本来であれば認証が通るようにするのが正しいのですが、今回は取得の確認を行いたかったのでWordPress側の設定を変更しました
おそらくgridsome.config.jsにheaderとしてBearerを設定するのがセキュリティ的に正解……のハズ

参考

WP REST API 認証 このページ崩れてるのって僕だけですかね?
Rest API restriction
Restrict WordPress REST API Access

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue】ローカル環境でのポート番号の変更

環境

npm create プロジェクト名で作成したプロジェクト

背景

複数のプロジェクトをローカル環境で確認したかったが、デフォルトポート番号で被ってしまい変更したかった

結論

config/index.jsを用意し、以下のコードを記述する。

index.js
module.exports = {
    devServer: {
        port: 8888,
        disableHostCheck: true,
    },
};
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsでページ離脱前に確認ダイアログを出す

編集中のものがあったりして、保存されませんが、続けますか?という確認をするあれです。
Nuxt.jsで書きます。

ブラウザを閉じたり、リロードしたりしたとき

export default {
  created () {
    window.addEventListener("beforeunload", this.confirmSave);
  },
  destroyed () {
    window.removeEventListener("beforeunload", this.confirmSave);
  },
  methods: {
    confirmSave (event) {
      event.returnValue = "編集中のものは保存されませんが、よろしいですか?";
    },
  }
}

もし、編集されている時だけ出す、のような分岐をする場合は、

    confirmSave (event) {
      if (this.formChanged){
        event.returnValue = "編集中のものは保存されませんが、よろしいですか?";
      }
    },

returnValueで設定したメッセージを表示できるのはIEだけのようです。

違うページに遷移しようとしたとき

export default {
  beforeRouteLeave (to, from, next) {
    const answer = window.confirm("編集中のものは保存されませんが、よろしいですか?")
    if (answer) {
      next()
    } else {
      next(false)
    }
  },
}

編集されている時だけ出す場合は、

export default {
  beforeRouteLeave (to, from, next) {
    if (this.formChanged){
      const answer = window.confirm("編集中のものは保存されませんが、よろしいですか?")
      if (answer) {
        next()
      } else {
        next(false)
      }
    } else {
      next();
    }
  },
}

beforeRouteLeaveを使うときは、next()が必須です。どこにも遷移できなくなります。
また、beforeRouteLeaveはルートコンポーネントにしか書けないようで、サブコンポーネントに書いても呼ばれません。
mixinに書くのは問題ないです。

サブコンポーネントに記述をまとめている場合はちょっと面倒ですが、コンポーネントの参照を使って書くことができます。

ルートコンポーネント

<template>
  <sub-component ref="subComponent" />
</template>

<script>
export default {
  beforeRouteLeave (to, from, next) {
    if (this.$refs.subComponent.formChanged){
      const answer = window.confirm("編集中のものは保存されませんが、よろしいですか?")
      if (answer) {
        next()
      } else {
        next(false)
      }
    } else {
      next();
    }
  },
};
</script>

コンポーネントの参照は、メソッドも可能なので、基本的に出来ないことはなさそうです。
ルートコンポーネントだけでしか使えないことがちょっと使いづらいかもしれません。

参考

https://blog.hirokiky.org/entry/2019/08/22/111031
https://ja.coder.work/so/vuejs2/724205
https://router.vuejs.org/ja/guide/advanced/navigation-guards.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】Jest+scss でユニットテスト時のスタイルのコンパイルを止めた件

Vue CLI 環境で Jest によるユニットテスト環境を構築しようとしたところ、 scss や CSS Modules との兼ね合いでうまくテストを走らせられませんでした。
スタイルに関するユニットテストをしないなら、テスト時はスタイルのコンパイルを止められるのではないかと考え調べてみたところ、実現できることがわかったのでその設定を紹介します。

動作確認した環境

  • macOS Catalina
  • Chrome 83
  • Node.js 12.18.1
  • npm 6.14.5
  • Vue CLI v4.4.4
    • vue 2.6.11
    • sass (dart-sass) 1.26.5
    • sass-loader 8.0.2
    • @vue/test-utils 1.0.3
    • @vue/cli-plugin-unit-jest 4.4.0

テスト時のスタイルのコンパイルを止める方法

vue-jest の設定で experimentalCSSCompile 1false にします:

jest.config.js
module.exports = {
  preset: '@vue/cli-plugin-unit-jest/presets/typescript',
  globals: {
    'vue-jest': {
      experimentalCSSCompile: false
    }
  }
}

ユニットテスト時のスタイルのコンパイルを止めた理由

理由1: スタイルに関するユニットテストは難しい

デザインの変更が頻繁に発生する場合、「この状態のときは○○の色が赤である」といったテストは壊れやすいのではないかと思います。
(デザイナーがわずかに色を調整するとユニットテストが通らなくなる、といったケースなど)

スタイルに関するユニットテストはしないという選択をする場合、テスト時は vue ファイルのスタイルブロックを無視できるのではないかと考えました。

理由2: scss 変数を vue ファイルのスタイルブロックに自動挿入しているケースに対応できる

sass-loaderprependData という設定2で、 vue ファイルの各 <style lang="scss"> ブロック(scoped, module 付きを含む)の先頭部分に scss 変数等を自動挿入することができます。

Vue Loader のドキュメントでも紹介されている手法で、使われている方も多いかと思います。

vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      scss: {
        prependData: `$main-color: #333333;`
      }
    }
  }
}

テスト時のスタイルのコンパイルを止めない場合 jest.config.jsjest.globals['vue-jest'].resources.scssvue.config.jsprependData と近い設定ができるようですが、設定の重複管理は避けたいと思いました。

スタイルのコンパイルを止めることで、この問題を解決できました。

理由3: <style lang="scss" module> があると node-sass のインストールを求められる

テスト時のスタイルのコンパイルを止めない場合、 CSS Modules と scss を併用している vue ファイルのテストをしようとすると node-sass のインストールが必須となります:

sass(dart-sass)をインストールしている場合
 FAIL  tests/unit/example.spec.ts
  ● Test suite failed to run

    [vue-jest] Error: You are trying to use "scss". node-sass is missing.

    To install run:
    npm install --save-dev node-sass

上記のメッセージに従って node-sass をインストールすることでもこのエラーを回避できます。
しかし node-sass は OS 依存のバイナリを含んでいたり sass(dart-sass) と同じく scss をコンパイルする役割を持つパッケージのため、インストールは避けたいと思いました。

スタイルのコンパイルを止めることで、この問題を解決できました。

理由4: CSS Modules を使っていてもテンプレート上の $style.class_name で例外が発生しない

vue ファイルに <style module> ブロックがあると $style というオブジェクトが注入されテンプレート上で利用することになります。

単純に vue ファイルから <style module> ブロックを消すことを考えると、 $style のオブジェクトが注入されなくなってテンプレート上にある $style.class_name のようなプロパティへのアクセスで例外が発生しそうです。

ですが、紹介した設定ではテスト対象の vue ファイルに <style module> がある場合はブロック内の記述は無視しつつ $style = {} を注入してくれるため、上記のような例外は発生しないようになっています。


  1. vue-jestREADME.md のコミットログを追ったところ 2018/04/09 のコミットexperimentalCSSCompile に関する記述が追加されていました。2年以上経過していますが "experimental" は外れていないようです 

  2. sass-loader 7.x.x までは data で、 9.x.x からは additionalData となるようです。 additionalData は prepend だけでなく append もできるそうですが、 8.x.x の prependData と同じように使うことができました 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】Vue.jsでMVVMモデルを説明する

vue.jsの初学者が忘れないように記録しました。
違っている部分がありましたらコメントください。

ここでは
・トップページから他のページに移るまで(MVVMモデル)の流れ
・各ファイルの役割の説明

を説明します。

MVVMモデル

モデル(M)とビュー(V)間のやり取りをビューモデル(VM)を介して行うアーキテクチャのこと。

ViewModel

・Vue.js のインスタンスのこと。

Model

・dataオブジェクト と methodsオブジェクト のこと。
・el で指定されたDOM要素のこと。elでDOM要素を指定することで、ViewModel の適用範囲を指定できます。

DeNA DESIGN BLOG

各ファイルの流れ

main.js→new Vue()で指定したファイル→public/index.html→router.js→子要素のVueファイル

main.js

まずは main.js から全てが始まる。
new Vue() で Vue App を起動している。この時App.vueがメイン(親)のファイル、つまりルートパスとして最初に表示される。

main.jsのmount内とpublic/index.html内のid=" "は同じにする必要がある。今回はappとしている。

main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

router.js

読み込むファイルはrouter.jsに記述しておく必要がある。

router.js
import Vue from 'vue'
import Router from "vue-router"
import page2 from '@/components/page2'
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/page2',
      component: page2
    },
    {
      path: '/',
      component: HelloWorld
    }
  ]
});

App.vue

・テンプレートタグ内に<router-view></router-view>と書いてあげることによって、router.jsに読み込んだファイルを表示する。

App.vue
<template>
  <div id="app">
   <router-view></router-view>
  </div>
</template>
<script>
export default {
  name: 'app',
  components: {
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

HelloWorld.vue

HelloWorld.vue
<template>
 <div>
<img alt="Vue logo" src="../assets/logo.png">
  <p>{{title}}</p>
  <!-- 子コンポーネントのmessageを設定して呼び出す -->
  <hello-child message='メッセージだよ'/>
  <router-link to="/page2">Go to page2</router-link>
 </div>
</template>

<script>
import HelloChild from './HelloChild'
export default {
  data: function () {
    return {
      title: 'はろー!わーるど!'
    }
  },
  components: {
    HelloChild
  }
}
</script>

page2.vue

page2.vue
<template>
  <div>
    <p>
      ここはページ2です。
    </p>
    <router-link to="/">Go to page1</router-link>
  </div>
</template>

<script>
</script>

<style>
</style>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む