20210815のvue.jsに関する記事は11件です。

Vue.jsでchart.jsが入らない

内容 chart.jsが入らない。なぜ? WARNING in ./node_modules/vue-chartjs/es/BaseCharts.js 62:32-37 "export 'default' (imported as 'Chart') was not found in 'chart.js' @ ./node_modules/vue-chartjs/es/index.js @ ./node_modules/babel-loader/lib??ref--4-0!./node_modules/vue-loader/lib??vue-loader-options!./resources/js/components/Chart.vue?vue&type=script&lang=js& @ ./resources/js/components/Chart.vue?vue&type=script&lang=js& @ ./resources/js/components/Chart.vue @ ./node_modules/babel-loader/lib??ref--4-0!./node_modules/vue-loader/lib??vue-loader-options!./resources/js/pages/Dashboard.vue?vue&type=script&lang=js& @ ./resources/js/pages/Dashboard.vue?vue&type=script&lang=js& @ ./resources/js/pages/Dashboard.vue @ ./resources/js/router.js @ ./resources/js/app.js @ multi ./resources/js/app.js ./resources/sass/app.scss 実行コマンドはこちら npm install vue-chartjs chart.js ちゃんとchartはimportしてる。 バージョンが違うらしいので入れ直してみる $ npm uninstall vue-chartjs $ npm uninstall chart.js $ npm install chart.js@2.8 $ npm install vue-chartjs@3.4.2 今度はコンパイル成功した! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】タスク管理アプリ「ToDoRo」を開発!(Laravel+Vue+Terraform+CircleCI+Fargate)

この度、タスク管理とポモドーロ・テクニックを組み合わせたタスク管理アプリ『ToDoRo(トゥードーロ)』をリリースしました! Laravel + Vue.js + Terraform + CircleCI + Fargate という技術スタックの個人開発プロダクトはあまり見かけなかったので、自分の成果物が誰かの役に立てば幸いです。 また、開発期間も1年2か月と長期間になってしまったので、長期間の開発を続けるコツもお伝えできたらと思います。 プロダクト:『ToDoRo(トゥードーロ)』 GitHub:Soh1121/ToDoRo: タスク管理アプリ『ToDoRo』 開発期間:約1年2か月 『ToDoRo』とは? 『ToDoRo(トゥードーロ)』は、仕事や学習に集中して取り組むことを手助けするタスク管理アプリです。 ToDoリストとポモドーロ・テクニック(25分の集中と5分の休憩による時間管理)を組み合わせ、メリハリを付けてタスクに取り組む事ができます。 使用技術 業務で外部に依頼した技術は自分でも触れるようになるため、様々な技術スタックに挑戦しました。 バックエンド PHP 7.4.6 Laravel 8.54.0 フロントエンド HTML Sass JavaScript axios 0.21.1 Vue.js 2.6.11 Vue CLI 4.5.13 vuex 3.4.0 vuetify 2.3.0 vuerouter 3.3.2 インフラ GitHub Docker/Docker Compose MySQL 5.7.30 NGINX 1.20 CircleCI 2.1 ecspresso 1.3.2 Terraform 1.0.1 AWS(VPC, ACM, Route53, IAM, ALB, ECR, ECS(Fargate), RDS, ElastiCache(Redis), S3, CloudWatchLogs, Parameter Store) テスト/静的コード解析 PHPUnit 9.5.8(テスト) PHPStan 0.12.94(静的コード解析) ESlint 6.8.0(静的コード解析) Prettier 1.19.1(コードフォーマッター) アーキテクチャ図 このアーキテクチャは、技術書同人誌を参考にしながら作成しました。 書店で売られている書籍以外にも役立つ書籍がゴロゴロある。 IT技術の魅力のひとつに、知見を自分の中に溜め込まないコミュニティの広さがあると感じました。 参考書籍:TerraformでFargateを構築してGitHub Actionsでデプロイ!Laravel編 ER図 自分なりに第一正規化〜第三正規化まで行ったつもりで作成したER図です。 ただ、途中で実現しようとした機能がデータ不足で実装できない事態に陥り、途中で色々と変更したので、正規化が崩れているかもしれません。 設計段階で完璧にできれば苦労しませんが、完璧を目指しすぎても失敗したときのダメージが大きいので、ほどほどのところまでで進めていくほうが良いと感じました。 機能一覧 利用のハードルを下げるため、一部の機能はJavaScriptでも実装し、ユーザー登録なしでも利用できるようにしました。 JavaScriptでの実装に思いの外時間がかかってしまいました… これをやらなければもう3か月は早くリリースできていたと思います。 ユーザー機能(PHP) 新規登録 ログイン ログアウト タスク機能(PHP/JavaScript) タスク登録/参照/更新/削除 完了・未完了切り替え ポモドーロ数カウントアップ タイマー時間記録 プロジェクト(タスクのカテゴリ)登録/参照/更新/削除 コンテキスト(タスクを実施する場所・時間など)登録/参照/更新/削除 表示絞り込み ポモドーロ機能(PHP/JavaScript) タイマー管理(4回集中したら15分の休憩) 1日のポモドーロ数管理(タイマー管理実現のため) タイマーカウントダウン タイマー時間セット タイマー時間リセット アラーム音再生 その他の機能 レスポンシブ対応 全ページSPA 開発背景 ここで少し開発背景をご紹介します。 「やりたいことがたくさんあって、いくら時間があっても足りない…」 日々このような悩みを抱えながら過ごしていました。 そこから、効率的にタスクに取り組んだり、集中力を高める方法などを調べているうちに、「タスク管理」と「時間管理」がどんどん切り離されていました。 「タスク管理はあのアプリ(ToDoアプリ)」、「時間管理はこのアプリ(カレンダー・タイマーなど)」と、2つのアプリを行ったり来たりする中で、「1つにまとめたい…!」という思いが生まれ、このアプリの開発に着手しました。 こだわった点/工夫した点 上記のような思いから、以下のようなこだわった点/工夫した点を持って取り組みました。 レスポンシブ対応し、PC・スマホ・タブレットから利用可能に 画面を行ったり来たりしても、なるべくエラーが出ないよう配慮 途中でデバイスを変更できるようなデータの持ち方に 1年2か月という長期に渡る開発 あまりいないと思うので、1年2か月という長期に渡って個人開発した感想と、振り返ってどうしたら良かったかを考えてみたいと思います。 はじめは半年くらいで完成を目指そうと思っていたのですが、ずるずると先延ばしになって、結局1年2か月という月日がかかってしまいました。 周りを見渡すと、短期的にコミットして完成させる方が多く、正直焦りの気持ちは大きかったです。 そのような中で開発を続けられたのは、以下の要因が影響したと考えています。 GitHubにおけるIssue管理 タスクはIssue管理し、IssueはProjectでざっくりとどのようなステータスかを記録しました。 働きながらで時間が取れないことも多々ありましたが、おかげで作業できない期間が長くあっても、作業の流れを追いながら開発も行うことができました。 コミットメッセージに#<issue Number>でIssueに作業を紐付けることができるので、付与しておくことをおすすめします。 テスト駆動開発 テストファーストで開発を進める「テスト駆動開発」に何度も救われました。 テストを作成しながら実装を進めることで、テストが充実し、途中で意図しない動作に変わっていることに気付く機会が何度もありました。 手間だからとテストを書いていなければ、恐らく途中でどこがおかしいかわからない状況になり、挫折していたことでしょう。 自分の考えを整理するのにも役立つので非常におすすめです。 まとめ 長期間の開発は辛いものもありましたが、無事完成したときの喜びはひとしおです。 自分の手で何かを作り上げていく作業、新しいことを学ぶこと、うまくいかないことが解決した瞬間をたくさん味わえるWeb開発は最高だなぁと改めて感じております。 ここで終わりにせず、引き続き機能を充実させたり、おかしなところを改善したり、コードを整理したりしながら育てていけたらと思います。 最後に、この開発を通して自分が言いたいことは GitHubを使うのであれば、Issue管理をしましょう! テストはあとから書くのではなく、書きながら実装しましょう! です。ご覧いただきありがとうございました! ぜひ『ToDoRo』を利用してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#6 Rails × Vue.jsで動的なページをSPA化させる

