知識のリンク集

技術系アウトプット

IE対応はそんなに大変じゃなさそう2020

幸いにも私のエンジニア人生ではこれまでIE対応を考えながらコードを書いたことがなかった。
しかし、新しい就職先ではIE対応は当たり前のように行われているという情報を耳にした。

IE対応を考えるとなると、CSSJavaScriptになるわけだが、CSSではAutoprefixerJavaScriptではpolyfilというブラウザ間の互換を保つ機能が存在する。

全体重をかけてこういう技術に乗っかる気満々であるが、polyfilの導入方法に加え、IE対応で困った時に参照しそうな情報くらいはまとめておこうと思う。

IEのシェアを確認

gs.statcounter.com

JavaScriptのブラウザ対応状況を確認

Can I use... Support tables for HTML5, CSS3, etc

IE環境で確かめる

Micro SoftがVM上で検証できる環境を提供しているのでこれを使うことでMac端末上でIEの検証が可能になる。

Virtual Machines - Microsoft Edge Developer

IE(11)非対応のJSメソッドたち

ありがたいことに先駆者達が記事にまとめてくれている。

IE非対応のJavascriptをまとめてみた
注意!IE11で使えない最新JavaScritコード(ES6) – console dot log
IE11 で使用できる・できない JavaScript の機能 | knooto

参照先に情報はまとまっているので、ここにはメソッド名を列挙するだけにとどめて置く。

  • promise
  • fetch
  • arrow
  • ParentNode, ChildNodeのメソッド
  • intersection observer
  • class
  • find
  • for-of
  • include
  • スプレッド構文
  • テンプレートリテラル
  • 関数の引数にデフォルト値を設定する
  • 配列に対して複数の変数を一気に代入
  • オブジェクトから変数に一気に代入
  • Map(※配列のメソッドではない)
  • Set

Reactでpolyfil

create-react-appでプロジェクトを作成すれば初期設定で対応されているらしい。

github.comreact-app-polyfill - npm

Vueでpolyfil

Vue CLIでプロジェクトを作成すれば@vue/babel-preset-appが対応している。

しかし散見する記事ではbabel-polyfillを使っているものも見受けられる。
設定方法は同じようだが設定がうまくいかなくて困ることがあればここら辺を参照しようと思う。

Browser Compatibility | Vue CLI

babel-polyfilはreactでもつかえるがreact-app-polyfilを使うのを結果としては同じであろうしstack overflowではreact-app-polyfillが推奨されている。

まぁここら辺は結果同じ1つのモジュールを使うにたどり着いていくのであろうが、深掘るつもりはない。
プロジェクトの作成時はコマンドラインを使うことを硬く心に誓うのみである。

とにかくpolyfil

CDNで機能ごとにpolyfilを入れる方法もある。

<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/3.3.1/es6-promise.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fetch/2.0.4/fetch.min.js"></script>

個人的にはCDNを使うべきポイントがわかっていないので存在するということだけ頭の片隅においておく。

最後に

実際にやったことがないから言えるということは100も承知なのだが、ここ数年でIE対応にかかる負荷が格段に少なくなってきたように感じる。(だからこそフロントエンドエンジニアに振り切ることに踏ん切りがついた)

Reactは2013年、Vueは翌年の2014年、さらに翌年の2015年にES6が使われるようになり、2020年の現時点では彼らが公の場に誕生して5年以上経っているわけでいよいよ成熟してきている。
かつてのようにフロントエンドエンジニアは細かいTipsを覚えている職人である必要はなくなってきたのが大変悦しく、デザイン設計の勉強をしてよりユーザーのために考える時間を創出できる、そんな仕事ぶりのフロントエンドエンジニアに私はなりたい。

ブラウザでデータを保存する方法○選!

なんてタイトルにしたが、選び取る以前に列挙する知識がない。

どんな選択肢があるのだろうか、5つくらいサイトを見た情報をまとめてみることにした。

Web Storage(Local Storage / Session Storage)

手始めに「ブラウザ データ 保存」で検索するとローカルストレージが検索にひっかかる。

