- 投稿日:2020-02-13T22:53:16+09:00
Vue.jsのmodalにうまくCSSが効かなかった話
Vue.jsのmodal
こんにちは。最近Vue.jsを書いている学生のwebエンジニアです。
動的にsession管理するために、Vue.jsでmodal使いたいよねという話になり、ごねごね書いていましたが、いきなりcssが効かなくなり、こんな画面になってしまった。。(一応ぼかしいれますがご了承ください)高さが足りなくて「作成」ボタンが埋もれてしまった、、、
CSSが効いていないときのコード
Modal.vue<script> import ~ export default { props: { 省略 }, data() { return{}; }, methods: { showModal() { this.$modal.show( CreateUserModal, { UserId: this.UserId }, { NameLists: this.NameLists }, { name: "CreateUserModal", height: "800", scrollable: true } ); } }, created() {} }; </script>このコードだと最初みたいな画像のmodalになってしまう、
解決方法
Modal_.vue<script> import ~ export default { props: { 省略 }, data() { return{}; }, methods: { showModal() { this.$modal.show( CreateUserModal, // ここを変える { UserId: this.UserId, NameLists: this.NameLists }, { name: "CreateUserModal", height: "800", scrollable: true } ); } }, created() {} }; </script>show()は`第一引数にプロパティ、第二引数にcssを書かないと適用されないらしい。引数追加しすぎて、第三引数に書いていたのが問題でした。
こんな感じに修正!
参考
vue-modal公式ドキュメントまとめ
公式ドキュメントをしっかり読もう
- 投稿日:2020-02-13T18:02:18+09:00
Vue.jsの環境構築を完了したら、翌日React.jsでオフショアとの共同開発が決まった時の話
概要
前回の続きです。
前回までのあらすじ
- プロジェクトで使用する試作品が Vue.js で開発することが決まった。
- Vue.js で協力会社の方が開発を行い試作品が完成した。
- 前任者から受け継いだ試作品の Vue.js のプロジェクトを実行したら 20 個のエラーが残っていた。
- 全部自分一人で修正した。
- ついでに Eslint すら入ってない環境だったので、既存の環境に Eslint と Pritter などを導入する。( 全く使用していない Jquey のライブラリも js というフォルダ内に何故か存在していたので抹消した )
- ついでに Nuxt と TypeScript の環境に構築し直して、ルーティングを行った。
- 本格的に開発が始まったので、オフショアと共同開発が決定した。
- オフショアは Vue.js の使用経験がなく、React の使用経験しかなかったので、オフショアに合わせて React で一から作り直すことが決まった。
- 筆者の React 経験は独学で 1 か月程。サンプルにページを追加して Redux を弄った程度、だが、筆者がオフショアに指示を出すことが決まった。
本編
今回はオフショアとの共同開発を行う上で、どのような問題が発生したか、どのようなメリットがあったか、その時どのような方法で対処したか、またどのように対処すればより良い結果が残せたか、
という反省を込めた記事になっております。あまり技術的な要素はない記事なのでご了承下さい。そもそもの問題
- そもそも筆者がオフショアの方と仕事をするのが初めてであり、更に人に指示などを行って共同開発した経験が少なかった。
- それまでは設計者から指示を貰って仕事をしたり、年の近い若手同士で協力して業務を行うことが多かった。
- そのため、初対面の外国の方との共同開発が大変だった。
- オフショアの方は 2 人いたが、一人は日本語を話せたがもう一人は日本語を話せなかった。( 英語で質問がきて、自分は英語で対応できなかった )
- 筆者の React.js の開発経験が不足していた。
苦労した点と対処方法
お互い日本語で話しても発音などの問題でコミュニケーションが上手くいかなかった。
- Slack などを使って文章による対話を行うことで対応。( オフショアも識字は得意だったのでお互い文通の方がコミュニケーションが上手くいった )
- 日本語の敬語や丁寧語が伝わらなかったので、単語を強調し、友達のような話し方で話した。( 相手も年が近かったからできた方法でした。でもこれのお陰でその人と仲良くなれました。)
- 同じ技術の用語でも違う言い方をすることがあったので、伝わらない場合、技術用語の言い方を変えた。( リポジトリ → Repository など、日本人がカタカナ用語で言っている言葉が通じないことがあった )
- 同じく、なるべく技術の用語は英語で書くように注意した。( GitHub、VSCode、Repository、k8s など )
- 無意識に.NET Framework で使う用語を使っていたので言い方を変えた。( Label → ReadOnlyText など )
文通に慣れてくるともう一人の方が、英語で質問をしてきた。
- Slack で英語を送るように頼み、自分はその文章を Google 翻訳で日本語に翻訳して、質問を理解し、回答を行った。
React について理解不足なので、どんなライブラリや環境構築を行えばいいかわからなかった。
- 以前に React で開発する可能性があるから React について調査して欲しいと言われたので React で開発を行った場合、 Vue.js で使用していたライブラリと比較して何を使えばいいか調査してスプレッドシートにまとめておいた。
- そのスプレッドシートを元に Vue.js で行っていたこの機能は React では何が対応する、などをオフショアの方とその場で話して合意を取り、環境構築で使用するライブラリを逐一スプレッドシートに追記するように合意を取った。( Vuex → Redux、TypeScript → Flow、vue-router → react-router-dom など )
オフショアの開発方法と、試作品での開発方法で意見が対立した。
- WebPack を使うか否か、静的型付けを行うために TypeScript にするのか Flow を使用するのか。
- それぞれのツールの良し悪しを調査し、打合せを重ねてよりプロジェクトに適したツールを使用した。
画面のイメージが Excel のお絵かきでオフショアに納品された。更にその画面のイメージでは色合いや大きさなどが実際に作る予定の試作品とマッチしておらず、開発するのに必要な情報が不足していた。
- 別の部署で Adobe XD を使用していると聞き覚えがあったので、その部署の方に使い方や利点などを教えて貰い、プロジェクト内に浸透させた。
よかった点
オフショアのフロントエンドに関する技術力が高かったこと。
- 自分がオフショアの方に React について質問した時に、懇切丁寧に応えてくれた。
- 自分も Vue.js をやっていたのでルーティングなど共通の概念は理解できたので、実装方法に関する話しなどは順調に進んだ。
オフショアが OSS に精通していたので GitHub などでのやり取りは社員よりも上手くいき ( 社内で GitHub 導入中であったため )、また GitHub Featerure Fllow など効率的な運用方法はオフショアから教えてもらい、プロジェクト内に浸透できた。
一部のライブラリは Vue.js でも React でも使用可能だったので、ライブラリの選定にかかる時間を短縮できた。( Jest、StoryBook、JsDoc、husky )
オフショアも使用経験のなかった Jest、JsDoc、husky、StoryBook などに関しては自分がプロジェクト内で有用性をアピールし、導入することでチーム開発効率化がなされた。
オフショアの影響もあり、弊社のレガシーな技術でのプロジェクト開発が一部モダンな物に置き換わり、社内に浸透した。( SVN → GitHub 、VSCode、Excel の画面イメージ → Adobe XD )
日頃から資料やメモなどは全て Markdown やスプレッドシートで作成しており、Gsuite の Google ドライブで管理していたため、オフショアとの情報のやり取りや管理方法で戸惑うことが少なかった。
反省点
- 社内に自分以外の React の有識者がいなかったこと。(オフショア以外のメンバーからもフロントエンド、クラウド、GitHub などの質問が全て自分に集中して対処しきれなくなった。)
- プロジェクト内でフロントエンド以外にもクラウドや GitHub の管理も担当していたので、フロントエンドに集中できなかったこと。
- React を独学した経験を活かして、最初から Vue.js ではなく React.js を推奨して開発を行うことをしなかったこと。( 開発初期では、内製を目指していたので、多人数で開発する場合、JavaScript の経験者が少ないのであれば学習コストの低い Vue.js が最適だと判断してしまった )
まとめ
日頃から学習した内容を資料として残しておいてよかった。効率的な方法で情報をまとめておき、臨機応変に対応すれば不意の事態にもある程度対応できることがわかった。
参考記事
husky で git commit 時に npm test を走らせる
- 投稿日:2020-02-13T17:49:05+09:00
VueやNuxtで自作コンポーネントをグローバルに呼び出す
Vueのコンポーネント便利ですよね!
これをいかにうまく作るかがVueの醍醐味だと思っております。
作ったコンポーネントを1度しか呼び出さないときは<script> import MyButton from '~/components/buttons/MyButton' export default { components: { MyButton } } </script>で呼び出すようにすれば充分でしょう。
でも、至る箇所でコンポーネントを呼び出したい場合、いろいろな.vueファイルに記述を書くのは面倒です。
そういったのはグローバルで呼び出せるようにしてしまいましょう。グローバルに登録
例えばMyButtonというコンポーネントを作ったとします。
以下のような記述を書くとグローバルに登録でき、.vueファイル内でMyButtonを呼び出すことが出来ます。import Vue from 'vue' import MyButton from '~/components/buttons/MyButton' Vue.component('MyButton', MyButton)ではこの記述をどこに書くのか・・・
僕はコンポーネントを種類別にディレクトリを分けます。
例えばボタンならcomponents/buttons
の中に
チェックボックスやラジオボタンならcomponents/forms
の中にといった具合です。
でこれらをグローバルに呼び出したいので、components/buttons
の中にindex.js
、components/forms
の中にindex.js
を作りその中に上記のようなグローバルに登録する記述を書きます。
そしてcomponents/index.js
を用意してそこに以下のように記述します。components/index.jsimport '~/components/buttons' import '~/components/forms'最後にこの
components/index.js
をどこかで呼び出せばOKです。
vue.jsの場合は、main.js
内にimport '~/components'を追記しましょう。
nuxt.jsの場合は
nuxt.config.js
の中のplugins
に登録しておけば、フレームワーク起動時に呼ばれるのでどのvueファイルでもコンポーネントを呼び出すことが出来ます。export default { plugins: [ '~/components/index.js' ] }
- 投稿日:2020-02-13T16:04:33+09:00
Vuex ORMの闇
Vuex ORMの闇を紹介しようと思ったが、v0.35.0で治っていた。と思ったら別の闇に遭遇。
Vuex ORMの別の闇
以下の問題に遭遇。v0.34.1に戻せば良いと書いてあるが、後述のようにv0.35.0の差分を使いたいので戻せない。
https://github.com/vuex-orm/vuex-orm/issues/544
上で言及されている、以下のコミットで治る。
https://github.com/cuebit/vuex-orm/commit/2ae995dba1142f1f343af9661ca8edf3797ba17d
Vuex ORMの正しい使い方
以下に書いてあるように、SSRの場合は、
this.$store.$db().model('users')
の形式で使うと良い。2019年12月に報告があり、2020年1月のv0.35.0でこの差分が入っている。参考資料
https://github.com/vuex-orm/vuex-orm/releases/tag/v0.35.0
https://github.com/vuex-orm/vuex-orm/issues/514
https://vuex-orm.github.io/vuex-orm/guide/digging-deeper/server-side-rendering.html
今まで普通にグローバルに使ってて気付かなかった。
ぎりぎり動いていたんだろう。インターフェースの変更で修正量が多いので、一旦は後述の解決策1などでも良いかもしれない。
どこにも言及されていないが、初期化はvuex pluginでstore初期化のたびにデータベースを作るようにしないといけないと思う。シングルトンの名残がまだ残っているから、複雑。
Vuex ORMの闇
サーバーサイドレンダリングで、asyncData(nuxtのやつ)で、Vuex ORMをいじると、リクエスト間でVuexのstateが混じる可能性がある。
集めたファクト
- Vuex ORMではコンテクスト管理にシングルトンが使われている
- 本番のnuxtでは、runInNewContextがfalseになるので、リクエスト間でnodeのコンテクストが共有される
- nuxt、connect(nuxtで使われているライブラリ)、vue-server-rendererでは、直列化のコードは無い
- nuxtのasyncDataでログ出力させたら、リクエストが返るまでにVuex ORMが参照するstoreが置き換わっていることを確認
詳細
1. Vuex ORMではコンテクスト管理にシングルトンが使われている
Vuex ORMの実装の中では、storeに以下のようにアクセスできる。
同時に2つのstoreを扱えないことを示唆している。Container.database.store
また、以下のようなVuex ORMのインターフェースから考えても、コンテクストを渡せないので、インターフェースを変えない限り、同時に2つのstoreを扱うのは難しい。
User.create({ data: { id: 1, name: 'John' } })2. 本番のnuxtでは、runInNewContextがfalseになるので、リクエスト間でnodeのコンテクストが共有される
nuxtのコードを読んだが、runInNewContextがfalseになる。
https://ssr.vuejs.org/ja/api/#runinnewcontext
3. nuxt、connect(nuxtで使われているライブラリ)、vue-server-rendererでは、直列化のコードは無い
それぞれのコードを読んだが、直列化しているコードはない。
以下、上から順に呼ばれる
- https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/server/src/server.js
- https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/server/src/middleware/nuxt.js
- https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/vue-renderer/src/renderer.js
- https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/vue-renderer/src/renderers/ssr.js
- https://github.com/vuejs/vue/blob/6390f70c2e4ec7e51cce1d2a4ba35e2d0d328205/packages/vue-server-renderer/build.dev.js
4. nuxtのasyncDataでhttpリクエストのところでログ出力させたら、リクエストが返るまでにVuex ORMが参照するstoreが置き換わっていることを確認
確認方法は、Vuex ORM graphql pluginでhttpリクエストのところに以下のようなコードを仕込んで確認。「同じリクエストIDである、ならば、同じstoreアドレスである」成立すれば問題ないが、
結果、成立しなかった。abコマンドなどで並行リクエストを送ると、store == store2 != store3となってしまうことがあった。
let seq = 1 const addressToId = new WeakMap() async asyncData({ store }) { const reqId = seq++ const store2 = Project.store() if (!addressToId.get(store)) addressToId.set(store, seq++) if (!addressToId.get(store2)) addressToId.set(store2, seq++) console.log('request started ' + reqId + ' ' + addressToId.get(store) + ' ' + addressToId.get(store2)) // await http request const store3 = Project.store() if (!addressToId.get(store3)) addressToId.set(store3, seq++) console.log('request finished ' + reqId+ ' ' + addressToId.get(store) + ' ' + addressToId.get(store3)) },解決策1
nuxtのモジュールで、vueのrenderの前後にフック(幸い、vueの実装ではawaitされている)を仕掛けて、直列実行させる。
並行実行されなくなるので、サーバーのスループットが犠牲になるが、nuxtなら一人のユーザーが一度に行うSSRリクエストは一回だけなので、ユーザーから見たパフォーマンスはあまり劣化しないはず。
解決策2
vue-server-renderのrunInNewContextに'fork'モードを追加する。
railsのunicornやpumaみたいに、ライブラリを読み込んだあとのプロセスを用意しておいて、そこからforkでサーバープロセスを作るパターンを使う。
linux上のnodeでnative moduleでforkが使えることは過去に確認したことがあるが、OSに依存するので不安定かもしれない。
解決策3
解決策2と似ているがZone.jsを使う。thread local storage的なものらしい。
nuxtのモジュールを作るのと、Vuex ORMの実装を変更する必要があるが、Vuex ORMのインターフェースは変更する必要がないのがメリット。いろいろ関係ないところまで影響しそうなのがデメリット。https://github.com/angular/angular/tree/master/packages/zone.js
- 投稿日:2020-02-13T16:04:33+09:00
Nuxt + SSR + Vuex ORMの闇
Vuex ORMの闇を紹介しようと思ったが、v0.35.0で治っていた。と思ったら別の闇に遭遇。そして、Vuex ORM graphql pluginの闇にも遭遇。
まとめ
- Nuxt + Vuex ORM graphql pluginをそのままSSRで使うのは難しい -> 後述の解決策1 or 4
- Nuxt + Vuex ORMをSSRで使うのは可能 -> 詳細は後述
俺氏の選択は、解決策1 + シングルトンVuex ORM + Vuex ORM graphql plugin
Vuex ORM graphql pluginをシングルトンじゃないように修正するのは辛すぎる。Vuex ORM graphql pluginの闇
これはVuex ORMのSSR対応版(v0.35.0)に対応していない気がする。
少なくとも正しく動くのかかんたんには検証できない。結論、Vuex ORM graphql pluginを使いたいなら、後述の解決策1を使うしかない。
パフォーマンスが問題になったら、解決策1を解決策4に昇華させるルートもある。Vuex ORMの別の闇
以下の問題に遭遇。v0.34.1に戻せば良いと書いてあるが、後述のようにv0.35.0の差分を使いたいので戻せない。
https://github.com/vuex-orm/vuex-orm/issues/544
上で言及されている、以下のコミットで治る。
https://github.com/cuebit/vuex-orm/commit/2ae995dba1142f1f343af9661ca8edf3797ba17d
Vuex ORMの正しい使い方
以下に書いてあるように、SSRの場合は、
this.$store.$db().model('users')
の形式で使うと良い。2019年12月に報告があり、2020年1月のv0.35.0でこの差分が入っている。参考資料
https://github.com/vuex-orm/vuex-orm/releases/tag/v0.35.0
https://github.com/vuex-orm/vuex-orm/issues/514
https://vuex-orm.github.io/vuex-orm/guide/digging-deeper/server-side-rendering.html
今まで普通にグローバルに使ってて気付かなかった。
ぎりぎり動いていたんだろう。インターフェースの変更で修正量が多いので、一旦は後述の解決策1などでも良いかもしれない。
どこにも言及されていないが、初期化はvuex pluginでstore初期化のたびにデータベースを作るようにしないといけないと思う。シングルトンの名残がまだ残っているから、複雑。
Vuex ORMの闇
サーバーサイドレンダリングで、asyncData(nuxtのやつ)で、Vuex ORMをいじると、リクエスト間でVuexのstateが混じる可能性がある。
集めたファクト
- Vuex ORMではコンテクスト管理にシングルトンが使われている
- 本番のnuxtでは、runInNewContextがfalseになるので、リクエスト間でnodeのコンテクストが共有される
- nuxt、connect(nuxtで使われているライブラリ)、vue-server-rendererでは、直列化のコードは無い
- nuxtのasyncDataでログ出力させたら、リクエストが返るまでにVuex ORMが参照するstoreが置き換わっていることを確認
詳細
1. Vuex ORMではコンテクスト管理にシングルトンが使われている
Vuex ORMの実装の中では、storeに以下のようにアクセスできる。
同時に2つのstoreを扱えないことを示唆している。Container.database.store
また、以下のようなVuex ORMのインターフェースから考えても、コンテクストを渡せないので、インターフェースを変えない限り、同時に2つのstoreを扱うのは難しい。
User.create({ data: { id: 1, name: 'John' } })2. 本番のnuxtでは、runInNewContextがfalseになるので、リクエスト間でnodeのコンテクストが共有される
nuxtのコードを読んだが、runInNewContextがfalseになる。
https://ssr.vuejs.org/ja/api/#runinnewcontext
3. nuxt、connect(nuxtで使われているライブラリ)、vue-server-rendererでは、直列化のコードは無い
それぞれのコードを読んだが、直列化しているコードはない。
以下、上から順に呼ばれる
- https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/server/src/server.js
- https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/server/src/middleware/nuxt.js
- https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/vue-renderer/src/renderer.js
- https://github.com/nuxt/nuxt.js/blob/e8aca9eb117851047e82e94948ff8b4bcb464b1a/packages/vue-renderer/src/renderers/ssr.js
- https://github.com/vuejs/vue/blob/6390f70c2e4ec7e51cce1d2a4ba35e2d0d328205/packages/vue-server-renderer/build.dev.js
4. nuxtのasyncDataでhttpリクエストのところでログ出力させたら、リクエストが返るまでにVuex ORMが参照するstoreが置き換わっていることを確認
確認方法は、Vuex ORM graphql pluginでhttpリクエストのところに以下のようなコードを仕込んで確認。「同じリクエストIDである、ならば、同じstoreアドレスである」成立すれば問題ないが、
結果、成立しなかった。abコマンドなどで並行リクエストを送ると、store == store2 != store3となってしまうことがあった。
let seq = 1 const addressToId = new WeakMap() async asyncData({ store }) { const reqId = seq++ const store2 = Project.store() if (!addressToId.get(store)) addressToId.set(store, seq++) if (!addressToId.get(store2)) addressToId.set(store2, seq++) console.log('request started ' + reqId + ' ' + addressToId.get(store) + ' ' + addressToId.get(store2)) // await http request const store3 = Project.store() if (!addressToId.get(store3)) addressToId.set(store3, seq++) console.log('request finished ' + reqId+ ' ' + addressToId.get(store) + ' ' + addressToId.get(store3)) },解決策1
nuxtのモジュールで、vueのrenderの前後にフック(幸い、vueの実装ではawaitされている)を仕掛けて、直列実行させる。
並行実行されなくなるので、サーバーのスループットが犠牲になるが、nuxtなら一人のユーザーが一度に行うSSRリクエストは一回だけなので、ユーザーから見たパフォーマンスはあまり劣化しないはず。
以下のフックを使う。
https://github.com/nuxt/nuxt.js/blob/dev/packages/vue-renderer/src/renderers/ssr.js#L65-L77
nuxt module例 (moduleの使い方は https://ja.nuxtjs.org/guide/modules/ )
サーバーサイドのみなのでWeakMapを使ってみた。let seq = 1 let lockPromise const auxData = new WeakMap() const lock = async (data) => { while (lockPromise) { console.log('serial-render waiting') await lockPromise } lockPromise = new Promise((resolve) => { data.lockPromiseResolve = resolve }) } const unlock = (data) => { if (data.lockPromiseResolve) { lockPromise = void 0 const resolve = data.lockPromiseResolve data.lockPromiseResolve = void 0 resolve() } } export default function () { this.nuxt.hook('vue-renderer:ssr:prepareContext', async ({ req }) => { const data = { id: seq++ } auxData.set(req, data) await lock(data) console.log('serial-render start ' + data.id) }) this.nuxt.hook('vue-renderer:ssr:context', async ({ req }) => { const data = auxData.get(req) console.log('serial-render finished ' + data.id) unlock(data) }) this.nuxt.hook('render:errorMiddleware', async (connectInstance) => { connectInstance.use((err, req, res, next) => { const data = auxData.get(req) if (data) { console.log('serial-render error ' + data.id) unlock(data) } next(err) }) }) }懸念: ↑のフックの間でthrowすることは無いか? -> asyncDataとかでthrowしたらthrowした。そして、vue-renderer:ssr:contextは呼ばれなかった。
throwしたら以下に行く
https://github.com/nuxt/nuxt.js/blob/dev/packages/server/src/middleware/nuxt.js#L85
そしてnextで以下に行く。ここで独自の例外補足用のエラーmiddlewareを入れておけば良い。
https://github.com/nuxt/nuxt.js/blob/dev/packages/server/src/server.js#L165
解決策2
vue-server-renderのrunInNewContextに'fork'モードを追加する。
railsのunicornやpumaみたいに、ライブラリを読み込んだあとのプロセスを用意しておいて、そこからforkでサーバープロセスを作るパターンを使う。
linux上のnodeでnative moduleでforkが使えることは過去に確認したことがあるが、OSに依存するので不安定かもしれない。
解決策3
解決策2と似ているがZone.jsを使う。thread local storage的なものらしい。
nuxtのモジュールを作るのと、Vuex ORMの実装を変更する必要があるが、Vuex ORMのインターフェースは変更する必要がないのがメリット。いろいろ関係ないところまで影響しそうなのがデメリット。https://github.com/angular/angular/tree/master/packages/zone.js
解決策4
解決策1の進化版。
vue-server-renderのrunInNewContextに'pool'モードを追加する。
contextプールを用意して空いているものに投げる。すでにtrueと'once'が実現できているから、実現できるはず。https://github.com/vuejs/vue/blob/52719ccab8fccffbdf497b96d3731dc86f04c1ce/src/server/bundle-renderer/create-bundle-runner.js#L107
このrunnerを複数にして、直列実行にすれば良い。メモリ消費量が問題になるかもしれないけど。
- 投稿日:2020-02-13T15:12:17+09:00
v-forを使ってselect optionを生成
<div id="app"> <select name="user" v-model="selected"> <option disabled value="">選択して下さい</option> <option v-for="user in users" v-bind:value="user.name" v-bind:key="user.id"> {{user.name}} </option> </select> </div>new Vue({ el: '#app', data: { users: [ {id:1, name:'佐々木'}, {id:2, name:'鈴木'}, {id:3, name:'高木'} ], selected: '' } })
- 投稿日:2020-02-13T13:03:47+09:00
Vueの 親子孫双方向バインディング
作成物の説明
atomics設計を参考にしています。
atoms→molecules→organisms→templates→pages
(孫) (子) (親) 今回のコンポーネントはこの対応で進めています。うまくいかないとき
(孫)
<template> <v-menu v-model="calendar" :close-on-content-click="false" :nudge-right="40" transition="scale-transition" offset-y min-width="200px" > <template v-slot:activator="{ on }"> <v-text-field :value="date" v-on="on" :label="label" prepend-inner-icon="mdi-calendar-range" readonly crearable /> </template> <v-date-picker v-model="date" @input="calendar = false" @change="$emit('input', $event)" /> </v-menu> </template> <script> export default { props: { label: { type: String, default: 'Not Defined' }, value: { type: String, default: '' } }, data: () => ({ // date: new Date().toISOString().substr(0, 10), date: '', menu: false, modal: false, calendar: false }), watch: { value (val) { this.date = val } } } </script>(子)
<template> <span class="calendar-style"> <input-calendar :label="label1 +`from`" v-model="dateFromTo[0]" @change="$emit('input', dateFromTo)" />  <input-calendar :label="label1+`to`" v-model="dateFromTo[1]" @change="$emit('input', dateFromTo)" /> </span> </template> <script> import InputCalendar from '~/components/atomics/InputCalendar' export default { components: { InputCalendar }, props: { value: { type: Array, default: () => (['', '']) }, label1: { type: String, default: '' }, label2: { type: String, default: '' } }, data () { return { dateFromTo: ['', ''] } }, watch: { value (val) { this.dateFromTo = val } } } </script> <style lang="scss" scoped> .calendar-style { display: inline-flex ; } </style>(親)
<template> <span> <v-col cols="12" class="calendar"> <v-row> <p>{{ arriveDay }}</p> <calendar-from-to :label1="arriveDay" v-model="FromTo" /> <p>{{ shipDay }}</p> <calendar-from-to :label1="shipDay" v-model="S_FromTo" /> </v-row> </v-col> <v-row> <v-col cols="3"> <input-arrive-place-company v-model="companyName" /> </v-col> <v-col cols="3"> <input-arrive-place-company-kana v-model="companyNameKana" /> </v-col> <v-col cols="3"> <input-ware-house v-model="warehouse" /> </v-col> </v-row> <v-row> <v-col cols="3"> <input-warning v-model="alert" /> </v-col> <v-col cols="3"> <input-status v-model="status" /> </v-col> <v-col cols="3"> <Input-registered-person v-model="person" /> </v-col> </v-row> </span> </template> <script> import CalendarFromTo from '~/components/molecules/CalendarFromTo' import InputArrivePlaceCompany from '~/components/atomics/InputArrivePlaceCompany' import InputArrivePlaceCompanyKana from '~/components/atomics/InputArrivePlaceCompanyKana' import InputWareHouse from '~/components/atomics/InputWareHouse' import InputWarning from '~/components/atomics/InputWarning' import InputStatus from '~/components/atomics/InputStatus' import InputRegisteredPerson from '~/components/atomics/InputRegisteredPerson' export default { components: { CalendarFromTo, InputArrivePlaceCompany, InputArrivePlaceCompanyKana, InputWareHouse, InputWarning, InputStatus, InputRegisteredPerson }, data () { return { arriveDay: '納品日', shipDay: '出荷日', warehouse: [], companyName: [], companyNameKana: [], alert: [], status: [], person: [], date: '', FromTo: ['', ''], S_FromTo: [] } }, methods: { } } </script>うまくいったとき
孫を直しました。
template内で@での呼び出しで、emitではなくonChangeというfunctionを呼び出し、$emitでinput とchangeを発火させるようにしました。
(孫のtemplate内)<v-date-picker v-model="date" @input="calendar = false" @change="onChange" />(孫のscript内)
methods: { onChange (e) { this.$emit('input', e) this.$emit('change', e) } }孫のtemplate内の$emitの呼び出しだけではうまく子のvalueの値が書き変わらなかったので困っているところ、
助けてもらい、functionで強制発火する方法で解決できました。
- 投稿日:2020-02-13T12:33:41+09:00
vue.js で サイト完成後にやっておきたいテスト
まぁ、重い。ページがすごく重い。
他の人の作ったvue.jsサイトを見ると 9点とかもある。重い場合はCSSを別読み込みにするといいかも。
これで 80点以上に上がる。vue.blade.php<script type="text/javascript"> // 非同期でfontawesomeを読み込む (function() { var css = document.createElement('link'); css.href = 'https://use.fontawesome.com/releases/v5.11.2/css/all.css'; css.rel = 'stylesheet'; css.type = 'text/css'; document.getElementsByTagName('head')[0].appendChild(css); var css2 = document.createElement('link'); css2.href = '/css/app.css'; css2.rel = 'stylesheet'; css2.type = 'text/css'; document.getElementsByTagName('head')[0].appendChild(css2); var css3 = document.createElement('link'); css3.href = '/css/lightbox.css'; css3.rel = 'stylesheet'; css3.type = 'text/css'; document.getElementsByTagName('head')[0].appendChild(css3); })(); </script>ページスピードテスト
https://developers.google.com/speed/pagespeed/insights/サーチコンソール
https://search.google.com/search-console/about?hl=ja・サイトマップは登録してる?
・インデックスされるURLをテストするとちゃんとタイトルタグは変更されてる?モバイルフレンドリー
https://search.google.com/test/mobile-friendly・エラー出てない?
・インデックスされるURLをテストするとちゃんとタイトルタグは変更されてる?テストマイサイト
https://www.thinkwithgoogle.com/intl/ja-jp/feature/testmysite/すべてやっておきましょう。
webpack.min.jsmix.js('resources/js/app.js','public/js') .version() .sass('resources/sass/app.scss','public/css') .sass('resources/sass/lightbox.scss','public/css');こんな感じで version(); をつけておく。
じゃないとnpm run prod するたびにユーザー側にキャッシュが残り、ハードリロードしない限り更新されないというバグになるので。
- 投稿日:2020-02-13T12:01:58+09:00
NuxtとVueを基礎から学びなおすために。Vue Schoolのすすめ
TL;DR
Vue Schoolに登録してみたら、VueとNuxtの理解が深まった。
※この記事は下書き保存したまま数ヶ月忘れてたので
書き直してはいますが一部でタイムラインの矛盾があるかもしれません。経緯
Nuxtでポートフォリオを作り直していた際に
よく考えたら今までは完全静的サイトの構築でNuxtメインが多かったので
実は細かい部分の知識がポロポロ漏れているのでは。。。
と思い直して、きっちり基礎から学び直しながら進めることにしました。ちょうどVueのページ開いたらVue Schoolの40%オフセールの広告があったので(Black Fridayでした)
何も考えずに誘導されてさくっと登録(笑)結論から言うと、登録してよかったです!
良かった点
◎実際の開発に近い形で学べる
フォーラムやブログを構築しつつ進んでいくので、実際のアプリ構築で必要な工程を順に学べます。
実際にこれを見ながら、進行中のポートフォリオサイトも構成の甘かった点など少し調整入れています。◎各レッスンが短め
コースの中でレッスンが機能ごとに細かく分けられているので
既に知ってる部分については、スキップしたり再生速度を速めたりもできるので楽。◎Nuxtの学習にも最適
「Vue School」ですが、VueのコースだけでなくNuxtに関連するコースもあります。
◎実装ファイルが用意されている
また、コースごとにボイラープレートなども用意されてたりするので、実際のファイルを見ながら進められます。
私はよくやるのですが、タイプミスとかでどうしてもうまくいかないときにコピペもできます(笑)
後からも見直しやすいです。◎字幕がついてる
英語のコースではあるのですが、英語字幕がついていますし、再生速度の調整も可能です。
◎ユーザーの声が結構反映されている
レッスンにコメントが残せるのですが、たとえば字幕つけてくれとかファイル提供してくれとか
そういったレスに対してしっかり対応しているようです。残念だった点
少しマイナスかな?というのが、動画の英語音声がとても訛っていること。。
最初無料コースを見て問題なさそうだなと思って
マスターコースを見たら突然訛りのある人になったのでぎょっとしました。。ただ内容は問題ないですし、再生速度の変更や字幕の表示も出来るので、逆に良かったかもしれないです。
Vue Schoolのすすめ
一部で無料コースもありますし、Nuxtのドキュメントからもリンクが貼られていたりします。
コースの種類も多様で、ピンポイントで不足している知識が学習出来ますし、とてもおすすめです。
個人的には、「The View.js Master Class」や「Async Data with Nuxt.js」あたりが参考になりました。これから
フリーランスで主に一人で活動しているため、知識がランダムになりがちです…。
まだまだ個人的には漏れている知識があると認識しているので
引き続きVue Schoolやドキュメントなどで学習続けたいと思います。
- 投稿日:2020-02-13T01:49:11+09:00
Vue Composition API を使ってリアクティブに値を更新する方法
書かないこと
- Vue Composition APIの導入方法
- TypeScriptではなく、Vueのみ
- refとreactiveの使い分けについて
Vue3.0より実装予定の Composition API を使ってstateの値を更新します。
setup関数内で更新する。
index.vue<template> <div> <p>{{ count }}</p> <button @click='increment'>+</button> </div> </template> <script> import { ref } from '@vue/composition-api' export default { setup(){ const count = ref(0) function increment () { count.value++ }, return{ increment, count } } } </script>公式にもある通りですが、シンプルですね。
Vueで値の更新を検知するにはrefを使う必要があります。Composition Functionで切り出した時の更新
index.vue<template> <div> <p>{{ count }}</p> <button @click='increment'>+</button> </div> </template> <script> import { ref } from '@vue/composition-api' const useIncrement = () => { const count = ref(0) const increment = () => { count.value++ } return { increment, count } } } export default { setup(){ const {increment, count } = useIncrement() return{ increment, count } } } </script>従来のVueではdataやmethodごとに記述箇所が決まっていましたが、
Composition APIを使うことで関数の切り出しが可能になりました。setup内のstateを更新する。
index.vue<template> <div> <p>{{ state.count }}</p> <button @click='increment'>+</button> </div> </template> <script> import { ref } from '@vue/composition-api' const useIncrement = (state) => { const increment = () => { state.count++ } return { increment } } } export default { setup(){ const state = reactive({ count: 0 }) const {increment } = useIncrement(state) return{ state, increment } } } </script>Composition Functionにstateを渡して更新すればOK
VueComposition APIはさわり始めたばかりですが、柔軟コードが書けそうな反面難しそうですね。
- 投稿日:2020-02-13T00:10:39+09:00
vue-cli-plugin-express でポート番号を指定してサーバーを起動する
vue-cli 3.x プラグインの vue-cli-plugin-express にて、ポート番号を指定してサーバーを起動する際に詰まったので備忘録として。
この記事は vue のプロジェクトに
vue add express
でプラグインを追加した状態から進めていきます。解決方法
package.json
の"scripts"
内にある"express"
または"express:run"
の値の末尾に--port [任意のポート番号]
を付け加えることで、ポート番号を指定してのサーバー起動に成功しました。package.json"scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint", "express": "vue-cli-service express:watch --port 3001", "express:run": "vue-cli-service express:run --port 3001" }> npm run express > test@0.1.0 express (プロジェクトのパス) > vue-cli-service express:watch --port 3001 DONE Wed Feb 12 2020 23:22:41 GMT+0900 (GMT+09:00) ♻️ Server running at: - Local: http://localhost:3001/ - Network: http://192.168.0.100:3001/ ⚙ You're in development mode. to start the application, run npm run serve. �? Fallback to this server enabled: you can use relative routes in your cod �? No api routes found (yet?).解決前に試したこと
プラグイン導入時に生成される
./srv/index.js
内にapp.listen(port, () => {})
の形で書いてもポート番号が反映されず、プラグインの README.md にポート番号の指定について書かれていなかったのでしばらく悩みました。node_modules の中を直に弄るのもなぁと思いながらファイルを漁ってみたところ、それっぽいことが書いてあるファイルが見つかりました。
→ vue-cli-plugin-express/src/config.js (GitHub)この時点でなんとなく指定方法がわかったのですが、何かの出力では…?と思い、
npm run express
を実行したときに呼び出されるvue-cli-service express:watch
に対して、--help
をつけて呼び出してみたところ、それらが出力されました。> npx vue-cli-service express:watch --help Usage: vue-cli-service express:watch [options] Options: --delay delays run by a small duration (default: false) --host specify host (default: 0.0.0.0) --port specify port (default: 3000) --https use https (default: false) For more info, see https://github.com/mathieutu/vue-cli-plugin-express今後はちゃんと
--help
を使っていこうと思いました…。