- 投稿日:2020-07-08T23:52:05+09:00
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などどんな属性でもこんな風に書けるから結構便利。
- 投稿日:2020-07-08T23:11:59+09:00
jquery世代の元デザイナー同僚に捧ぐVue.js
人に読んで頂くシンプルな記事…ひとりよがりな長文…ぶつぶつ…
反省中のyagrushです。
こんばんは。jquery世代の元デザイナー同僚に、
Vue.jsを紹介したらちょっと臆されてしまったので、この記事を捧げます。Vue.js
こちらのを押すと流れる動画は分かりやすくて好きなのですが、元デザイナーにはイマイチなみたいでした…
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>↓
もう少し具体的に説明
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>↓
- 投稿日:2020-07-08T23:11:59+09:00
Vue.jsのイメージが全く浮かばない方に超超簡単な紹介をする
人に読んで頂くシンプルな記事…ひとりよがりな長文…ぶつぶつ…
反省中のyagrushです。
こんにちは。jquery世代の元デザイナー同僚に、
Vue.jsを紹介したらちょっと臆されてしまったので、この記事を捧げます。Vue.js
こちらのを押すと流れる動画は分かりやすくて好きなのですが、イマイチ響かないみたいでした。
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>↓
もう少し具体的に説明
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>↓
- 投稿日:2020-07-08T22:25:42+09:00
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)を比較した図が下記になります。
あくまで個人的な主観ですが、GoogleAPIの方がWebSpeechAPIよりも文字起こしの精度が高く感じます。
また、GoogleAPIはREST形式なので、どのプラットフォームでも使うことができます。
そしてgRPC版であれば、GoogleAPIもストリーミング入力に対応しているのでリアルタイム性ある文字起こしができそうなのですが、gRPC版は現状ブラウザには対応していないようです。
ブラウザでのリアルタイム性についてはWebSpeechAPIに軍配が上がります。GoogleAPIを使ったブラウザでのリアルタイム文字起こし
どうにかGoogleAPIの文字起こし精度を使いつつ、ブラウザでリアルタイムな文字起こしを行いたいと色々と試行錯誤した結果たどり着いたやり方が、音声検出(Voice Activity Detection)を用いるやり方です。
GoogleAPIはREST形式であり、音声ファイルをリクエストすると文字起こしテキストがレスポンスされます。
通常多いのは、「録音開始」ボタンを押して録音し、「録音終了」ボタンを押した際に録音終了しつつ、作成された音声ファイルをAPIに投げ、受け取ったテキストデータを表示する、というやり方だと思います。この録音開始・録音終了のトリガーを、ボタンではなく音声検出イベントをトリガーにすることで、リアルタイム感ある文字起こしを実現します。
voice-activity-detectionというライブラリがあるため、これを使いつつ文字起こししてみた結果が下記になります。
上が原文、下の枠の中に書かれているのが実際に文字起こしされたテキストになるのですが、最初の文字だけ文字起こしがうまくいっていません。
これは、音声検出が発話を始めてから2~3ワード後に開始されているからです。「OK,Google」のようなウェイクワードがないため、人間の発話タイミングに音声検出が追いついていないため、このような現象がおきます。Web Speech APIとの併用でうまくいった!
voice-activity-detectionライブラリの中には、音声検出のための様々なパラメータがあるため、それらをいじりながら試行錯誤していたのですが、ふと「音声検出の部分はWeb Speech APIが優れているわけだし、そこと併用できないか?」という考えに思い至り、試しに組んでみたらうまくいきました!
上記の通り、話し始めもバッチリ文字起こしできています。
実装したソースコードは下記のようなものになります。(今回は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のみ対応となる点は注意です。
昨今オンラインでのやりとりが増えてきている中で、文字起こしの技術はそれを手助けするのに相性のいい技術ですので、今後こういった技術がもっと世の中に浸透するといいですね!
- 投稿日:2020-07-08T22:08:03+09:00
Vuex StoreのModule分割
はじめに
Vuex便利ですよね!Vueでサービス作る上で欠かせない機能だと思います。
ただ規模が大きくなるにつれ肥大化が否めません。
そこでstoreをmoduleという単位で分割する方法、さらにそれらを別ファイル管理する方法を記載します。module管理
まずmoduleで区切っていないストアの例を見てみましょう
src/store/index.jsimport 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.jsimport 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.jsimport user from './user'; import article from './article'; import material from './material'; export { user, article, material, };src/store/modules/user.jsexport default { namespaced: true, state: { userId: '', username: '', introduction: '', followUsers: [], star: 0, }, mutations: { ... }, actions: { ... }, };src/store/modules/article.jsexport default { namespaced: true, state: { postArticles: [], articles: [], articleId: '', popularArticles: [], searchAricles: [], }, mutations: { ... }, actions: { ... }, };src/store/modules/material.jsexport default { namespaced: true, state: { help: [], topCarousel: [], notices: [], }, mutations: { ... }, actions: { ... }, };このように区分することで今後のストアの増加にも対応できます。
現時点で必要なさそうでも導入は早めが楽です、是非導入のご検討いかがでしょうか?
- 投稿日:2020-07-08T21:10:36+09:00
【第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 -vbrewコマンドが使えていなければ、Homebrewはインストールできていないです。
Node.jsのバージョン管理のためにnodebrewをインストール
$ brew install nodebrewnodebrewコマンドで確認
$ nodebrew -vNode.js安定版のインストール
node.jsをインストールする時、最新版や安定版など、バージョン指定でインストールが可能ですが、ここでは安定ばんのインストールを行います。
nodebrew install-binary stablePathを通す(nodeコマンドが使えるようにします)
$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profilevue-cliのインストール
Vue.jsを使う環境を準備するためのコマンドラインインターフェースをインストールします。
npm install -g @vue/cliプロジェクトを作成する
vue create practice-appvs codeを立ち上げる
vue create my-projectサーバーを立ち上げる
npm run serveローカルで見れるようになります。
http://localhost:8080/
次回以降、学習していきます!
- 投稿日:2020-07-08T21:10:36+09:00
【第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 -vbrewコマンドが使えていなければ、Homebrewはインストールできていないです。
Node.jsのバージョン管理のためにnodebrewをインストール
$ brew install nodebrewnodebrewコマンドで確認
$ nodebrew -vNode.js安定版のインストール
node.jsをインストールする時、最新版や安定版など、バージョン指定でインストールが可能ですが、ここでは安定版のインストールを行います。
nodebrew install-binary stablePathを通す(nodeコマンドが使えるようにします)
$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profilevue-cliのインストール
Vue.jsを使う環境を準備するためのコマンドラインインターフェースをインストールします。
npm install -g @vue/cliプロジェクトを作成する
vue create practice-appvs codeを立ち上げる
code .サーバーを立ち上げる
npm run serveローカルで見れるようになります。
http://localhost:8080/
次回以降、Vue.jsの中身について学習していきます(>_<)!
- 投稿日:2020-07-08T20:25:23+09:00
Vue Router 直接URLでアクセスするとエラーが起きる
Vue.jsを使っていて、通常の画面遷移のルーティングは問題無いがURLを直打ちすると
Uncaught SyntaxError: Unexpected token <というエラーになってしまう現象が起きた。
index.html内のパスの設定が原因
これを
<script type="text/javascript" src="js/app.js"></script>このように変えてやるだけ
<script type="text/javascript" src="/js/app.js"></script>上のパターンだと直接URLを叩いた時とリンクから画面遷移した時で見ている場所が変わってしまう。
- 投稿日:2020-07-08T18:53:10+09:00
Gridsome,WP REST APIでビルド時に401エラーが出る
概要
GridsomeでWP REST APIをソースとして使おうとしたら躓いたので備忘録
起こったこと
ビルド時に401が返ってきてしまいコンテンツが取得できない
gridsome.config.jsmodule.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のダッシュボードからセキュリティに移動
WordPressの微調整の項目の設定からREST APIをデフォルトアクセスに変更
再度ビルド
$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
- 投稿日:2020-07-08T14:41:11+09:00
【Vue】ローカル環境でのポート番号の変更
- 投稿日:2020-07-08T11:29:27+09:00
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
- 投稿日:2020-07-08T10:38:06+09:00
【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.11sass
(dart-sass) 1.26.5sass-loader
8.0.2@vue/test-utils
1.0.3@vue/cli-plugin-unit-jest
4.4.0テスト時のスタイルのコンパイルを止める方法
vue-jest
の設定でexperimentalCSSCompile
1をfalse
にします:jest.config.jsmodule.exports = { preset: '@vue/cli-plugin-unit-jest/presets/typescript', globals: { 'vue-jest': { experimentalCSSCompile: false } } }ユニットテスト時のスタイルのコンパイルを止めた理由
理由1: スタイルに関するユニットテストは難しい
デザインの変更が頻繁に発生する場合、「この状態のときは○○の色が赤である」といったテストは壊れやすいのではないかと思います。
(デザイナーがわずかに色を調整するとユニットテストが通らなくなる、といったケースなど)スタイルに関するユニットテストはしないという選択をする場合、テスト時は vue ファイルのスタイルブロックを無視できるのではないかと考えました。
理由2: scss 変数を vue ファイルのスタイルブロックに自動挿入しているケースに対応できる
sass-loader
のprependData
という設定2で、 vue ファイルの各<style lang="scss">
ブロック(scoped
,module
付きを含む)の先頭部分に scss 変数等を自動挿入することができます。Vue Loader のドキュメントでも紹介されている手法で、使われている方も多いかと思います。
vue.config.jsmodule.exports = { css: { loaderOptions: { scss: { prependData: `$main-color: #333333;` } } } }テスト時のスタイルのコンパイルを止めない場合
jest.config.js
のjest.globals['vue-jest'].resources.scss
でvue.config.js
のprependData
と近い設定ができるようですが、設定の重複管理は避けたいと思いました。スタイルのコンパイルを止めることで、この問題を解決できました。
理由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 = {}
を注入してくれるため、上記のような例外は発生しないようになっています。
vue-jest
のREADME.md
のコミットログを追ったところ 2018/04/09 のコミット でexperimentalCSSCompile
に関する記述が追加されていました。2年以上経過していますが "experimental" は外れていないようです ↩
sass-loader
7.x.x まではdata
で、 9.x.x からはadditionalData
となるようです。additionalData
は prepend だけでなく append もできるそうですが、 8.x.x のprependData
と同じように使うことができました ↩
- 投稿日:2020-07-08T00:16:19+09:00
【初心者】Vue.jsでMVVMモデルを説明する
vue.jsの初学者が忘れないように記録しました。
違っている部分がありましたらコメントください。ここでは
・トップページから他のページに移るまで(MVVMモデル)の流れ
・各ファイルの役割の説明を説明します。
MVVMモデル
モデル(M)とビュー(V)間のやり取りをビューモデル(VM)を介して行うアーキテクチャのこと。
ViewModel
・Vue.js のインスタンスのこと。
Model
・dataオブジェクト と methodsオブジェクト のこと。
・el で指定されたDOM要素のこと。elでDOM要素を指定することで、ViewModel の適用範囲を指定できます。各ファイルの流れ
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.jsimport 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.jsimport 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>