やたらとローカルストレージでひっかかるがローカルストレージはWeb Storageの仕組みのうちの1つにすぎないらしい。

MDNに知識がまとまっていた。

Web Storage API - Web API | MDN


Web Storageはキーバリューのオブジェクトで2種類の仕組みを持つ。

SessionStorage

・セッション中に使用可能。オリジンごとに保存領域は分割されている。

= 同一オリジンのブラウザを全て閉じるまでデータが保存される。

・ストレージ制限は5MB

LocalStorage

・ブラウザを閉じてもデータは持続し、有効期限はない。

・ブラウザキャッシュをクリアすると消える。

・ストレージ制限は3つ

面白かったのがこの記事で実際にローカルストレージにどんなデータファイルのがあるのか確認している。(※Windows)

Cookie

HTTP Cookie - HTTP | MDN

・サーバーとデータをやり取り(セッション管理/ パーソナラライゼーション / トラッキング)する用途で使われる。

・制限は 4096bytes/domain 50cookies/domain(最大でも3000cookies)。

※ 制限に関しては情報が古いかもしれない。しかしてそう変わる技術でもないのかもしれない。

IndexedDB

https://developer.mozilla.org/ja/docs/Web/API/IndexedDB_API

・キーバリューのオブジェクト

・WebStorageよりも多くのデータを保存したい場合に有用。

IndexedDBよりもっとシンプルな使い勝手なのでこちらも検討してねと紹介されていたリストもあったのだが、(localForage / dexie.js / ZangoDB など)ECサイトでもつくる場合を除いてそんなに必要になるケースはないと思うので、この情報は頭の片隅に配置する。

Web SQL

https://www.w3.org/TR/webdatabase/

なんだこの公式ドキュメントは。読みにくいことこの上ないぞ。

と思ったらそれもそのはず、Web SQLは非推奨のようだ。

SQLite

SQLite Home Page

なんだこの公式ドキュメントは。読みにくいことこの上ないぞ!(本記事2回目)

しかも上記リンクなんか違うぽい。。

とりあえずSQLちっくに操作ができる(あとIndexとかがあるのかな)ファイルベースのDBであるということしかわからなかった。

記事が全然ないので使われていないのではないか。



まとめ

Web StorageCookie、IndexedDBの3つと使い分けを覚えておけば十分だろう。

そんなに変わる技術でもないのでここの概要を掴んでおけば安心じゃろ!

Vuex.Storeのコンストラクタオプションの中身を覗く

VuexをいじっていたらStoreのコンストラクタオプションにgetterの存在を確認した。

「お!getterなんてあるんかい!」と、getterを使ったことがなかったために妙なテンションになり、たまには特に目的を持たず一箇所深掘りしてみるか〜と思い至った次第である。

Vuex.Store コンストラクタオプション

getterがあるなんて他はなにがあるんじゃろと思い、getterから参照すると型定義は下記のようであった。

export interface StoreOptions<S> {
  state?: S | (() => S);
  getters?: GetterTree<S, S>;
  actions?: ActionTree<S, S>;
  mutations?: MutationTree<S>;
  modules?: ModuleTree<S>;
  plugins?: Plugin<S>[];
  strict?: boolean;
  devtools?: boolean;
}

各プロパティの説明はこちらのリファレンスに載っていた。
API リファレンス | Vuex

下の4つがよくわからないので調べがてらまとめてみた。

modules

Vuexでは大規模アプリケーションを考慮し、ストア全体を管理するルートストアの配下にストアを分割配置するという手段を提供してくれているらしい。

そこで登場するのがこちらのmoduleプロパティである。

具体例はこちらにあるが、💡Vuexには大きくなりすぎたStoreを分割する手段がある。ということだけとりあえず覚えておこう。

plugins

これ自体は単一の引数としてストアを受けつけるただの関数。

ミューテーションのコミットをトリガーとし、ログの取得やストアと外部データソースの同期などのために利用される。

具体的な例はこちらを見ると納得感が得られる。

💡DBにデータを保存したいときはpluginで行うということを覚えておきたい。