前回の続き。 railsのデータをjson形式でvueに受け渡すことができたので、vueの方の記述をやっていこうというフェーズです。 投稿の詳細ページをvueで実装しようとしてるのですが、以前railsで実装したいいね機能や、コメント機能などをvueで新たに書き直す必要があるので、vueに疎い自分にとってはまぁまぁ難関ポイントですが しっかり頑張っていきたいと思います。 まずはVueでいいね機能を実装 drink(投稿)テーブルの方にlikes_countといったカラムがあり、 何個いいねがついてるかといったデータはそこのカラムに格納されます。 そのデータは既にjson形式でvueの方に受け渡しています。 なので、その投稿に何個いいねがついたか表示するといった機能は簡単にできそう。 問題は、どうやっていいねをつけるか。 app.vue import axios from 'axios'; と非同期通信を可能にしてくれるモジュールをインポートしてるのでそれを駆使したりしそうですね。 一旦railsがどうやっていいねを機能させていたかを振り返り いいね機能がどの様にできていたか分解すればvueの方のいいね機能の実装が簡単になる気がするので 一旦railsがどうやっていいねを機能させていたかを振り返ります。 _like.html.erb <div class="like" id="like-link-<%= drink.id %>"> <% if current_user.likes.find_by(drink_id: drink.id) %> <%= link_to unlike_path(drink.id), method: :delete, remote: true do %> <div class = "iine__button"> <i class="fas fa-heart"></i> <%= drink.likes.count %></div> <% end %> <% else %> <%= link_to like_path(drink.id), method: :post, remote: true do %> <div class = "iine__button"><i class="far fa-heart"></i><%= drink.likes.count %></div> <% end %> <% end %> </div> userオブジェクトの方に、likesメソッドがあってそこでいいねされていた場合は、リンクのHTTP動詞がdetele、いいねされていなかったらpostになっています。 likesメソッドとはなんぞやと思ったので見てみると、 users_controller.rb def likes @user = User.find(params[:id]) @pagy,@drinks = pagy(@user.like_drinks.order('created_at DESC').includes(:user,{image_attachment: :blob})) @title = "#{@user.nickname}がいいねした投稿" end んー、このメソッドは一言でまとめると、users/likes.html.erbを表示するメソッドに過ぎないぞ。。。 呼んでるメソッドはこれではなさそう。。。。 となったら、 binging.pryで処理を止めて、 [10] pry(#<#<Class:0x00007f9c2612b258>>)> current_user.likes => [#<Like:0x00007f9c262c00c8 id: 2, user_id: 7, drink_id: 8, created_at: Fri, 06 Aug 2021 05:41:22.009804000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:41:22.009804000 UTC +00:00>, #<Like:0x00007f9c262c7e90 id: 3, user_id: 7, drink_id: 5, created_at: Fri, 06 Aug 2021 05:41:54.834559000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:41:54.834559000 UTC +00:00>, #<Like:0x00007f9c262c7cb0 あー、なるほどね。 user.rbの has_many :likes has_many :like_drinks, through: :likes, source: :drink これですね。has_manyとはbelongs_toの本質はアソシエーションではなく、メソッドを作るメソッドということです。ってのをRails チュートリアルの安川さんが仰っていたのを思い出した。 てか、多対多のリレーションを組む時に、throughとかsourceとかあったわ。 調べてみよ。 まず、userは色々な投稿にいいねしていて、drinks(投稿)も色々なユーザーにいいねされている。 そこで、多対多のリレーションになる。 中間テーブルとして、likesテーブルがある。 user_id,drink_idカラムがあり、どのユーザーがどんなユーザーにいいねしたか分かるようになる。 多対多の関連がある時はthroughオプションをつけてあげると中間テーブルを経由して関連先のモデルを取得できるようになります。 throughをつけないと中間テーブルの先のレコードを取得するのが一手間必要になり微妙なので設定するのが吉です。 というか、中間テーブルを用いた多対多のリレーションの時に、はthroughにそもそも中間テーブルを指定してあげないといけない感じ。 もうそーゆーもんだと一旦割り切ろう(脳死) ただ、railsのアソシエーションを忘れていた。っていう課題が出てきたので、 後日またrailsチュートリアルを見直すしかない。 sourceオブションは、 sourceオプションを設定することでuser.like_drinksでuserがお気に入りした投稿を一気に取得することができます。 んー、とりあえずこの二つの合わせ技で、user.like_drinksとやったら、 ユーザーがいいねした投稿を取得できるって感じですかね。。 [11] pry(#<#<Class:0x00007f9c2612b258>>)> current_user.like_drinks => Drink Load (1.9ms) SELECT `drinks`.* FROM `drinks` INNER JOIN `likes` ON `drinks`.`id` = `likes`.`drink_id` WHERE `likes`.`user_id` = 7 ↳ app/views/likes/_like.html.erb:3 [#<Drink:0x00007f9c200b4258 id: 8, name: "aaaaa", price: 3000, explain: "#sssss", user_id: 7, created_at: Fri, 06 Aug 2021 05:33:18.586567000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:33:18.657514000 UTC +00:00, region_id: 3, body_id: 2, acidity_id: 3, processing_id: 2, likes_count: 1>, #<Drink:0x00007f9c200b4190 id: 5, name: "エチオピア", price: 1330, explain: " #CITRUS(シトラス) #DARK_COCOA(ダークココア) ダークチョコレート、ペッパーのようなスパイス、そしてスイートシトラスの風味が特徴の、やわらかでベルベットのような口あたりのコーヒーです。", user_id: 6, created_at: Sat, 12 Jun 2021 13:35:18.097644000 UTC +00:00, updated_at: Sat, 12 Jun 2021 13:35:18.153340000 UTC +00:00, region_id: 4, body_id: 3, acidity_id: 4, processing_id: 2, 確かにこんな感じで取得できた。 binding.pryのおかげで助かった。。。 んで本題に戻る。として、 current_user.likesで現在のユーザーが、 [10] pry(#<#<Class:0x00007f9c2612b258>>)> current_user.likes => [#<Like:0x00007f9c262c00c8 id: 2, user_id: 7, drink_id: 8, created_at: Fri, 06 Aug 2021 05:41:22.009804000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:41:22.009804000 UTC +00:00>, #<Like:0x00007f9c262c7e90 id: 3, user_id: 7, drink_id: 5, created_at: Fri, 06 Aug 2021 05:41:54.834559000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:41:54.834559000 UTC +00:00>, #<Like:0x00007f9c262c7cb0 どの投稿にいいねしてるかという情報が手に入る。 そこでまた、find_byをしてあげて、その投稿のidがuser.likesの中のdrink_idと一致したら その投稿にいいねを既にしているということなので、 <%= link_to unlike_path(drink.id), method: :delete, remote: true do %> このリンクが表示される。 このリンクを押すと、likes#unlikeメソッドが作動する。 その時にremote: trueなのでjava scriptのリクエストになる。 こうすることで一部分だけの表示を変えることができる。 likes_controller.rb before_action :set_variables def unlike like = current_user.likes.find_by(drink_id: @drink.id).destroy # binding.pry #=> like.js.erbに遷移する。 end def set_variables @drink = Drink.find(params[:drink_id]) @id_name = "#like-link-#{@drink.id}" end とあるので、パラメーターで受け取ったdrink_idと一致したcurrent_user.likes(ユーザーがいいねした投稿)のいいねを消去する。 そして like.js.erb $("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink )) %>'); <%= @id_name %>この部分の表示だけ、renderで部分的な更新をします。 既に、 current_user.likes.find_by(drink_id: @drink.id).destroy でユーザーがいいねした投稿のいいねを消しているので、 likes/likeにレンダリングしたら今度は <% else %> <%= link_to like_path(drink.id), method: :post, remote: true do %> <div class = "iine__button"> <i class="far fa-heart"> </i><%= drink.likes.count %> </div> <% end %> <% end %> こっちの部分が表示されることになります。 まぁ、なんとなくは分かった。 改めて、Vue.jsでいいね機能を実装したい。 コントローラーの記述は既存の物を使って、like.json.jbuilderとかで json形式でデータをやり取りして vueの方で、いいねの表示の切り替えをする様なイメージですかね。。。 まぁ何はともあれ「Vue.js いいね機能」とかで検索。 https://qiita.com/TakeshiFukushima/items/a6c698fec648c11eee9a この様な記事がヒットしたので一旦この方の記事を拝見させていただきながら進めます。 rails g controller api/likes index create destroy --skip-template-engine --skip-test-framework --skip-assets --skip-helper railsでいいね機能を実装した時と同様にlikes_controllerをapiな感じで作成 api/likes_controller.rb class Api::LikesController < ApplicationController include SessionsHelper before_action :set_variables def like current_user.likes.new(drink_id: @drink.id).save # redirect_to drinks_path # jsを用いるので画面遷移は行わない # binding.pry #=> like.js.erbに遷移する。 end def unlike current_user.likes.find_by(drink_id: @drink.id).destroy # binding.pry end private def set_variables @drink = Drink.find(params[:drink_id]) end end drinks#showをそのままコピペした時と同様に、likes_controllerもそのまま既存の物を 一旦コピペしときます。 api*じゃない方の*likes_controllerは likes_controller.rb def set_variables @drink = Drink.find(params[:drink_id]) @id_name = "#like-link-#{@drink.id}" end この様なメソッドを定義していましたが、 今回は@id_nameの方はいらないでしょう。 vueの方でフロントの処理を書くので。 んで、drinks_controllerの方のAPIを作成した時は、 app/views/api/drinks/show.json.jbuilderにどんなデータをjson形式で送るかを定義したので、 今回、likes_controllerの方をjson形式でデータのやり取りをさせたいから app/views/api/likes/like.json.jbuilder app/views/api/likes/unlike.json.jbuilder と言ったjsonファイルを作成すればいい感じかも んで、そこでどんなjsonデータを定義すればいいか。 drinks#showの時は api/drinks_controller.rb class Api::DrinksController < ApplicationController def show @drink = Drink.find(params[:id]) #@user = @drink.user #@comment = Comment.new # @comments = @drink.comments.includes(:user).order('created_at DESC') end end app/views/api/drinks/show.json.jbuilder show.json.jbuilder json.(@drink, :name ,:price , :explain , :region_id , :body_id , :acidity_id , :processing_id , :likes_count, :user_id) こんな感じで定義してた。 となると一旦インスタンス変数を定義する必要があるので、 api/likes_controller.rb class Api::LikesController < ApplicationController include SessionsHelper before_action :set_variables def like @like = current_user.likes.new(drink_id: @drink.id) @like.save # redirect_to drinks_path # jsを用いるので画面遷移は行わない # binding.pry #=> like.js.erbに遷移する。 end def unlike @like = current_user.likes.find_by(drink_id: @drink.id) @like.destroy # binding.pry end private def set_variables @drink = Drink.find(params[:drink_id]) end end こんな感じで定義して、 likes/like.json.jbuilder json.jbuilder json.(@like, :user_id,:drink_id) 雰囲気こんな感じ? ただ今回は、drinks#showと違って、既にrailsにあるデータをjson形式で手に入れるのではなく、 post,destroyなど、railsの方のデータを書き換えたり、消去しなければならない。 となると、drinks#showのやり方を踏襲しても意味ない気がする。。。。 https://qiita.com/TakeshiFukushima/items/a6c698fec648c11eee9a この方の記事を拝見させていただくと、 controllerの方でjson形式の何かを書いているわけではない。。。 いいね一覧を取得するメソッドのときはrender: jsonとかやってるけど。。。 create,destroyの時にはそう言った物を書いていない。。。 なぜだ。。。 まぁ、とりあえず、一旦rails側で何か書くものはもう既にないって感じでいいのかな。。。。 あとはvueで色々書けばいいね機能が実装されるのか。。。 他にも検索してみよう。 んーー、多分なさそう。vue.jsのaxiosがrailsのapl/likesにpostリクエストを送って、api/likes_controllerが動くから非同期処理できる。って仮説を立ててvueの方の処理を書こう。 だけど、どのユーザーがどんな投稿にいいねしたかという情報は必要じゃないか?? post_idとかuser_idをどうやって付与してリクエストを送るのだろうか。。。。 Vue.jsを記述 app.vue <likeButton></likeButton> <script> import likeButton from './packs/components/like/likeButton.vue' export default { components: { likeButton }, を追記して、んで、コンポーネントを読み込み。 export default { // propsでrailsのviewからデータを受け取る props: ['userId', 'postId'], この方の記事に、propsでviewからデータを受け取る。と言った記述があった。 そんなことができるの?と思ったので、調べてみると https://qiita.com/kazutosato/items/66bb6604bd680421101d こんな記事が見つかった。 https://qiita.com/kazutosato/items/38caffdbd21508a5c126 この記事を参考にして、 _like.html.erb <script> var userId = <%= { userId: current_user.try(:id) }.to_json.html_safe %>; var drinkId = <%= { drinkId: drink.try(:id) }.to_json.html_safe %>; </script> この様な記述をして、 likeButton.vue <script> import axios from 'axios' console.log(userId) console.log(drinkId) export default { props: ['userId', 'drinkId'] } </script> にこんな感じで書いたら、useridに7が入っていて、drinkIdには10が入っていました。 とりあえず、erbからvue.jsにデータを受け渡すのは成功した?っぽい!! これで、axios使っていい感じにrailsにリクエストを 送れそう!! やった====!!!! 次回はその続きでvueでいいね機能の仕上げに入りたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#6 Rails × Vue.jsで動的なページをSPA化させる【いいね機能】

前回の続き。 railsのデータをjson形式でvueに受け渡すことができたので、vueの方の記述をやっていこうというフェーズです。 投稿の詳細ページをvueで実装しようとしてるのですが、以前railsで実装したいいね機能や、コメント機能などをvueで新たに書き直す必要があるので、vueに疎い自分にとってはまぁまぁ難関ポイントですが しっかり頑張っていきたいと思います。 まずはVueでいいね機能を実装 drink(投稿)テーブルの方にlikes_countといったカラムがあり、 何個いいねがついてるかといったデータはそこのカラムに格納されます。 そのデータは既にjson形式でvueの方に受け渡しています。 なので、その投稿に何個いいねがついたか表示するといった機能は簡単にできそう。 問題は、どうやっていいねをつけるか。 app.vue import axios from 'axios'; と非同期通信を可能にしてくれるモジュールをインポートしてるのでそれを駆使したりしそうですね。 一旦railsがどうやっていいねを機能させていたかを振り返り いいね機能がどの様にできていたか分解すればvueの方のいいね機能の実装が簡単になる気がするので 一旦railsがどうやっていいねを機能させていたかを振り返ります。 _like.html.erb <div class="like" id="like-link-<%= drink.id %>"> <% if current_user.likes.find_by(drink_id: drink.id) %> <%= link_to unlike_path(drink.id), method: :delete, remote: true do %> <div class = "iine__button"> <i class="fas fa-heart"></i> <%= drink.likes.count %></div> <% end %> <% else %> <%= link_to like_path(drink.id), method: :post, remote: true do %> <div class = "iine__button"><i class="far fa-heart"></i><%= drink.likes.count %></div> <% end %> <% end %> </div> userオブジェクトの方に、likesメソッドがあってそこでいいねされていた場合は、リンクのHTTP動詞がdetele、いいねされていなかったらpostになっています。 likesメソッドとはなんぞやと思ったので見てみると、 users_controller.rb def likes @user = User.find(params[:id]) @pagy,@drinks = pagy(@user.like_drinks.order('created_at DESC').includes(:user,{image_attachment: :blob})) @title = "#{@user.nickname}がいいねした投稿" end んー、このメソッドは一言でまとめると、users/likes.html.erbを表示するメソッドに過ぎないぞ。。。 呼んでるメソッドはこれではなさそう。。。。 となったら、 binging.pryで処理を止めて、 [10] pry(#<#<Class:0x00007f9c2612b258>>)> current_user.likes => [#<Like:0x00007f9c262c00c8 id: 2, user_id: 7, drink_id: 8, created_at: Fri, 06 Aug 2021 05:41:22.009804000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:41:22.009804000 UTC +00:00>, #<Like:0x00007f9c262c7e90 id: 3, user_id: 7, drink_id: 5, created_at: Fri, 06 Aug 2021 05:41:54.834559000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:41:54.834559000 UTC +00:00>, #<Like:0x00007f9c262c7cb0 あー、なるほどね。 user.rbの has_many :likes has_many :like_drinks, through: :likes, source: :drink これですね。has_manyとはbelongs_toの本質はアソシエーションではなく、メソッドを作るメソッドということです。ってのをRails チュートリアルの安川さんが仰っていたのを思い出した。 てか、多対多のリレーションを組む時に、throughとかsourceとかあったわ。 調べてみよ。 まず、userは色々な投稿にいいねしていて、drinks(投稿)も色々なユーザーにいいねされている。 そこで、多対多のリレーションになる。 中間テーブルとして、likesテーブルがある。 user_id,drink_idカラムがあり、どのユーザーがどんなユーザーにいいねしたか分かるようになる。 多対多の関連がある時はthroughオプションをつけてあげると中間テーブルを経由して関連先のモデルを取得できるようになります。 throughをつけないと中間テーブルの先のレコードを取得するのが一手間必要になり微妙なので設定するのが吉です。 というか、中間テーブルを用いた多対多のリレーションの時に、はthroughにそもそも中間テーブルを指定してあげないといけない感じ。 もうそーゆーもんだと一旦割り切ろう(脳死) ただ、railsのアソシエーションを忘れていた。っていう課題が出てきたので、 後日またrailsチュートリアルを見直すしかない。 sourceオブションは、 sourceオプションを設定することでuser.like_drinksでuserがお気に入りした投稿を一気に取得することができます。 んー、とりあえずこの二つの合わせ技で、user.like_drinksとやったら、 ユーザーがいいねした投稿を取得できるって感じですかね。。 [11] pry(#<#<Class:0x00007f9c2612b258>>)> current_user.like_drinks => Drink Load (1.9ms) SELECT `drinks`.* FROM `drinks` INNER JOIN `likes` ON `drinks`.`id` = `likes`.`drink_id` WHERE `likes`.`user_id` = 7 ↳ app/views/likes/_like.html.erb:3 [#<Drink:0x00007f9c200b4258 id: 8, name: "aaaaa", price: 3000, explain: "#sssss", user_id: 7, created_at: Fri, 06 Aug 2021 05:33:18.586567000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:33:18.657514000 UTC +00:00, region_id: 3, body_id: 2, acidity_id: 3, processing_id: 2, likes_count: 1>, #<Drink:0x00007f9c200b4190 id: 5, name: "エチオピア", price: 1330, explain: " #CITRUS(シトラス) #DARK_COCOA(ダークココア) ダークチョコレート、ペッパーのようなスパイス、そしてスイートシトラスの風味が特徴の、やわらかでベルベットのような口あたりのコーヒーです。", user_id: 6, created_at: Sat, 12 Jun 2021 13:35:18.097644000 UTC +00:00, updated_at: Sat, 12 Jun 2021 13:35:18.153340000 UTC +00:00, region_id: 4, body_id: 3, acidity_id: 4, processing_id: 2, 確かにこんな感じで取得できた。 binding.pryのおかげで助かった。。。 んで本題に戻る。として、 current_user.likesで現在のユーザーが、 [10] pry(#<#<Class:0x00007f9c2612b258>>)> current_user.likes => [#<Like:0x00007f9c262c00c8 id: 2, user_id: 7, drink_id: 8, created_at: Fri, 06 Aug 2021 05:41:22.009804000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:41:22.009804000 UTC +00:00>, #<Like:0x00007f9c262c7e90 id: 3, user_id: 7, drink_id: 5, created_at: Fri, 06 Aug 2021 05:41:54.834559000 UTC +00:00, updated_at: Fri, 06 Aug 2021 05:41:54.834559000 UTC +00:00>, #<Like:0x00007f9c262c7cb0 どの投稿にいいねしてるかという情報が手に入る。 そこでまた、find_byをしてあげて、その投稿のidがuser.likesの中のdrink_idと一致したら その投稿にいいねを既にしているということなので、 <%= link_to unlike_path(drink.id), method: :delete, remote: true do %> このリンクが表示される。 このリンクを押すと、likes#unlikeメソッドが作動する。 その時にremote: trueなのでjava scriptのリクエストになる。 こうすることで一部分だけの表示を変えることができる。 likes_controller.rb before_action :set_variables def unlike like = current_user.likes.find_by(drink_id: @drink.id).destroy # binding.pry #=> like.js.erbに遷移する。 end def set_variables @drink = Drink.find(params[:drink_id]) @id_name = "#like-link-#{@drink.id}" end とあるので、パラメーターで受け取ったdrink_idと一致したcurrent_user.likes(ユーザーがいいねした投稿)のいいねを消去する。 そして like.js.erb $("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", drink: @drink )) %>'); <%= @id_name %>この部分の表示だけ、renderで部分的な更新をします。 既に、 current_user.likes.find_by(drink_id: @drink.id).destroy でユーザーがいいねした投稿のいいねを消しているので、 likes/likeにレンダリングしたら今度は <% else %> <%= link_to like_path(drink.id), method: :post, remote: true do %> <div class = "iine__button"> <i class="far fa-heart"> </i><%= drink.likes.count %> </div> <% end %> <% end %> こっちの部分が表示されることになります。 まぁ、なんとなくは分かった。 改めて、Vue.jsでいいね機能を実装したい。 コントローラーの記述は既存の物を使って、like.json.jbuilderとかで json形式でデータをやり取りして vueの方で、いいねの表示の切り替えをする様なイメージですかね。。。 まぁ何はともあれ「Vue.js いいね機能」とかで検索。 https://qiita.com/TakeshiFukushima/items/a6c698fec648c11eee9a この様な記事がヒットしたので一旦この方の記事を拝見させていただきながら進めます。 rails g controller api/likes index create destroy --skip-template-engine --skip-test-framework --skip-assets --skip-helper railsでいいね機能を実装した時と同様にlikes_controllerをapiな感じで作成 api/likes_controller.rb class Api::LikesController < ApplicationController include SessionsHelper before_action :set_variables def like current_user.likes.new(drink_id: @drink.id).save # redirect_to drinks_path # jsを用いるので画面遷移は行わない # binding.pry #=> like.js.erbに遷移する。 end def unlike current_user.likes.find_by(drink_id: @drink.id).destroy # binding.pry end private def set_variables @drink = Drink.find(params[:drink_id]) end end drinks#showをそのままコピペした時と同様に、likes_controllerもそのまま既存の物を 一旦コピペしときます。 api*じゃない方の*likes_controllerは likes_controller.rb def set_variables @drink = Drink.find(params[:drink_id]) @id_name = "#like-link-#{@drink.id}" end この様なメソッドを定義していましたが、 今回は@id_nameの方はいらないでしょう。 vueの方でフロントの処理を書くので。 んで、drinks_controllerの方のAPIを作成した時は、 app/views/api/drinks/show.json.jbuilderにどんなデータをjson形式で送るかを定義したので、 今回、likes_controllerの方をjson形式でデータのやり取りをさせたいから app/views/api/likes/like.json.jbuilder app/views/api/likes/unlike.json.jbuilder と言ったjsonファイルを作成すればいい感じかも んで、そこでどんなjsonデータを定義すればいいか。 drinks#showの時は api/drinks_controller.rb class Api::DrinksController < ApplicationController def show @drink = Drink.find(params[:id]) #@user = @drink.user #@comment = Comment.new # @comments = @drink.comments.includes(:user).order('created_at DESC') end end app/views/api/drinks/show.json.jbuilder show.json.jbuilder json.(@drink, :name ,:price , :explain , :region_id , :body_id , :acidity_id , :processing_id , :likes_count, :user_id) こんな感じで定義してた。 となると一旦インスタンス変数を定義する必要があるので、 api/likes_controller.rb class Api::LikesController < ApplicationController include SessionsHelper before_action :set_variables def like @like = current_user.likes.new(drink_id: @drink.id) @like.save # redirect_to drinks_path # jsを用いるので画面遷移は行わない # binding.pry #=> like.js.erbに遷移する。 end def unlike @like = current_user.likes.find_by(drink_id: @drink.id) @like.destroy # binding.pry end private def set_variables @drink = Drink.find(params[:drink_id]) end end こんな感じで定義して、 likes/like.json.jbuilder json.jbuilder json.(@like, :user_id,:drink_id) 雰囲気こんな感じ? ただ今回は、drinks#showと違って、既にrailsにあるデータをjson形式で手に入れるのではなく、 post,destroyなど、railsの方のデータを書き換えたり、消去しなければならない。 となると、drinks#showのやり方を踏襲しても意味ない気がする。。。。 https://qiita.com/TakeshiFukushima/items/a6c698fec648c11eee9a この方の記事を拝見させていただくと、 controllerの方でjson形式の何かを書いているわけではない。。。 いいね一覧を取得するメソッドのときはrender: jsonとかやってるけど。。。 create,destroyの時にはそう言った物を書いていない。。。 なぜだ。。。 まぁ、とりあえず、一旦rails側で何か書くものはもう既にないって感じでいいのかな。。。。 あとはvueで色々書けばいいね機能が実装されるのか。。。 他にも検索してみよう。 んーー、多分なさそう。vue.jsのaxiosがrailsのapl/likesにpostリクエストを送って、api/likes_controllerが動くから非同期処理できる。って仮説を立ててvueの方の処理を書こう。 だけど、どのユーザーがどんな投稿にいいねしたかという情報は必要じゃないか?? post_idとかuser_idをどうやって付与してリクエストを送るのだろうか。。。。 Vue.jsを記述 app.vue <likeButton></likeButton> <script> import likeButton from './packs/components/like/likeButton.vue' export default { components: { likeButton }, を追記して、んで、コンポーネントを読み込み。 export default { // propsでrailsのviewからデータを受け取る props: ['userId', 'postId'], この方の記事に、propsでviewからデータを受け取る。と言った記述があった。 そんなことができるの?と思ったので、調べてみると https://qiita.com/kazutosato/items/66bb6604bd680421101d こんな記事が見つかった。 https://qiita.com/kazutosato/items/38caffdbd21508a5c126 この記事を参考にして、 _like.html.erb <script> var userId = <%= { userId: current_user.try(:id) }.to_json.html_safe %>; var drinkId = <%= { drinkId: drink.try(:id) }.to_json.html_safe %>; </script> この様な記述をして、 likeButton.vue <script> import axios from 'axios' console.log(userId) console.log(drinkId) export default { props: ['userId', 'drinkId'] } </script> にこんな感じで書いたら、useridに7が入っていて、drinkIdには10が入っていました。 とりあえず、erbからvue.jsにデータを受け渡すのは成功した?っぽい!! これで、axios使っていい感じにrailsにリクエストを 送れそう!! やった====!!!! 次回はその続きでvueでいいね機能の仕上げに入りたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nuxt.jsにてfabricでcanvasに四角形図形を追加できるようにする

目的 javascriptのみでnuxt.jsのアプリにfabric.jsを導入したい 前提 fabric.jsをインストールしている。 こちらの記事にインストール方法は書きました。 インストール vue3のcomposition apiを使いたいので、インストールします。 yarn add @nuxtjs/composition-api nuxt.config.jsのモジュールに追加しておきます。 nuxt.config.js buildModules: [ // https://go.nuxtjs.dev/eslint '@nuxtjs/eslint-module', // https://go.nuxtjs.dev/stylelint '@nuxtjs/stylelint-module', // https://go.nuxtjs.dev/vuetify '@nuxtjs/vuetify', '@nuxtjs/composition-api/module', ], vueファイル index.vue <template> <div> <h1>Hello, Fabric!!</h1> <v-btn @click="add()">add</v-btn> <div class="wrapper"> <canvas id="canvas" width="600" height="600" /> </div> </div> </template> <script lang="js"> import { computed, defineComponent } from "@nuxtjs/composition-api" import { fabric } from "fabric" export default defineComponent({ setup() { const canvas = computed(() => new fabric.Canvas("canvas")) const add = () => { canvas.value.add( new fabric.Rect({ top: 100, left: 100, width: 60, height: 60, fill: "red", }) ) } return { add } }, }); </script> <style scoped> .wrapper { width: 600px; height: 600px; border: 1px solid; } </style> どのように見えるか Addするとこのように表示される。四角形は自分の好きなように変形させることが可能。 参考 ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【エラー】Elements in iteration expect to have 'v-bind:key' directives

はじめに RailsとVueでtodo-listを作成していた際に、起こったエラーの解決をまとめる app/javascript/packs/components/index.vue <li v-for="(task, index) in tasks"> ここでエラーが発生しました。 仮説 v-bind:keyディレクティブが必要とあり、v-bind:keyを付け加える必要があると考える。 模索 公式にはこのように書かれていました。 -引用- 繰り返される DOM の内容が単純な場合や、性能向上のために標準の動作に意図的に頼る場合を除いて、可能なときはいつでも v-for に key 属性を与えることが推奨されます。 修正 app/javascript/packs/components/index.vue <li v-for="(task, index) in tasks" :key='index'> 配列のindexをつかいkeyを作成し、エラーを解決しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React とか Vue とか Webpack とかで混乱している人のための現代フロントエンド開発入門

序論 本稿は 元市役所職員がWEBプログラマに転職するまでのロードマップ の連載記事の一部です。 本稿は、Node.js ベースの現代的フロントエンド開発についてまとめた入門記事です。 主に以下のような方を対象としています。 HTML, CSS, JavaScript を使った基本的なWebフロントエンド開発ができる方 Node.js ベースの最近のフロントエンド開発について、情報が多すぎてよく分からなくなっている方 React や Vue といった最近のフロントエンドライブラリを使った開発に興味のある方 Webフロントエンド開発の歴史 1991年に Web (World Wide Web) がインターネット上に誕生した当初、Web は文書閲覧のためのものでした。 そのため、現在我々が利用するような SNS や Google Maps のようなインタラクティブコンテンツ(ユーザと Web が対話的にやりとりするようなコンテンツ)は存在せず、ユーザは Web が配信する文書を一方的に受信するだけでした。 1990年代半ば以降、CSS や JavaScript がWebブラウザに搭載されるようになり、よりリッチなコンテンツの配信が可能となってきました。 しかしながら、当時の JavaScript はまだ貧弱で、CSS ともに文書の装飾に用いられることが一般的でした。 同時期、CGI を始めとする Web 向けのサーバサイドプログラム技術が登場しました。 これにより、データベースサーバによるデータ管理/サーバサイドプログラムによる動的なHTML文書の生成/Webクライアント(ブラウザ)という古典的なWebシステムが生まれました。 この、サーバ側がデータベースやアプリケーション本体を担い、クライアントは見た目を担うという形式は形を変えつつ現在でもある程度支持されています。 その後もサーバサイド側は CGI から進化を続け、Ruby on Rails のような MVC (Model View Controller) ベースのフレームワークが登場することで、ECサイトやブログのような複雑なWebシステムが作られるようになってきました。 一方、フロントエンド側はそれほど大きく進化することなく、CSS による装飾、JavaScript によるアラートや入力受付程度の簡単な処理のみを求められていました。 Ajax の登場 前述の通り、しばらくの間フロントエンドは見た目の補助以上の処理を求められませんでしたが、2005年にGoogle社がリリースした Google Maps によりこの常識は覆されました。 Google Maps は地図サービスであるが、当時としては革新的な、ページを遷移することなくWebブラウザ側で地図を拡大・縮小する機能を提供しました。 これによりユーザは、地図を拡大・縮小する際に毎回サーバからの応答を待つことなくシームレスに操作を行うことが可能になったのです。 これは Ajax と呼ばれる、JavaScript によってサーバと非同期に通信する技術によって実現されています。 Ajax の登場により、Webブラウザでもデスクトップアプリケーションのようなインタラクティブなアプリケーション開発が可能となり、フロントエンドはより高度な処理を求められるようになってきました。 例えば、Ajax 通信の結果により DOM (HTMLタグによって構築されている木構造) の構造を変化させるような処理です。 なお上図の【Ajax通信を使ったインタラクティブ処理】において、「サーバからは必要な情報のみ返す」とありますが、このような仕様を Web API と呼びます。 またこの頃、高度な DOM 操作を簡単に実現できるライブラリとして jQuery が人気を集めました。 最近では React.js や Vue.js といった仮想DOM操作ライブラリによって取って代わられつつありますが、それでも jQuery を使ったフロントエンド開発は未だに根強く残っています。 ともあれ、こうして Ajax と Web API により、よりリッチなWeb業務システムなWebサービスを構築することが一般的となり、Webクライアント(ブラウザ)側でも JavaScript を駆使して本格的なプログラムが書かれるようになりました。 この頃から、開発体制としてサーバサイドとクライアントサイドの分業化が見られるようになり、バックエンドエンジニアとフロントエンドエンジニアという職種が現れました。 HTML5の登場とフロントエンド開発の高度化 2000~2010年代にかけて Web はさらに複雑化・高度化していきました。 そして2014年に HTML の新規格として HTML5 が登場し、Web 全体の仕様をアップデートする大きなムーブメントが発生しました。 現在の HTML は HTML Living Standard という規格に統一され、頻繁に仕様追加・変更が行われており、フロントエンド技術は加速度的に進化してきています。 この辺りの詳細については、HTML/CSS から始めるフロントエンド開発 - HTML仕様の歴史 にて言及しております。 こうして、HTML5 の登場とそれに伴う CSS 仕様、JavaScript ライブラリの進化などで、クライアントサイドにおいてもより強⼒な表現が可能となりました。 これを受けて、プレゼンテーション層のプログラムがサーバーサイドからクライアントサイドにシフトしていき、従来サーバーサイドで⾏なっていた HTML の描画がクライアントサイドで行われるようになってきました。 これは、クライアントサイドで HTML 描画を行うことで画⾯遷移を減らし、ユーザにより優れたWeb体験を与えられる、という理由による変遷です。 Node.js による JavaScript エコシステムの進化 2009年、サーバサイド JavaScript 環境である Node.js が登場しました。 これは、元々はサーバサイド側で JavaScript 言語を扱うことができるというだけのものですが、フロントエンド開発にも以下の2つの大きな変化を与えました。 フロントエンド開発・検証の効率化: 本来 JavaScript はブラウザ内で動くため、PC内のファイルを読み込んだり書き込んだりすることはできません しかし、Node.js によりブラウザ外で JavaScript を実行可能となり、コーディング中のファイル監視をして、ファイル変更に合わせて自動的にブラウザをリロードしたりするようなことが可能となりました また、記述した JavaScript コードのテスト等も簡単に行えるようになり、フロントエンド開発・検証が効率的にできるようになりました パッケージマネージャ npm の普及: JavaScript で実装されたライブラリを npm 経由で利用できるようになったことで、開発したものをモジュール化して配布する文化が育ちました これによりコード資産が共有できるようになり、開発効率が格段に向上しました このように、複数の開発者が互いの技術や資本を生かしながら広く共存共栄していく仕組みを エコシステム と呼びます ES2015 によるプログラミング言語としての JavaScript の進化 Webフロントエンド開発が⾼度化するなかで問題になったのが JavaScript の⾔語機能の貧弱さでした 元々ブラウザでちょっとした飾り付け等を行うことを目的として開発された JavaScript は、本格的なアプリケーションを作成する上では物⾜りないところが多かったのです。 そこで⼤々的な仕様のアップデートが求められ、登場したのが ES2015 (ECMAScript 2015) という規格です。 ES2015 は JavaScript の歴史上でも最⼤のアップデートであり、構⽂が増え、const や let 宣言の普及など、書き⽅も⼤々的に変わることになりました。 しかしながら、サーバサイドで動く Node.js などは仕様のアップデートに合わせてバージョンアップすれば済みますが、クライアントサイド(ブラウザ)で動く JavaScript は、仕様が提案されてすぐに全てのブラウザに実装されるわけではなく、統一的に書くことが難しいという問題がありました。 とは言え、多くの仕様変更は古い JavaScript への不満を解消する魅⼒的なものではあったので、こういった仕様をブラウザ実装に先駆けて利⽤しようとする動きが広がってきました。 Babel はこういったニーズに応える JavaScript to JavaScript Compiler です。 すなわち、次世代の JavaScript を、まだその仕様を実装していないブラウザで動作する JavaScript に変換するという訳です。 なお、Babel は基本的に Node.js で動くコンパイラであるため、Babel を使いたい場合は Node.js の導入もほぼ必須となります。 こうして ES2015 以降の仕様の⼈気で、本来はコンパイルという過程を必要としない JavaScript 開発において、JavaScript to JavaScript Compile というビルド手順が増えてしまいました。 このような状況が、最近のフロントエンド開発への入門の敷居を上げているのではないかと感じられます。 Webpack について Webpack はモジュールバンドラの一つで、複数の JavaScript ファイルを1ファイルにパッキングするものです。 ES2015 仕様以前の JavaScript には、別のファイルに記述された JavaScript モジュールを読み込むという機能がないため、それを実現するモジュールバンドラの登場で大規模な JavaScript 開発が可能となりました。 (なお ES2015 では ES Module という仕様が策定され、モジュール読み込みが可能となっています) 特に npm パッケージマネージャを中心としたエコシステムを十分活用するためには、それぞれ分かれて開発されている各モジュールを上手く一つにまとめる機能は必須となります。 そのため、フロントエンド開発において Node.js 環境を導入する場合、ES2015 仕様に未対応のブラウザで動く JavaScript 開発が必要であれば Webpack の使用もほぼ必須となってきます。 ちなみに、Webpack と Babel は利用ケースがほぼ同じため、ごっちゃになってしまっている人も多そうですが、あくまで用途は別のものです。 モジュールバンドラの仕組みは単純で、ES Module の構文(import)を探し、その部分に直接 import 元の JavaScript ファイルの内容を埋め込む、という仕組みになっています。 なお、Webpack の機能として面白いのが、モジュールバンドルの対象が JavaScript だけに限定されないところで、CSS や WebFont 等、比較的何でもバンドルすることができます。 これにより、グローバル汚染されがちなWebデザイン(CSSスタイル)をコンポーネント単位で分割して、スタイルの影響範囲をコンポーネント内に効率的に閉じ込めることも可能です。 余談ですが、最近のブラウザは ES2015 への対応が概ね完了しており、古いブラウザをサポートする目的がなければ必ずしも Webpack は必須ではなくなってきています。 そのため、これから先のフロントエンド開発では ES Module 機能を効率的に扱うための Snowpack のような開発環境が主流になっていく可能性が高いです。 React, Vue 等のフロントエンドライブラリの出現 ここまで紹介してきたようにフロントエンドを取り巻く仕様、技術は⾼度化しています。 これらが可能になったことで、アプリケーション、サービスにおいても複雑な要件が求められるようになりました。 すなわち、アプリケーションデータフローをフロントエンド側で受け持つなど、設計段階から難易度が上がってきたのです。 DOM を Web API と連携させて適切に書き換えるのも考えなしにはできなくなり、こうなってくるとアプリケーションの構造化を持たない jQuery のようなライブラリでは⼒不⾜となってきました。 そのため、MVC のようなアプリケーションの構造を持ったフレームワークが必要とされるようになり、Backbone.js, AngularJS などの新たなWebアプリケーションフレームワーク、ライブラリが次々と出現しました。 この流れの中で現れたのが、Facebook社が開発した React と Flux です。(React はビューライブラリ、Flux はアプリケーションアーキテクチャ) React を中⼼とした開発スタイルは仮想DOMによってDOM操作を⾼速で快適なものにし、また、Flux によって混乱しがちなフロントエンドのアーキテクチャに⽅向性が⽰され、React/Flux は大人気のライブラリとなりました。 React などの登場によって、⾼度なフロントエンドアプリケーションの開発は jQuery 等で無理やりにつくるよりも構造化しやすくなりました。 ここで新しく登場するのが、学習コストの問題です。 React 自身もAPIを⼩さく保つなど学習コストをむやみに増やさない設計をしていますが、JSX、データフローに関する知識など、React 導⼊に当たって少なくない知識が必要とされるのもまた事実です。 このような学習コストの高さを低減するべく開発されたのが Vue というライブラリです。 Vue はシンプルなAPIを提供し、UIの構築にはHTMLベースの平易なテンプレートを利⽤できます。 そのため、HTML や JavaScript を多少触っていれば、Vue 固有の知識がほとんどなくてもすぐに利⽤可能となっています。 一方、React のように固い設計はしづらいというデメリットもあり、大規模なプロジェクトでは React が採用されることが多い印象です。 ともあれ、最近のフロントエンド開発では React や Vue といった構造的なアプリケーション開発可能なライブラリを採用することが多くなっているということです。 Node.js を使わない Vue, React 入門 前述の通り、Node.js を使うことでフロントエンド開発・検証を効率的に行うことができます。 また、Node.js で動く Babel という JavaScript to JavaScript Compiler を使えば、ブラウザごとの JavaScript 仕様の違いを気にせず、最新の JavaScript 仕様(ES2015 以降の仕様)を使ってプログラムを書くこともできます。 しかしながら、これらはあくまで開発効率を向上させるための手段であり、Vue や React のような最新ライブラリを使うのに必須なわけではありません。 最終的に各ブラウザに搭載された JavaScript エンジンが、JavaScript ファイルを解釈して実行するという基本原理は今も昔も変わらないためです。 そのためここでは、まず Node.js を開発に使わない古典的な手法で Vue, React ライブラリを使ったフロントエンド開発を体験してみます。 Environment Terminal: Bash (Ubuntu 20.04) Zsh (macOS) PowerShell (Windows 10) Editor: VSCode Live Server Extension Browser: Google Chome ここでは、VSCode をエディタとして採用し、Live Server 拡張機能を使うことにします。 無論これは必須ではありませんが、ファイルの変更を検知して自動的にブラウザのページをリロードしてくれたりして便利なため採用しています。 詳細については HTML/CSS から始めるフロントエンド開発 - VSCodeエディタを使ったフロントエンド開発 を参照してください。 また、ブラウザは極力 Google Chrome を使うことを推奨しています。 前述の通り、ブラウザごとに JavaScript で使用可能な文法等の仕様が異なり、以降で掲載しているコードが必ずしもすべてのブラウザで問題なく実行されるか未検証のためです。 Node.js を使わない Vue 入門 ターミナル(Bash, Zsh, PowerShell 等)から以下の通りプロジェクトディレクトリの作成とVSCodeの起動を行います。 # プロジェクトディレクトリ `simple-vue` 作成 $ mkdir simple-vue # simple-vue ディレクトリで VSCode 起動 $ cd simple-vue/ $ code . VSCode を開いたら、以下のようにファイルを作成します。 simple-vue/ |_ index.html # トップページ |_ main.js # index.html から読み込まれる JS ファイル index.html index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Node.js を使わない Vue 入門</title> <!-- 簡単に見栄えを良くするため Bootstrap 5 CSS を CDN 経由でロード --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"> <!-- Vue 3.1.5 を CDN 経由でロード --> <script src="https://unpkg.com/vue@3.1.5"></script> </head> <body> <div id="app" class="container my-4"> <h1 class="title">Node.js を使わない Vue 入門</h1> <div class="card"> <div class="card-body"> <h2 class="card-title">Vue によるカウンター</h2> <p class="card-text"> <!-- {{ 変数 }}: Vue により DOM 操作され、変数が埋め込まれる --> Count: <span class="text-light bg-dark px-2 py-1">{{ count }}</span> </p> <div class="d-flex justify-content-end"> <!-- @click.prevent: ボタンのデフォルトクリックイベントを消し、Vue 側で定義した countUp メソッドを実行する --> <button class="btn btn-primary me-5" @click.prevent="countUp">カウントアップ</button> </div> </div> </div> </div> <!-- このページ用のメイン JS スクリプトをロード ※ Vue を使う場合は、必ず body タグの最後でスクリプトを実行すること --> <script src="./main.js"></script> </body> </html> 上記のように、Vue では HTML に特殊な記法を用いることができ、{{ 変数名 }} という書き方で Vue 側の変数を埋め込むことができます。 また、@イベント名 (正式には v-on:イベント名) という属性を指定することも可能で、これにより Vue 側で定義したイベントメソッドを実行させることができます。 他にも様々な HTML 拡張記法があるため 公式ガイド を確認すると良いです。 main.js main.js /** * 本スクリプトには ES2015 以降の文法が含まれる(オブジェクトイニシャライザの短縮メソッド定義等) * モダンなブラウザでは基本的にサポートされているはずだが、動かない場合は最新の Google Chrome で確認すること */ // Vue Application 定義 const VueApp = { // Vue 変数宣言: ここで返したオブジェクトマップが変数として利用可能 data() { return { // {{ count }} は初期状態で 0 として表示される count: 0, } }, // メソッド宣言: ここで定義したメソッドを v-on:*** (もしくは @***) イベントで呼び出すことができる methods: { // countUp メソッド: Vue 変数 `count` をインクリメントする countUp() { this.count++; } } }; // id="app" のエレメントをマウントし、Vue 側で HTML を構築できるようにする Vue.createApp(VueApp).mount('#app'); 上記のように、変数やメソッドを定義したオブジェクトを Vue.createApp に渡し、それを HTML エレメントにマウントすることで Vue で仮想DOMを操作できるようになります。 なお、ここでは HTML と JavaScript を別ファイルに分けていますが、コンポーネント単位で HTML/CSS/JavaScript を一つのファイルにまとめて記述する機能もあります。(Single File Component (SFC)) しかし、SFC機能は基本的に Node.js 環境を構築して、SFC(.vue ファイル)をブラウザで実行可能な JavaScript ファイルに変換して使う形になります。 動作確認 VSCode 右下の「Go Live」アイコンをクリックするか、F1 キー > Live Server: Open with Live Server コマンドを実行すると、ブラウザが起動してページを確認できるはずです。(なお Alt + L |> Alt + O キーでも Live Server を起動できます) 以下のようにカウンターアプリケーションが実行されればOKです。 Node.js を使わない React 入門 ターミナル(Bash, Zsh, PowerShell 等)から以下の通りプロジェクトディレクトリの作成とVSCodeの起動を行います。 # プロジェクトディレクトリ `simple-react` 作成 $ mkdir simple-react # simple-react ディレクトリで VSCode 起動 $ cd simple-react/ $ code . VSCode を開いたら、以下のようにファイルを作成します。 simple-react/ |_ index.html # トップページ |_ main.jsx # index.html から読み込まれる JSX ファイル JSX という見慣れないファイルがありますが、これは HTMLタグ(風のタグ)を直接 JavaSCript コードの中で書けるように拡張された JavaScript 拡張言語 です。 これについて、詳しくは後述します。 index.html index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Node.js を使わない React 入門</title> <!-- 簡単に見栄えを良くするため Bootstrap 5 CSS を CDN 経由でロード --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css"> <!-- React 16 を CDN 経由でロード --> <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <!-- React JSX スクリプトを解釈できるようにするための Babel ライブラリをロード --> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> </head> <body> <div id="app" class="container my-4"><!-- この中の HTML は React 側で構築 --></div> <!-- このページ用のメイン JSX スクリプトをロード ※ React を使うには JSX という JavaScript 拡張言語を使う必要がある ※ JSX を使うには Bebel ライブラリをロードして script type="text/babel" でスクリプトを記述する --> <script type="text/babel" src="./main.jsx"></script> </body> </html> JSX は JavaScript の拡張言語であるため、そのままではブラウザで実行することはできません。 こういった場合、通常は Node.js + Babel を使って JSX to JavaScript Compile を行い、JSX をブラウザで実行可能な JavaScript ファイルに変換する必要があります。(実は Bebel は ES2015 以降の文法だけでなく JSX 構文も JavaScript に変換できます) そのため React でのフロントエンド開発には、基本的に Node.js の導入が不可欠なのですが、実はピュアJS(ブラウザで実行可能な JavaScript)で書かれた Bebel Compiler も配布されており、これを利用することで Node.js なしに JSX ファイルを直接ブラウザで実行することが可能なのです。 動作原理自体はシンプルで、ピュアJS製の Babel は、HTML 内にある <script type="text/babel"> というタグを検索し、その中に記述された JSX コードを JavaScript にコンパイルした上で実行しています。 すなわち、本来は事前に JSX to JavaScript Compile された JavaScript ファイルを読み込んで実行するところを、ブラウザ上で JSX to JavaScript Compile して、そのコンパイル結果の JavaScript を実行しているということです。 ブラウザにコンパイルを任せてしまう分だけ、動作速度は遅くなってしまうため注意が必要です。 main.jsx main.jsx /** * App component * @description React では JSX という独自言語で HTML タグをそのまま使うことができる * @returns {JSX} */ function App() { // カウンタ変数: 初期値 0 let [count, setCount] = React.useState(0); // カウンタ変数をインクリメントする関数: カウントアップ button の onClick イベントとして埋め込まれる function countUp(e) { e.preventDefault(); // ボタンクリックのデフォルトイベントを削除 // React で変数の値を更新するためには React.useState の第2戻り値の関数を使う必要がある setCount(count + 1); } /** * JSX の書き方: * - JSX では複数の HTML タグを同じ階層に配置して扱うことはできない * - そのため複数のタグを扱いたい場合は、適当なタグの中に入れて一つのタグにまとめる必要がある * - class の代わりに className という属性を使う必要がある * - 変数や関数は { 変数(関数)名 } という形で埋め込む */ return ( <div> <h1 className="title">Node.js を使わない React 入門</h1> <div className="card"> <div className="card-body"> <h2 className="card-title">React によるカウンター</h2> <p className="card-text"> Count: <span className="text-light bg-dark px-2 py-1">{ count }</span> </p> <div className="d-flex justify-content-end"> <button className="btn btn-primary me-5" onClick={ countUp }>カウントアップ</button> </div> </div> </div> </div> ); } /** * React JSX で構築した HTML を id="app" のエレメントの中に描画 * @description React JSX を返す関数は <関数名 /> という独自タグとして呼び出すことができる */ ReactDOM.render(<App />, document.getElementById('app')); 上記のように JSX では、普通の JavaScript 関数の戻り値として HTML タグ風のタグをそのまま記述するようなことが可能です。 このような記述が Babel によりピュアJSに変換されるわけですが、実際どのように変換されるのかは理解しておいたほうが良いです。 例えば以下のような JSX コードがあるとして、 function Hello() { return <p>Hello</p>; } これは以下のような JavaScript に変換されます。 function Hello() { return React.createElement('p', null, 'Hello'); } つまり、JSX タグは最終的に React.createElement により仮想DOM要素として生成されることになります。 この辺りの話はやや難しいため、実際 React をプロジェクトで採用する際に改めて勉強すると良いと思います。 動作確認 Live Server を起動して、以下のようにカウンターアプリケーションが実行されればOKです。 Node.js 入門 前述の通り、Vue も React もただ使うだけであれば、Node.js を開発環境に導入する必要はありません。 しかしながら、Vue SFC や React JSX 等、事前にピュアJSにコンパイルしておいた方が、ブラウザ上でいちいちコンパイルしなくて良くなるためパフォーマンス的に有利です。 そこでここでは Node.js 環境を構築し、その基本的な使い方を習得します。 Setup WSL2 + Ubuntu 20.04 + Docker 開発環境構築 で環境構築してある場合は、anyenv + nodenv を使って Node.js 環境済みのはずです。 上記で Linux (Ubuntu 20.04) での環境構築方法は紹介済みのため、ここでは macOS (11 Big Sur) と Windows 10 (WSL2 を使わない場合) での環境構築方法を掲載します。 なお、環境構築時に Node.js のサードパーティ製パッケージマネージャとして yarn を導入していますが、これはかつての npm パッケージマネージャがインストール速度やセキュリティの問題を抱えていたためです。 現在では npm 側に yarn の多くの機能が取り込まれ、実質的な機能の差はほとんどなくなっている ため、必ずしも yarn を導入する必要はありませんが、筆者は今も yarn を好んで使っているので導入しています。 Setup on macOS 11 Big Sur 基本的には Linux と大きく変わりません。 Command + Space |> terminal.app (以下、デフォルトシェルが zsh である前提で進めます) # --- anyenv 導入 --- # Homebrew 未導入の場合は導入しておく ## 最近のインストーラは自動的に Xcode Command Line Tools も入れてくれるため、一通りの開発環境は簡単に整う $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" # Linuxbrew で anyenv 導入 $ brew install anyenv $ anyenv install --init ## Do you want to checkout ? [y/N]: <= y # anyenv 初期化スクリプトを .zshrc に記述 $ echo 'eval "$(anyenv init -)"' >> ~/.zshrc $ source ~/.zshrc # anyenv update plugin の導入 $ mkdir -p $(anyenv root)/plugins $ git clone https://github.com/znz/anyenv-update.git $(anyenv root)/plugins/anyenv-update $ anyenv update # バージョン確認 $ anyenv -v anyenv 1.1.4 # --- pyenv 導入 --- ## npm package の中には python を必要とするものも多いため、ここで Python 環境を導入しておく # anyenv を使って pyenv 導入 ## pyenv を使うことで、複数バージョンの Python 環境を構築できる $ anyenv install pyenv $ exec $SHELL -l # pyenv で Python 2.7.18 と 3.7.7 をインストール $ pyenv install 2.7.18 $ pyenv install 3.7.7 # pyenv では 2系 と 3系 を同時に指定できる ## python => 2.7.18 ## python3 => 3.7.7 $ pyenv global 2.7.18 3.7.7 # 現在選択されているバージョンを確認 $ pyenv versions * 2.7.18 (set by /home/user/.anyenv/envs/pyenv/version) * 3.7.7 (set by /home/user/.anyenv/envs/pyenv/version) $ python --version 2.7.18 $ python --version 3.7.7 # pip パッケージマネージャを更新しておく $ pip install --upgrade pip setuptools $ pip3 install --upgrade pip setuptools # --- nodenv 導入 --- # anyenv を使って nodenv 導入 ## nodenv を使うことで、複数バージョンの Node.js 環境を構築できる $ anyenv install nodenv $ exec $SHELL -l ## nodenv-yarn-install プラグイン導入: nodenv install 時に yarn もインストールする $ mkdir -p "$(nodenv root)/plugins" $ git clone https://github.com/pine/nodenv-yarn-install.git "$(nodenv root)/plugins/nodenv-yarn-install" $ echo 'export PATH="$HOME/.yarn/bin:$PATH"' >> ~/.zshrc # Node.js インストール可能なバージョンを確認 $ nodenv install --list # Node.js 14.17.5 インストール $ touch $(nodenv root)/default-packages $ nodenv install 14.17.5 # Node.js 14.17.5 に切り替え $ nodenv global 14.17.5 # 現在選択されているバージョンを確認 $ nodenv versions * 14.17.5 (set by ~/.anyenv/envs/nodenv/version) # 一度シェルを再起動しないと Node.js が使えない $ exec $SHELL -l # バージョン確認 $ node -v v14.17.5 $ yarn -v 1.22.11 Setup on Windows 10 Windows 環境の場合、WSL2 を導入可能であれば WSL2 + anyenv + nodenv 環境を使うのが現状の最適解と思われます。 しかし、事情により WSL2 を使えない場合もあると思われるため、ここでは WSL2 を使わない場合の Windows 10 での環境構築手順を掲載します。 Windows 10 ネイティブ環境では nodenv が使えないため、nvm-windows を使います。(無論、無理して Node.js のバージョン管理システムを導入しなくても良いのですが、手動でバージョン管理しようとすると複数の案件を回そうとしたときに辛くなりやすいです) Win + X |> A => 管理者権限 PowerShell 起動 # パッケージマネージャとして Chocolatey ではなく scoop を使う ## Chocolatey で nvm を導入した場合、非管理者権限で npm グローバルインストール系のコマンドがこけることが多かったため ### (おそらく `C:\Program Files\` で node.js 周りのファイル管理をしているためと思われる) > iwr -useb get.scoop.sh | iex # powershell script の実行ポリシーを付与 > Set-ExecutionPolicy -ExecutionPolicy RemoteSigned # => 設定変更を反映するため、一度 PowerShell 再起動 # scoop で nvm インストール > scoop install nvm # => 自動的に環境変数情報が変更されるため、再び PowerShell 再起動 # nvm で Node.js 14.17.5 インストール > nvm install 14.17.5 # Node.js 14.17.5 を使う > nvm use 14.17.5 # npm, yarn パッケージマネージャを更新しておく > npm update -g npm yarn # yarn global bin の PATH をユーザ環境変数に追加しておく > [System.Environment]::SetEnvironmentVariable("PATH", [System.Environment]::GetEnvironmentVariable("PATH", "User") + ";$(yarn global bin)", "User") # バージョン確認 $ node -v v14.17.5 $ yarn -v 1.22.11 Node.js で Hello, world 以上で Node.js が使えるようになったため、動作確認を兼ねて Hello, world を表示してみましょう。 ファイル名は任意ですが、ここでは hello.js というファイルを作成し、以下のように JavaScript コードを記述してみます。 # hello.js ファイルを作成しながら VSCode で開く $ code hello.js hello.js // コンソールに "Hello, world" と表示 console.log('Hello, world'); スクリプトファイルを作成したら、以下のコマンドで Node.js を実行します。 # Node.js で hello.js ファイルを実行する $ node hello.js Hello, world npm でパッケージをインストールしてみる Node.js エコシステムには便利なパッケージが多くあります。 ここでは、コンソールに色をつけることのできる colors というパッケージを npm でインストールして使ってみましょう。 # npm で colors パッケージインストール ## --save (-S) オプション: パッケージのインストール情報も保存 $ npm install --save colors ## 短縮形で `npm i -S colors` という書き方も可 # npm の代わりに yarn パッケージマネージャを使うこともできる ## 筆者は yarn の方が使いやすいと感じているため、こちらを使うことが多い # yarn で colors パッケージインストール ## yarn の場合は特にオプションをつけなくても自動的にパッケージのインストール情報も保存される $ yarn add colors 上記コマンドを実行すると、カレントディレクトリに node_modules/ ディレクトリが作成されます。 このディレクトリ内に各種パッケージがインストールされるという仕組みになっています。 なお、--save (短縮形: -S) オプションを付けると、一緒に package.json というファイルも作成されますが、このファイルにはインストールパッケージの情報等が記述されています。 このファイルがあれば node_modules/ ディレクトリを削除しても package.json の情報をもとに、使われていたパッケージを再インストールすることができるようになっています。 (チーム開発においては、node_modules/ ディレクトリは共有せず、package.json ファイルのみ共有して各開発者がそれぞれパッケージをインストールして開発を進めることが多いです) ※ nvm-windows 版の npm について、筆者環境では package.json が生成されないバグが発生することがありました => そのため、Windows 10 ネイティブ環境では yarn パッケージマネージャを使う方が良いかもしれないです # ./package.json の情報をもとにパッケージを一括インストール ## 短縮形で `npm i` という書き方も可 $ npm install # yarn の場合は `yarn install` もしくは `yarn` コマンドで同じことができる # ./package.json の情報をもとにパッケージを一括インストール $ yarn 話がそれてしまいましたが、インストールした colors パッケージを使って、コンソールに色付き文字を表示してみましょう。 hello.js // Node.js では require 関数を使って、別ファイルに記述された JavaScript モジュール(パッケージ)を読み込むことができる // 以下のようにして colors パッケージを読み込む require('colors'); // 黄色テキストで "Hello, world" 表示 console.log('Hello, world'.yellow); これで node hello.js を実行すると、黄色テキストで Hello, world と表示されるはずです。 パッケージのグローバルインストールとローカルインストール 今回、パッケージは作業ディレクトリにローカルインストールしましたが、作業ディレクトリ以外の JavaScript プログラムからも読み込むことができるようにグローバルインストールすることも可能です。 その場合は npm に -g オプションをつけてインストールを行います。 # colors パッケージをグローバルインストール $ npm install -g colors ## 短縮形で `npm i -g colors` という書き方も可 # yarn の場合は `yarn global ***` という形でコマンドを呼び出す # colors パッケージをグローバルインストール $ yarn global add colors なお、require でグローバルインストールしたパッケージを読み込みたい場合、環境変数 NODE_PATH で npm グローバルインストール先ディレクトリを設定しておく必要があるため注意が必要です。 本稿では、環境構築時に NODE_PATH の設定を行っていないが、これは基本的にグローバルインストールしてパッケージを使うことを想定していないためです。 パッケージをグローバルインストールしてしまうと、他の開発環境において同じパッケージ環境を再現するのが難しくなってしまうため、本稿においては、グローバルインストールして使うのは npm や yarn のようなコマンドとして利用されることを前提としているパッケージのみとしています。 今回サンプルとして使った colors のような、require で読み込んで使うパッケージは、ローカルインストールして使うことを推奨しています。 ローカルインストールであれば、package.json ファイルを共有するだけで、同じパッケージ環境を再現できるためです。 依存パッケージのアップデート Node.js で開発をしていると、依存パッケージが高い頻度で更新されて困ることが多いです。 特に GitHub リポジトリでコード管理していると、「このパッケージは脆弱性があります、そのパッケージは推奨されません」という親切なセキュリティアラートで埋め尽くされることが度々あります。 こういった場合、npm outdated コマンドを使うことで各パッケージのアップデートを確認することはできますが、一つ一つのパッケージをすべて確認してアップデートを行うのは非常に面倒です。 また通常、パッケージのアップデートは npm update コマンドを用いて行いますが、このコマンドの問題点として package.json に記述されたバージョンの範囲で しか最新バージョンにアップデートしてくれないという問題があります。 例えば、"package": "^3.2.1" などのように記述されている場合、そのパッケージの最新版として 4.0.0 がリリースされていたとしても、そのバージョンまでは上げてくれないということです。 そういった場合は手動で npm install package@4.0.0 のようにバージョンを指定して再インストールするしかありません。 以上のような理由で、依存パッケージのバージョン管理を行うのに npm コマンドだけでは労力がかかってしまうため、npm-check-updates というパッケージを利用すると便利です。 npm-check-updates の導入 基本的には npm-check-updates パッケージをグローバルインストールして npm-check-updates コマンドを使えるようにすれば良いです。 # npm-check-updates のグローバルインストール $ npm i -g npm-check-updates # 以降、`npm-check-updates` コマンドが使えるようになる $ npm-check-updates -h Usage: npm-check-updates [options] [filter] : しかし、正直 npm-check-updates コマンドはそれほど頻繁に使うようなコマンドではないため、あまりグローバルインストールして環境を汚したくない、という場合も多いです。 そういった場合に便利なコマンドとして npx というコマンドが用意されています。 これは npm コマンドをより簡単に使えるように拡張されたコマンドで、npm@5.2.0 から同梱されており、Node.js 8.2 以降であればデフォルトでインストールされているはずです。 npx は以下のような機能を有しています。 run-script を使用せずにローカルインストールしたコマンドを実行可能 グローバルインストールせずに一度だけコマンドを実行可能 GitHub や Gist で公開されているコマンドを実行可能 上記2番目の機能を利用することで、グローバルインストールすることなく一度だけ npm-check-updates コマンドを実行することが可能です。 # グローバルインストールせずに一度だけ npm-check-updates コマンドを実行 $ npx npm-check-updates -h Ok to proceed? (y) # <= そのまま Enter して実行 Usage: npm-check-updates [options] [filter] : npm-check-updates による package.json の更新 npm-check-updates コマンドの動作原理は単純で、package.json に記述された各パッケージのバージョン情報を確認し、最新バージョンがあれば package.json に記述された各パッケージのバージョン情報を書き換えるだけです。 これにより npm install コマンドを実行するだけで最新バージョンのパッケージが再インストールされる、という仕組みです。 # ncu でアップデート可能なパッケージの確認 $ npx npm-check-updates -c 'ncu' Checking package.json : # ncu -u を実行すると package.json が更新される $ npx npm-check-updates -c 'ncu -u' Checking package.json : Run npm install to install new versions. # 更新された package.json をもとに npm install の実行 ## => 最新パッケージが再インストールされる $ npm install Webpack 開発入門 ここまでで Node.js の使い方はある程度確認できました。 次は Node.js のエコシステム (npm パッケージ資産) をクライアントサイド(ブラウザ内蔵の JavaScript エンジン)で利用することを考えてみましょう。 前述の通り、ES2015 仕様以前のクライアントサイドJSではモジュール読み込み機能(ES Module)がないため、npm パッケージの資産を効率よく利用するためには、Webpack のようなモジュールバンドラを利用する必要があります。 Webpack + Babel 環境構築 まずは、Webpack と Babel をインストールします。 改めてそれぞれのパッケージの目的を以下にまとめておきます。 Webpack: ES2015 以前の JavaScript しかサポートしていないブラウザにおいて、ES Module の機能を使って npm パッケージ資産を効率よく利用するために必要 別ファイルに実装された JavaScript モジュールや CSS, WebFont 等を一つの JavaScript ファイルにパッキングすることができる Babel: ES2015 以前の JavaScript しかサポートしていないブラウザにおいて、ES2015 の構文を使うために必要 ES2015 の構文を古い JavaScript の構文に変換することができる # yarn で Webpack系のパッケージと Babel系の必要なパッケージをインストール ## -D オプション: devDependencies (開発環境用の依存パッケージ) としてパッケージをローカルインストール $ yarn add -D webpack webpack-cli babel-loader @babel/core @babel/preset-env babel-polyfill # npm を使う場合 # $ npm i -D webpack webpack-cli babel-loader @babel/core @babel/preset-env babel-polyfill webpack.config.js 続いて、Webpack の設定ファイルである webpack.config.js をカレントディレクトリに作成し、以下のように記述します。 webpack.config.js // 絶対パスを記述する際などに必要なため path パッケージを使う const path = require('path'); module.exports = { // 実行モード: development => 開発, production => 本番 // production を指定すると、パッキング後のファイルサイズを削減し、より早く JS ファイルを読み込めるようになる // webpack4系以降はmodeを指定しないと警告が出る mode: 'development', // エントリーポイント: ソースとなる JS ファイル // ここで指定した JS ファイルに、必要なモジュールをすべて読み込む想定 entry: './src/index.js', // 出力設定 output: { // バンドル後のファイル名 filename: 'bundle.js', // 出力先のパス(※絶対パスで指定すること) path: path.join(__dirname, 'public') }, // => ここまでの設定で ./src/index.js(と関連モジュール)が ./public/bundle.js にパッキングされる // モジュール読み込みの設定 module: { rules: [ // JavaScript(.js ファイル)読み込み設定 { test: /\.js$/, // 指定正規表現にマッチするファイルを読み込む // ファイル読み込み時に使うローダーを指定 use: [ // babel-loader: ES2015以上の JavaScript をすべてのブラウザで使える JavaScript にトランスコンパイルするローダー { loader: 'babel-loader', // babel-loader のオプション options: { // @babel/preset-env をプリセットとして使うといい感じの JavaScript にトランスコンパイルしてくれる presets: ['@babel/preset-env'] } }, ] }, ] }, }; Webpack 動作確認 Webpack の設定が完了したら、ES2015 以上の JavaScript のコードを書いて、Webpack + Babel で変換しながら1ファイルにバンドルしてみましょう。 まず、バンドル後の JavaScript ファイル(bundle.js)を読み込むだけの HTML を ./public/index.html に作成します。 public/index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>動作確認</title> </head> <body> <!-- Webpack でバンドルされた bundle.js を読み込む --> <script src="bundle.js"></script> </body> </html> 続いて、Webpack のバンドル対象としてエントリーポイントに設定した ./src/index.js と、モジュール用の ./src/mylib.js を作成します。 src/mylib.js // ES2015 以上の JavaScript はアロー関数が使える const hello = () => { alert("こんにちは!Webpack"); }; // hello関数を export export { hello }; src/index.js // ES Module 機能を使い ./mylib.js を読み込む import { hello } from "./mylib"; // IE11/Safari9用のpolyfill // babel-polyfill を import するだけで IE11/Safari9 に対応した JavaScript にトランスコンパイルされる import 'babel-polyfill'; // mylib.jsに定義された hello 関数を実行 hello(); ここまでで、プロジェクトディレクトリは以下のような構成になっているはずです。 ./ |_ node_modules/ # Node.js の各種パッケージがインストールされている | |_ public/ | |_ index.html # bundle.js を読み込む HTML | |_ src/ | |_ index.js # エントリーポイント(メインソースファイル) | |_ mylib.js # index.js から読み込まれるモジュール | |_ package.json # インストールしたパッケージ情報等が記述されている |_ webpack.config.js # Webpack バンドル設定 |_ yarn.lock # yarn パッケージマネージャを使っていると作られる lock ファイル 問題なければ、Webpack を使って JavaScript ファイルをバンドルしてみましょう。 # yarn webpack で ./node_modules/.bin/webpack が実行される ## npm を使う場合は `npm run webpack` $ yarn webpack ## => webpack.config.js の設定に従って Webpack が実行される ## => ./public/bundle.js が作成されるはず バンドル済みファイル ./public/bundle.js が作成されたら VSCode Live Server を起動して、public/ ディレクトリを確認します。 http://localhost:5500/public/ が開かれ、「こんにちは!Webpack」というアラートが表示されたらOKです。 Webpack開発サーバの導入 今のままでは、ファイルを修正したりした際、毎回 Webpack を実行しブラウザで確認するという作業をしなければなりません。 特にフロントエンド開発では、デザインやレイアウトの修正が頻繁に行われるため、ファイルの変更を監視して自動的にコンパイル&ブラウザリロードできると便利です。 Webpack にもこういった自動化ツールが存在するため導入しておきましょう。 # Webpack開発サーバをインストール $ yarn add -D webpack-dev-server webpack-dev-server をインストールしたら、webpack.config.js に設定を追加します。 webpack.config.js // ...(略)... module.exports = { // ...(略)... // モジュール読み込みの設定 module: { // ...(略)... }, // 開発サーバー設定 devServer: { // 起点ディレクトリを ./public/ に設定 => ./public/index.html がブラウザで開かれる contentBase: path.join(__dirname, 'public'), // ポートを 3000 に設定 // 重複しないポートを指定すること port: 3000, }, }; ここまで設定したら webpack-dev-server を起動します。 # Webpack開発サーバを実行 $ yarn webpack serve ## => 開発サーバを終了したい場合は Ctrl + C キー この状態で http://localhost:3000 にアクセスすると ./public/index.html の内容が表示されるはずです。 これだけでは何が良いのかよく分からないと思うため、この状態のまま ./src/mylib.js を以下のように変更してみましょう。 src/mylib.js const hello = () => { alert("Webpack Development Server による自動コンパイル&ブラウザリロード"); }; // hello関数を export export { hello }; ファイルを保存した瞬間に Webpack によるコンパイル処理が走り、./public/bundle.js が更新 => http://localhost:3000 ページが自動リロードされたはずです。 即座に変更されたプログラムの内容をブラウザで確認することができるため非常に便利です。 npm script を書いてみる Webpack開発サーバは yarn webpack serve という比較的長いコマンドを打たなければならないので面倒な場合も多いです。 こういった場合には npm script を書くと便利です。 npm script は package.json の scripts 項目に定義するコマンドスクリプトで、様々なコマンドを自由に記述することができます。 ここでは、webpack serve コマンドを start コマンドで実行できるように、以下のように記述してみます。 { "scripts": { "start": "webpack serve" }, "devDependencies": { // ...(略)... } } 上記のように scripts を記述すると yarn start コマンドで yarn webpack server を呼び出すことができます。 # `yarn start` で `yarn webpack serve` コマンドを間接実行 $ yarn start Vue 開発環境構築 Node.js と Webpack によるフロントエンド開発環境が整ったため、続いて Vue 開発環境を構築していきます。 Vue と VueLoader のインストール # vue, vue-loader インストール $ yarn add -D vue vue-loader vue-template-compiler インストールしたら webpack.config.js に設定を追加し、.vue ファイルを vue-loader でコンパイルできるようにします。 webpack.config.js // 絶対パスを記述する際などに必要なため path パッケージを使う const path = require('path'); // vue-loader plugin を使う const VueLoaderPlugin = require('vue-loader/lib/plugin'); module.exports = { // ...(略)... // モジュール設定 module: { rules: [ // JavaScript(.js ファイル)読み込み設定 { // ...(略)... }, // Vue単一ファイルコンポーネント(.vue ファイル)読み込み設定 { // 拡張子 .vue の場合 test: /\.vue$/, // vue-loaderを使って .vue ファイルをコンパイル use: [ { loader: "vue-loader", }, ], }, ] }, // import文で読み込むモジュールの設定 resolve: { extensions: [".js", ".vue"], // .js, .vue をimport可能に modules: ["node_modules"], // node_modulesディレクトリからも import できるようにする alias: { // vue-template-compilerに読ませてコンパイルするために必要な設定 vue$: 'vue/dist/vue.esm.js', }, }, // VueLoaderPluginを使う plugins: [new VueLoaderPlugin()], // 開発サーバー設定 // ...(略)... }; Vue の動作確認 設定が完了したら Vue の動作確認を行うために、以下のようにプロジェクトを構成します。 ./ |_ node_modules/ | |_ public/ | |_ index.html | |_ bundle.js # Webpack が作成するバンドル済みファイル | |_ src/ | |_ App.vue # index.js から読み込まれる Vue単一ファイルコンポーネント(Vue SFC) | |_ index.js | |_ package.json |_ webpack.config.js |_ yarn.lock public/index.html public/index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Vue 動作確認</title> </head> <body> <!-- Vueにより id="app" の要素が置き換えられる --> <div id="app"></div> <!-- Webpack でバンドルされた bundle.js を読み込む --> <script src="bundle.js"></script> </body> </html> src/App.vue ここでは Vue SFC については詳しく記載しませんが、テンプレート(HTML に Vue の機能を埋め込んだもの)とスクリプト(Vue JavaScript)およびスタイル(CSS, SCSS)を一つのファイルにまとめたものです。 詳しくは 公式リファレンス を参照してください。 src/App.vue <template> <div> <!-- script タグで定義された Vue 変数 `message` を表示 --> <p>{{ message }}</p> </div> </template> <script> export default { // Vue 変数宣言 data() { return { // {{ message }} で埋め込まれる変数 message: 'Hello, Vue!', }; }, } </script> src/index.js src/index.js import Vue from 'vue'; // Vue を使う import App from './App'; // ./App.vue を読み込む // IE11/Safari9用のpolyfill // babel-polyfill を import するだけで IE11/Safari9 に対応した JavaScript にトランスコンパイルされる import 'babel-polyfill'; new Vue({ el: '#app', // Vueでマウントする要素 render: h => h(App), // App.vue をレンダリング }); 動作確認 # webpack-dev-server 起動 $ yarn webpack serve http://localhost:3000 にアクセスして「Hello, Vue!」と表示されたらOKです。 Vuetify を使ってみる せっかくなので、Webpack が CSS や Icon などもパッキングできることを確認しておきましょう。 動作確認用に今回は Vuetify を使ってみます。 Vuetify は、Googleが提唱したマテリアルデザインの考えにのっとって構成された Vue ベースの UI コンポーネントで、格好いいデザインのインタフェースを手軽に作成することができます。 # CSS, Icon 等を Webpack で読み込むためのローダーをインストール $ yarn add -D css-loader style-loader url-loader # Vuetify インストール $ yarn add -D vuetify # フォントアイコン類をインストール $ yarn add -D material-design-icons-iconfont @fortawesome/fontawesome-free 各種パッケージをインストールしたら webpack.config.js に設定を追加し、.css や .svg 等のファイルをバンドルできるようにします。 webpack.config.js // ...(略)... module.exports = { // ...(略)... // モジュール設定 module: { rules: [ // ...(略)... // スタイルシート(.css ファイル)読み込み設定 { // .css ファイル: css-loader => vue-style-loader の順に適用 // - css-loader: cssをJSにトランスコンパイル // - style-loader: <link>タグにスタイル展開 test: /\.css$/, use: ['style-loader', 'css-loader'] }, /* アイコンローダーの設定 */ { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'url-loader?mimetype=image/svg+xml' }], }, { test: /\.woff(\d+)?(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'url-loader?mimetype=application/font-woff' }], }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'url-loader?mimetype=application/font-woff' }], }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'url-loader?mimetype=application/font-woff' }], }, ] }, // ...(略)... }; 設定したら、ソーススクリプトを修正し、Vuetify に対応させましょう。 src/App.vue src/App.vue <template> <!-- Vuetifyコンポーネントを使う場合は v-appタグで囲むこと! --> <v-app> <v-content> <!-- Alertコンポーネントを使ってみる --> <v-alert :value="true" type="success">{{ message }}</v-alert> </v-content> </v-app> </template> <script> export default { // Vue 変数宣言 data() { return { // {{ message }} で埋め込まれる変数 message: 'Hello, Vuetify!', }; }, } </script> src/index.js src/index.js import Vue from 'vue'; // Vue を使う import App from './App'; // ./App.vue を読み込む // IE11/Safari9用のpolyfill // babel-polyfill を import するだけで IE11/Safari9 に対応した JavaScript にトランスコンパイルされる import 'babel-polyfill'; // vuetify を使う import Vuetify from 'vuetify'; // vuetify のスタイルシートを読み込む import 'vuetify/dist/vuetify.min.css'; // material-design-icons を読み込む import 'material-design-icons-iconfont/dist/material-design-icons.css'; Vue.use(Vuetify); // Vuetifyのコンポーネントを使用可能に new Vue({ el: '#app', // Vueでマウントする要素 vuetify: new Vuetify(), // Vuetify を使う render: h => h(App) // App.vue をレンダリング }); 動作確認 修正したら webpack-dev-server を起動し http://localhost:3000 を確認しましょう。 Vuetify が適用されていれば以下のような見た目になるはずです。 本稿は以上になります。 可能であれば、Snowpack + React の話もしたかったのですが、また時間のあるときに更新するかもしれません。 引き続きどうぞよろしくお願いいたします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vueでお絵かきアプリ作成1(canvas使用)

趣向 vueでお絵かき機能を作成する機会があったため作ってみました! はじめに このページではお絵描きできるCanvasの実装まで行います。 まずはvueの作成。 widthとheight で枠の大きさ、lineWidthでペンの太さ。strokeStyleでペンの色を決めることができます。 JavaScript new Vue({ el: '#app', data() { return { weightNum: 5, canvas: null, color: "black", context: null, } }, mounted () { // カンバス作成 this.canvas = document.querySelector('#myCanvas') this.context = this.canvas.getContext('2d') this.context.lineCap = 'round' this.context.lineCap = 'round' this.canvas.width = 700 this.canvas.height = 600 this.context.lineWidth = this.weightNum this.context.strokeStyle = this.color }, }); 次にhtml。canvasを作成し、マウスを動かした時の処理を書いていきます。 html <div id="app"> <main> <div> <canvas id="myCanvas" ref="myCanvas" style="border:1px solid #aaa" @mousedown="dragStart" @mouseup="dragEnd" @mouseout="dragEnd" @mousemove="draw"></canvas> </div> </main> </div> このままではエラーになってしまうのでマウスを動かした時の処理をvueにも追加 JavaScript methods: { // 描画 draw :function(e) { let x = e.layerX let y = e.layerY if(!this.isDrag) return; this.context.lineTo(x, y); this.context.stroke(); }, // 描画開始(mousedown) dragStart:function(e) { let x = e.layerX let y = e.layerY this.context.beginPath(); this.context.lineTo(x, y); this.context.stroke(); this.isDrag = true; this.context.getImageData(0, 0, this.canvas.width, this.canvas.height) }, // 描画終了(mouseup, mouseout) dragEnd: function() { this.context.closePath(); this.isDrag = false; } } とりあえずこれでかけるようになったと思います。 次回はペン機能と消しゴム機能をわけ。ペンの太さと色を変更できるようにしたいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel Forgeにvue.jsだけのSPAをデプロイする方法

やりたかったこと Laravel をよく使うので、Laravel Forge + Vultur を使っています(Forge + Vultr ではじめてLaravelアプリをデプロイするときにつまずいたこと)。 最近、Laravelを使わず vue.jsだけのアプリ(SPA)を作り、本番公開しようとしました。 vue.jsのアプリであればNetlify、GithubPageなどが簡単にできるようですが、せっかく有料でForgeを契約しているのだし等いろいろ事情があり、どうしてもLaravel Forgeにデプロイしたかったのです。 vue.jsアプリの概要 vue version 2.5.2 webpack version 3.6.0 Github にレポジトリを公開 やり方は簡単です。まずは下準備。 Laravel Forgeは、Githubからソースコードを取り込み、デプロイすることができます。 そのため、Githubレポジトリを作成し、vueアプリのソースコード一式をpush しておきます。 Laravel Forge でのディレクトリ設定 次に、いつも通りLaravel Forge で新規サイトを設定します。 この時のポイントは2つ。 1つめは、サイトの種類をStatic HTMLではなくGeneral PHP/Laravel で設定すること。Static HTML だと上手くできませんでした。 2つめは、初期ディレクトリを /dist に設定すること。これは、コンパイルされたファイルがdist フォルダに生成されるためです。 デプロイスクリプトの書き換え Laravel Forgeにて、デプロイスクリプトを次のように書き換えます。 cd /home/forge/www.domain.com git pull origin $FORGE_SITE_BRANCH npm install npm run build ( flock -w 10 9 || exit 1 echo 'Restarting FPM...'; sudo -S service $FORGE_PHP_FPM reload ) 9>/tmp/fpmlock npm を2行だけ、追加しています。 最初に調べていた時、Laravel Forgeでnpmするとタイムアウトする...などといった情報が出てきましたが、デプロイは普通に実行できました。 デプロイスクリプト以外でコマンドを叩くと、確かにエラーになっていました。 Nginx Configration File を編集する 最後に、Nginx の設定を変えます。画面下のFilesというメニューからEdit Nginx Configurationを選びます。 index.php となっているところを、index.html に変えます。最低限、この1箇所。 location / { try_files $uri $uri/ /index.html?$query_string; } この設定でデプロイすると、vue.jsだけのアプリも公開できます。 参考 こちらの解説が大いに参考になりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【就活】階層分析で最高の就職先を算出するアプリをつくりました【転職】

はじめに 複数の就職先候補で迷っている就活生・転職希望者の方、たくさんいらっしゃると思います。 そうした方々に向けて、選択肢の中から最高の就職先を算出するアプリをつくりました。 後悔なく意思決定するための少しでも足しになればと。 JobHunter's Choice(ジョブハンターズチョイス) サービス概要 AHP(階層分析法)という分析法を用いて複数の就職先の選択肢の中からベストなものを算出する、という手法を採用しました。 階層分析とは 複数ある選択肢のうちから最良のものを選択するための意思決定法です。 概要 例えばお部屋探しという目標に対して、評価基準(どの条件で選ぶか)と代替案(候補物件)があるとします。それらについて、 評価基準同士の重要性を比較する(ex:家賃と間取りのどちらをどの程度重視するか) 評価基準ごとに代替案同士を比較する(ex:家賃に関して物件Aと物件Bのどちらがどの程度すぐれているか) 以上をすべての組み合わせで行い、いろいろ計算すると、その人がどの評価基準をどの程度重視するかを加味した上で、それらの基準を最も満たす代替案を算出することができるわけです。 実績 工場の建設地の選定 大学教授の選抜 ソフトウェアシステムの品質の数量化 etc.. 企業活動、公共事業における幅広い分野で利用されているようです サービスの目的 で、この作業には前述の通りいろいろな計算が伴うので、表計算ソフトとかでやると相当面倒です。そもそも分析のノウハウどころか階層分析の存在すら知らない人のほうが多いですよね。 というわけで、 就職先選びにおいて、定量的なデータを提供し、かつその手間を最小限まで削減する これを目的として、サービス作成に至りました。 使い方 トップページのSTARTボタンを押すと分析開始。 STEP1 選択肢の記入 就職先の選択肢を入力します。 入力欄はデフォルトで3つですが、必要に応じて増やすことが可能。 STEP2 評価基準の選択 就職先選びの上で考慮する条件にチェックを付けます。 基準はデフォルトで出ているものの他に追加することが可能。 STEP3 評価基準の重要度評価 STEP2で選んだ基準がそれぞれペアになっており、全ての組み合わせが表示されています。 7段階評価のボタンが用意されているので、以下の基準でどちらを重視するか選びます。 1 = 左側の基準を重視する 4 = 両方同じくらい 7 = 右側の基準を重視する STEP4 選択肢の評価 評価基準ごとに、STEP1入力した選択肢がそれぞれペアになって表示されています。 その基準においてどちらの選択肢が優れている(と思う)かをSTEP4と同様に7段階評価ボタンで評価します。 結果 全て評価し終えてボタンを押すと、結果が表示されました! 階層分析によって企業ごとの評点が算出されており、総合評点が最も高い企業がベストチョイスになります。 各企業に各評価基準での評点が加算されていますが、重視する基準ほど多めに加算されているのがわかりますね。 その他の機能 分析結果はTwitterでシェア可能。 また、アカウント作成しログイン状態で使うと分析結果を保存したり過去の入力データを再利用してより作業を簡略化できます。 ↓ログイン時。過去の入力値がボタンで表示されます。 使用技術 Backend - Ruby on Rails 6.0.3 Frontend - Vue.js 2.6.12 UI - Vuetify 2.5.6, vue-chartjs 3.5.1, bootstrap 5.0.2 Infra' - AWS(EC2, RDS, Route53) WebServer - nginx 1.20.0 AppServer - unicorn 6.0.0 DB - PostgreSQL 13.x 段階評価ボタン Vuetifyのv-btn-toggleで作成。 評点は$emitで上位のコンポーネントに渡し、表示順通りに配列に格納する形で集計。 [Vue.js+Vuetify]段階評価ボタン ~評価項目の数と評価対象の数に応じて動的に表示する~ ボタンのレスポンシブ対応 [Vuetify]v-btn-toggle内のボタン幅をレスポンシブ対応 自動スクロール window.scrollByでボタン間のy座標の差分だけ自動スクロールさせます。 ボタン間の距離が固定値でなかったので、両者のy座標を毎回取得するようにしました。 // ボタンを押したら次のボタンへと自動スクロール autoScroll() { const cur = event.currentTarget.getBoundingClientRect().top // そのボタンの上端のy座標 const nxtItem = document.getElementById(`${次のボタンのID}`) // 自身の次のボタン(ターゲット) const nxt = nxtItem.getBoundingClientRect().top // ターゲットの上端のy座標 window.scrollBy(0, nxt-cur) //ターゲットと自身のy座標の差分だけスクロール } フォームの追加 フォームでバインドしているalternativesの初期値を長さ3のnullの配列にしているためデフォルトでフォームが3つイテレートされています。 この配列に新たにnullを加えるとフォームが追加され、過去の入力値を再利用するときはこの配列にその入力値を追加しています。 <template> ... <input v-for="(item, index) in alternatives" :key="index" v-model="alternatives[index]" > ... </template> <script> ... data() { return { alternatives: [null, null, null], ... </script> チェックボックスの追加 criteriaの要素がデフォルトで表示されている項目で、チェックされた要素はselectedCriteriaに格納されます。 項目を追加したり、過去のチェック項目を再利用するときは、criteriaとselectedCriteriaの両方にその項目が追加されることでチェックが入った状態で項目が追加されます。 <template> ... <v-checkbox v-for="(item, index) in criteria" :key="index" v-model="selectedCriteria" :value="item" :label="item" /> ... </template> <script> ... data() { return { criteria: [ '労働時間', '通勤時間', '雇用の安定', '仕事の裁量権', '社会への貢献度', ... ], selectedCriteria: [], ... おわりに できる限りユーザーの手数を減らすことを意識しました。このアプリでかなり時短になるはずです(当社比) まあ就職先の優先順位つけるだけっちゃつけるだけですが、わりと楽しくポチポチできるんじゃないでしょうか。 ジョブハンターの皆さんぜひ使ってみてください! 引用文献 [Vue.js+Vuetify]段階評価ボタン ~評価項目の数と評価対象の数に応じて動的に表示する~ [Vuetify]v-btn-toggle内のボタン幅をレスポンシブ対応 階層分析法
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nuxtjsにfabricjsを導入するときの手順

nuxtjsにfabricjsを導入するときの情報が少なかったのでそのメモです。 必要な項目のインストール yarn add fabric yarn add canvas package.jsonとyarn.lockに追加される。 plugin直下にfabric.jsファイルを作成。 fabric.js import { fabric } from 'fabric' import Vue from 'vue' Vue.use(fabric) export default fabric nuxt.config.jsに情報を追加 nuxt.config.js // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins plugins: [ { src: '@/plugins/fabric', mode: 'client' } ], 実際のコード これは赤い四角形を表示するためのサンプル fabric/index.vue <template> <canvas ref="can" width="200" height="200"></canvas> </template> <script> import { fabric } from 'fabric'; export default { mounted() { const ref = this.$refs.can; const canvas = new fabric.Canvas(ref); const rect = new fabric.Rect({ fill: 'red', width: 20, height: 20 }); canvas.add(rect); } }; </script> これで localhost:3000/fabricにアクセスすると以下のように赤い四角形が表示されているはず。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む