【Sass】@extendと@mixinの使い分け
定義したスタイルを複数のクラスで使用できるSassの@extendと@mixinの違いが分からなかったので、両者を比較して使い分けるべきケースをまとめる。
@extendの特徴
定義したスタイルを他のクラスに継承して使用できる
/* Sass */ .btn-base{ border:1px solid; border-radius: 0.5rem; padding: 0.5rem; } .btn-success{ @extend .btn-base; background-color: green; } .btn-error{ @extend .btn-base; background-color: red; }
/* CSS */ .btn-base, .btn-error, .btn-success { border: 1px solid; border-radius: 0.5rem; padding: 0.5rem; } .btn-success { background-color: green; } .btn-error { background-color: red; }
プレースホルダーを使用する場合
継承元になったクラス(.btn-base)を利用しない場合は、プレースホルダー(%)を使用することでCSSにコンパイルされない
/* Sass */ %btn-base{ border:1px solid; border-radius: 0.5rem; padding: 0.5rem; } .btn-success{ @extend %btn-base; background-color: green; } .btn-error{ @extend %btn-base; background-color: red; }
/* CSS */ .btn-error, .btn-success { border: 1px solid; border-radius: 0.5rem; padding: 0.5rem; } .btn-success { background-color: green; } .btn-error { background-color: red; }
@mixinの特徴
定義したスタイルを他のクラスで呼び出して使用できる
/* Sass */ @mixin btn-base{ border:1px solid; border-radius: 0.5rem; padding: 0.5rem; } .btn-success{ @include btn-base; background-color: green; } .btn-error{ @include btn-base; background-color: red; }
/* CSS */ .btn-success { border: 1px solid; border-radius: 0.5rem; padding: 0.5rem; background-color: green; } .btn-error { border: 1px solid; border-radius: 0.5rem; padding: 0.5rem; background-color: red; }
引数を使用する場合
@includeに引数を渡すことで、呼び出し元のクラスごとにスタイルを調整することが出来る
/* Sass */ @mixin btn-base($bgc:transparent){ border:1px solid; border-radius: 0.5rem; padding: 0.5rem; background-color: $bgc; } .btn-success{ @include btn-base(green); } .btn-error{ @include btn-base(red); }
/* CSS */ .btn-success { border: 1px solid; border-radius: 0.5rem; padding: 0.5rem; background-color: green; } .btn-error { border: 1px solid; border-radius: 0.5rem; padding: 0.5rem; background-color: red; }
比較
@extend
メリット
- コード量が少ない
継承元と継承先のクラスが1セレクタにグルーピングされるため、共通スタイルのコードの重複を防止できる
- コード量が少ない
デメリット
引数が使用できない
@mixinと違い引数が使えない分、継承されるスタイルは固定になる可読性が下がる
継承元と継承先のコードが離れるため、コードの可読性が落ちる。
そのため、グルーピングできない関連性がないクラスへの@extendは控える。(関連性があるコードは距離が近く、関連性がないコードは距離が遠いため)
@mixin
メリット
引数が使える
@includeしたクラスごとにスタイルの調整が出来る可読性が高い
コードが@includeしたクラスに全て記載されるため、可読性が高い
デメリット
- コード量が増える
@includeしたクラスに同じスタイルが記載されるため、コードが重複する。
- コード量が増える
まとめ
グルーピング出来るような関係性のあるスタイルは@extendを使用する
関係性がない、または引数を利用したいなら@mixinを使用する
まとめのまとめ
継承関係があるなら@extend、それ以外は@mixin
参考
【JavaScript】async/await
async/awaitとは
Promiseより快適に非同期処理を利用することが出来る構文。
async
- 非同期関数を定義する
関数名の前にasync
を書くと、その関数はPromiseオブジェクトを返す。
async function test() { return 'test'; } test().then(value => { console.log(value); // => test });
await
- JavaScriptを待機させる
async
関数の中に記載すると、await
を指定した関数のPromiseオブジェクトが結果を返すまでJavaScriptを待機させる。
function test(value) { return new Promise(resolve => { setTimeout(() => { resolve(value * 2); }, 1000); }) } async function sample() { const result = await test(5); return result + 5; } sample().then(result => { console.log(result); // => 15 });
注意
async
関数でない場合にawait
は使えないため、必ずセットで利用する
Promiseとの比較
先ほどのawait
の例で書いた処理をPromiseで書いた場合、async/awaitの方がより簡潔に非同期処理を書くことが出来る。
function test(value) { return new Promise(resolve => { setTimeout(() => { resolve(value * 2); }, 1000); }) } function sample() { return test(5).then(result => { return result + 5; }); } sample().then(result => { console.log(result); // => 15 });
参考
【JavaScript】Promiseオブジェクト
Promiseとは
JavaScriptの非同期処理のネストを深くせずに実装することができる仕組み。
背景
- コールバック地獄
JavaScriptの非同期処理はコールバック関数を利用して実装するが、処理が連続するとネストが深くなり、コードが肥大化してしまう問題があった。
// 引数を倍にする関数 function async(data, callback) { setTimeout(function() { callback(data * 2); }, 1000); } function callback_hell() { async(100, function(data) { console.log(data);// => 200 async(data, function(data) { console.log(data);// => 400 async(data, function(data) { console.log(data);// => 800 }); }); }); }
そのため、連続する非同期処理をネストを深くせず同期処理のように記載するためにPromiseが利用される。
実装例
//Promiseオブジェクト const async = value => { return new Promise((resolve,reject) => { setTimeout(()=>{ if(value) { console.log(value) resolve('success') }else{ console.log('値なし') reject('failure') } },1000) }) } //thenメソッド async('test1').then( response => { // => test1 return async('test2') // => test2 } ).then( response => { }, error => { console.log('エラー') } )
Promiseオブジェクト
非同期処理の最終的な完了もしくは失敗を表すオブジェクト
Promiseオブジェクトのコンストラクターには3つの引数が必要。function(resolutionFunc, rejectionFunc){
// typically, some asynchronous operation.
}- resolutionFunc
処理の成功を通知するための関数 - rejectionFunc
処理の失敗を通知するための関数 asynchronous operation
処理本体上記コードでは処理としてseTimeOutを実行し、引数があればresolve,なければrejectを呼び出している。
- resolutionFunc
thenメソッド
Promiseが成功した場合と失敗した場合のコールバック関数を受け取るメソッド。p.then(onFulfilled[, onRejected]);
p.then(value => {
// fulfillment
}, reason => {
// rejection
});- onFulfilled
Promiseが成功した時に呼び出される関数(resolveによって呼び出し) - onRejected
Promiseが失敗した時に呼び出される関数(rejectによって呼び出し)
- onFulfilled
メリット
連続する非同期処理をネストせずに書ける
Promiseを利用することで連続する非同期処理をthenメソッドでつなぐことができるため、コールバック地獄を回避でき、メソッドが肥大化することを防止できる。エラー処理を必要な箇所にまとめることができる
Promiseを利用することで、どの処理でエラーになってもthenメソッドで用意した失敗コールバック関数(reject)が呼び出されるため、メソッドごとにエラー処理を記載する必要がない。
参考
【Rails,Vue.js】JestでCSRFトークンを設定したaxiosを含むコンポーネントをテストするとき
事象
JestでCSRFトークンを設定したaxiosインスタンスを含むコンポーネントをマウントする際にTypeError: Cannot read property 'getAttribute' of nullになる
$ npm test
● Test suite failed to run TypeError: Cannot read property 'getAttribute' of null 6 | common: { 7 | 'X-Requested-With': 'XMLHttpRequest', > 8 | 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') | ^ 9 | } 10 | }, 11 | })
原因
マウントしたコンポーネントにCSRFトークンが設定されているmetaタグが含まれていないため
Jestでマウントしたコンポーネントの中身をみると
wrapper = shallowMount(Message) console.log(wrapper.html())
<div class="container"> <div class="msg standard my-3"><span>メッセージ</span></div> </div>
shallowMountしたコンポートにはmetaタグが含まれていない。
そのため、axiosが取得しようとしているmetaタグがないため、エラーになる。
対応
今回の原因がmetaタグからCSRFトークンを設定しているため、metaタグではなくCSRFトークンを発行してくれるモジュールであるrails-ujsを使う。
手順
インストール
npm install @rails/ujs
axiosインスタンスを生成しているファイルを編集する
import axios from 'axios' import { csrfToken } from '@rails/ujs'; //追加 const http = axios.create({ headers: { common: { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-TOKEN': csrfToken() //追加 } }, }) export default http
参考
【JavaScript】コールバック関数
コールバック関数
引数として渡される関数。
JavaScriptの関数はデータ型の1種のため、数値型や文字列型と同様に関数の引数・戻り値として扱うことができる。
「関数を引数、戻り値として扱う関数」を関数を高級関数と呼ぶ。
function execCallBack(callback){ console.log('execCallBack') callback() } var callBack = function(){ console.log('callBack') } execCallBack(callBack) // 出力結果 // callBack
なぜコールバック関数が必要か
- 非同期処理の順番を制御するため
JavaScriptは前の処理結果を待たずに次の処理を実行する、非同期処理が可能。
(例) サーバーからデータを取得している間にフロント側のhtmlを更新する
ただし、前の処理結果を元にして次の処理に移りたい場合、コールバック関数によって処理の実行順序を制御することができる。
// 表示する値をセットする前に次の処理が実行されてしまう function setMessage() { var msg; setTimeout(function() { msg = 'Hello World'; }) console.log(msg); } setMessage(); // 出力結果 // undifined
// 表示する値がセットされた後に次の処理を実行する function setMessage(callback) { var msg; setTimeout(function() { msg = 'Hello World'; callback(msg) }); } var showMessage = function (msg){ console.log(msg) } setMessage(showMessage); // 出力結果 // Hello World
参考
【JavaScript】Ajax
Ajaxとは
Asynchronous JavaScript + XMLの略称。 ※Asynchronous=非同期
JavaScriptがサーバーと非同期で通信し、取得したデータを使用してHTMLを更新する技術。
代表的なのがGoogleマップ。表示位置を変えた際に、移動した分だけの地図情報を取得して更新される。
メリット
データ量を抑えられる
必要なデータだけをサーバーから取得するため、HTMLそのものをやりとりするよりデータ量が抑えられる。レスポンスを待つ間も作業できる
サーバーからデータを取得する間に、ブラウザ側でHTMLを更新したり、ユーザーの入力を受け付けることができる。
背景
元々はブラウザとサーバーが交互に処理を行う同期通信を行なっていた。
同期通信だとサーバーからのレスポンスが返ってくるまで、ブラウザは何も処理ができない。
またHTMLをやりとするため、サーバーとの通信量が多くなりがちだった。
Ajaxで使用されている技術
XMLHttpRequest
JavaScriptでHTTPリクエストを行うためのAPI。
JavaScriptがサーバーとやりとりするための技術で、ページ全体を更新することなく、データを受け取れる。XML
Extensible Markup Language。文書やデータの構造を記述するマークアップ言語。
データをやりとするときの形式、近年はもっと読みやすく軽量なJSON形式でやりとりすることが多い。DOM
DocumentObjectModel。HTMLなどの文書の構造をプログラムで制御するためのAPI。
サーバーから受け取ったデータをDOMを利用して、変更・更新する。
実際の流れ
- ブラウザからデータ取得のためのイベントが発生
- JavaScript(XMLHttpRequest)によりサーバーへリクエスト
- サーバーがリクエストに応じてデータをXML,JSON形式でレスポンスを返す
- データを受け取ったらJavaScrptがDOMを利用してページを更新
参考
【Rails】rails newした時にGem::GemNotFoundExceptionが発生する
事象
railsのプロジェクトを作成する時にGem::GemNotFoundException
が発生する
$ rails _5.2.2_ new rails_vue Traceback (most recent call last): 2: from /Users/(username)/.rbenv/versions/2.5.0/bin/rails:23:in `<main>' 1: from /Users/(username)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/rubygems.rb:308:in `activate_bin_path' /Users/(username)/.rbenv/versions/2.5.0/lib/ruby/2.5.0/rubygems.rb:289:in `find_spec_for_exe': can't find gem railties (= 5.2.2) with executable rails (Gem::GemNotFoundException)
原因
指定したバーションのrailsがインストールされていなかったため
$ gem list rails rails (6.0.1, 6.0.0, 5.2.4.1, 5.2.1) # 指定した5.2.2がない
対応
該当のバージョンをインストールする
$ gem i -v 5.2.2 rails # インストール後にインストールされているか確認 $ gem list rails rails (6.0.1, 6.0.0, 5.2.4.1, 5.2.2, 5.2.1) # 5.2.2が追加されている $ rails _5.2.2_ new new_app # 指定したバージョンでプロジェクトの作成が出来る