strict

なんとストアにstrictモードがあるらしい。

Vuex ストアを厳格モードにします。厳格モードでは、ミューテーションハンドラ以外で、 Vuex の状態の変更を行うと、エラーが投げられます。

なるほど、デフォでstrictモードにしたい。

devtool

特定の Vuex インスタンスに対して開発ツールをオン、またはオフにできる。

上記で紹介したpluginの機能を使ってvueは開発者ツールにVueの情報を出しているようだが、それを操作できる代物らしい。

おそらくお世話になることはないだろう。


ここ最近気分でブログに書き出しているが、ブログにまとめようと思える単位で新しいことを学ぶと理解しやすい気がする。

TypeScriptのプリミティブ型を拡張して便利メソッドを生やす

入力値が空であるかどうかを確認して、空だったらPOST処理を走らせたくない。

という要件は往往にして存在するわけで、Stringが空であるかどうかを返してくれるメソッドがありそうなもんだと思ったがこれがないのである。

そこで偉大なる先輩がStringなどプリミティブな型を拡張していたのを思い出す。

ちょうど下記記事の手法を採用して文字列が空であるかどうかを返すisBlankメソッドを実装した。
kakkoyakakko2.hatenablog.com



ここから得るべき教訓

TypeScriptはインターフェースのマージができる。

String*1を参照するとStringのインターフェースに飛ぶ。

interface String {
    toString(): string;
    charAt(pos: number): string;
    charCodeAt(index: number): number;
    concat(...strings: string[]): string;
 ・・・
}

こういった既存のインターフェースに自分が追加したいメソッドを宣言を追加することでTypeScriptのプリミティブ型を拡張できる。

declare global {
  interface String {
    isBlank(): boolean;
  }
}

String.prototype.isBlank = function () {
  if (this && this.length > 0) return false;
  return true;
};



ESLintエラー

実装する際1つエラーにぶつかった。

エラーを出しているのはESLintさんだった。

String prototype is read only, properties should not be added

prototypeの参照先に飛ぶとこちらにもインターフェース定義があり、prototypeにはreadonly修飾子がついているので上記のエラーが生じたということである。

interface StringConstructor {
    new(value?: any): String;
    (value?: any): string;
    readonly prototype: String;
    fromCharCode(...codes: number[]): string;
}

eslintの設定ファイルに下記記述を追加すると解消する。

'no-extend-native': 0

「拡張して便利メソッド生やしておきました!」なんていかにもできそうな人に見えてよいではないか。

*1:stringじゃないよ。頭文字は大文字だよ

.browserlistrc

.browserlistrcよ。お主はなんぞ

Vueの練習をしようとCLIでプロジェクトを作成したところ設定ファイル群に見慣れぬものがいる。

.browserlistrc

> 1%
last 2 versions
not dead


これについて知るために下記2つの記事を参考にした。

.browserslistrcで対象ブラウザを指定する - Qiita

CSSベンダープレフィックス-webkit-を今この瞬間に辞める為のAutoprefixerの導入 - Qiita



要するに

webサービスを提供する際に、ブラウザによって、そしてCSSプロパティによってベンダープレフィックスをつける必要がある。

私自身は今までブラウザ対応をしっかり行うWebサービスに従事していなかったため、あまり使ったことがない。
最近はそんなに必要ないんじゃないだろうか。と安易に考えつついざ要求されたらめんどくさそうな仕事であることは容易に推察できる。

ベンダープレフィックス *1

CSSの新しいプロパティに付けて、記述したプロパティが拡張機能であることを、各ブラウザに示すために使われる識別子

これを解消するために Autoprefixer なるものが存在するらしい。

ちなみにそれまでの先人たちはCanI Useというサイトで対応ブラウザを確認していたらしい。しかしflexbox transform を入力しても何も出てこなかった。

MDN web docsのCSSの項目にも対応ブラウザの記載があるのでなんだかんだこちらで検索した方が良さそうである。

transform - CSS: カスケーディングスタイルシート | MDN

話を戻すとAutoprefixerは最新のブラウザ実装状況をまとめたサイト「Can I use」の情報を使用し、必要なベンダープレフィックスのみを付与する為のツールであるということだ。

必要なベンダープレフィックスを指定をするために登場するのが.browserlistrcである。

AutoprefixerはGulp、webpack、などいくつか使う方法があり、指定ファイル、実行方法もコマンドラインで適応させるなどいくつかパターンあるようだが.browserslistrcは汎用的に対応できるようなのでこちらを使うようにするのが良さそうだ。

責任には明確な方が良いし探すときもすぐに見つけられるので私は設定ファイルはpackage.jsonにまとめないで個別にだしたい派だ。

特に気になったのはVS Code拡張機能「Autoprefixer」もしくは「Live Sass Compiler」を使うという方法だ。

ベンダープレフィックスってなに?自動で付与する方法とは? | Web Design Trends



最後に

VueのプロジェクトをCLIで作成すると最初から下記のような記述がすでにある。

各記述の意味は下記のとおり。

> 1%                  // シェアが1%以上のブラウザで必要なベンダープレフィックスのみ
last 2 versions       // 最新2バージョンで必要なベンダープレフィックスのみ
not dead              // 24ヶ月以内にアップデートされてないバージョンのブラウザのみ


ベンダープレフィックスを気にせずWebアプリケーション開発をできることは良きことなり。

*1:先頭に付ける-moz-や-webkit-など

GolangでGraphQLのクエリに添付したファイルをS3にアップロードする

GraphQLでファイルデータをアップロード

GraphQLサーバーには gqlgen を利用。
gqlgenが提供する型の1つである `Upload` を使う。

scalar Upload
https://gqlgen.com/reference/scalars/

@graphql.schema

type Mutation {
  uploadFile(file: Upload): String
}

goで処理を受け取る際の引数(resolver)の型には*graphql.Uploadを指定

func (r *mutationResolver) UploadFile(ctx context.Context, file *graphql.Upload) (*string, error) {
  // ...
}

Uploadの中身は下記のとおり

type Upload struct {
	File        io.Reader
	Filename    string
	Size        int64
	ContentType string
}
S3にアップロード

S3にアップロードするために aws-sdk-go が提供するs3managerを利用。
https://aws.amazon.com/jp/sdk-for-go/

バケット名とオブジェクトキー、アップロードするファイルをio.Reader型で渡す。
Upload.Fileをそのまま渡せる。

bucketName := "xxx-bucket"
objectKey := "xxx-key"

uploader := s3manager.NewUploader(sess)
_, err = uploader.Upload(&s3manager.UploadInput{
    Bucket: aws.String(bucketName),
    Key:    aws.String(objectKey),
    Body:   file,
})
テスト

いつもはgqlgenが提供してくれるclientを使ってクエリを叩いてインテグレーションテストを行っていたのだがfileを正しく渡す方法がわからなくて断念した。

c := client.New(handler.GraphQL(graphql.NewExecutableSchema(graphql.Config{})))

targetFilePath := "./sample.txt"
file, err := os.Open(targetFilePath)
query := fmt.Sprintf(`mutation uploadFile {
    uploadFile(file: {file:%+v, fileName:"%s", size: %d, contenttype:"%s"}) 
}`, file, "fileName", 5, "contentType")

c.Post(query, &resp)

解決策としてはファイルのアップロードに関してはaltairを使って手動テストすることにした。
https://altair.sirmuel.design/altair.sirmuel.design


左下のファイルを選択でFinderからファイルを選択できる。
枠線のAdd Filesをクリックすると複数ファイルの選択も可能。

f:id:yuri_iOS:20200624000041p:plain

ShellScript備忘録

確かめ方

shファイルを作って試すのが早い

  • ファイル冒頭で #!/bin/sh を宣言
  • 実行は ./ファイル名 をターミナルで実行

確認用テンプレ

ファイル名: test.sh
実行: ./test.sh

#!/bin/sh
export ENV=pro

echo $ENV

if [ $ENV == pro ]; then
  echo "ok!"
else
  echo "ng...."
fi