Wiz テックブログ

Wizは、最新のIoTやICTサービスをお客様に届ける「ITの総合商社」です。

PHPのInterface -メリットと使い所-

今回はPHPのInterfaceの使い所について、例を交えつつ紹介してみようと思います。

本題に入る前にこの記事内のサンプルコードについてです。
サンプルコードは全て、フレームワークにLaravelを使用しているという前提で記述しています。
LaravelでInterfaceを使う場合、サービスプロバイダーのregisterメソッド内でbindメソッドを使用し、実装クラスとInterfaceの紐付けを行ってください。

$this->app->bind(XxxInterface::class, ConcreteClass::class);

これがないと"XxxInterface is not instantiable..."みたいなエラーが出てしまいますので、ご注意ください。

Interfaceの概要

PHPの公式ドキュメントを確認すると、以下のように説明されています。

オブジェクトインターフェイスにより、あるクラスが実装する必要があるメソッドの 種類を、これらのメソッドの実装を定義することなく、指定するコードを作成できる ようになります。

インターフェイスは通常のクラスと同様に定義することができますが、 キーワード class のかわりに interface を用います。またメソッドの実装は全く定義されません。

インターフェイス内で宣言される全てのメソッドはpublicである必要があります。 これは、インターフェイスの特性によります。 https://www.php.net/manual/ja/language.oop5.interfaces.php

Interfaceを使うメリット

"php interface メリット"などのキーワードでググるといろいろ出てきますが、大きなメリットの一つとして

ということがあげられます。
Interfaceを使うと、複数のクラスがお互いに直接依存し合わず、Interfaceに依存している状態を作ることができます。

どういう状態なのか、以下の例1をご覧ください。

例1

  • メルマガを配信するような機能があったとする
  • 外部のメールサービスを使っている
  • メールサービスのAPI呼び出し用にライブラリが提供されており、それを使って機能を実装している

という状態があるとします。
サンプルコードで書いてみると、こういう感じです。

ある時、使用しているメールサービスを変更するような仕様変更があったとします。
その場合、上記のようにInterfaceを使った実装をしていれば、変更の影響範囲をEmailLibServiceのみに抑えることができます。

コントローラーのemailMagazineメソッドでは、EmailLibInterfaceのsendというメソッドを呼び出しています。
EmailLibServiceのsendメソッドを直接使用していません。
なので使用するメールサービスが変わった場合にEmailLibServiceの中身を書き換えようが、新しくクラスを作って差し替えようが、EmailLibInterfaceさえimplementsされていれば呼び出し元に影響が及ぶことはありません。

例えばコントローラーからライブラリのクラスを直接newして使用していたり、あるいはEmailLibServiceのメソッドを直接呼び出していたとすると、コントローラーにも影響が及んでいたかもしれません。

Abstract classでもいいのでは?

Abstract classについてはこちらをご覧ください。

https://www.php.net/manual/ja/language.oop5.abstract.php

InterfaceとAbstract classは

  • 抽象メソッドを定義できる
  • 定義されたメソッドを継承先のクラス(Interfaceは実装クラス)で必ず実装しなければならない
  • そのままでは使えない(直接newしてインスタンスを作成したりできない)

などの点で似ています。しかし

  • Interfaceのメソッドは全てpublicでなければならない
  • Abstract classはInterfaceと違って、メソッドに具体的な実装内容を持たせることができる

などの違いがあります。
なのでAbstract classを使えば共通処理を具体的な実装部分まで定義してしまって、動きが変わる部分のみ抽象メソッドで定義するようなことが可能です。
Abstract classの方ができることが多く自由度が高いので、Interfaceを使う必要がなさそうに思えます。
しかし、具体的な処理を持った共通メソッドに変更を加える必要がある場合、継承先クラス全てに影響が及んでしまうというデメリットがあります。
そう考えるとInterfaceの場合は元々実装内容を持たないので、変更に強いと考えられます。
振る舞いが異なるという前提で同じメソッドを不特定多数のクラスに持たせたい場合は、Interfaceを使用するメリットがあるのではないでしょうか。
ということを踏まえた上で、2つ目の例です。

例2

例えばユーザーに何かしらの通知を送るようなことがあったとして

  • パラメーターによってどういう通知を送信するかを変えたい
  • 通知のフォーマットや通知に必要な要素はバラバラ

みたいな条件を満たして実装したい場合、以下のような使い方ができます。

(参考: https://youtu.be/cUV1nXPfjFY)

Interfaceは複数implementsできる

もう一つAbstract classとInterfaceの特徴的な違いは、Interfaceは複数implementsできるという点です。

https://www.php.net/manual/ja/language.oop5.interfaces.php#language.oop5.interfaces.implements

PHPは多重継承ができない仕様です。Abstract classを使用する場合、あくまでもクラスの継承なので1つのAbstract classしか継承できません。対してInterfaceに個数の縛りはありません。カンマ区切りで複数implementsが可能です。

例えば例2で "ある通知クラスの場合は管理者にも同時に通知を送りたい" みたいな場合、以下のような形で複数implementsすると便利なのではないでしょうか。

↑のように空のInterfaceを作成して、自作の型のように手軽に扱えるのもInterfaceのいいところだと思います。

さいごに

Wizではエンジニアを募集しております。 興味のある方、ぜひご覧下さい。

careers.012grp.co.jp

Next.js10新機能 next/imageを使ってみた

こんにちは、フロントエンドエンジニアの松尾です。

2020年10月27日にNext.js10がリリースされ、パフォーマンスを改善するための20個を超える新しい機能が追加されました。

nextjs.org

私たちフロントエンドチームでも、プロジェクトにNext.js10を導入しており様々な恩恵を得ています。

本記事では、その中でも最も便利だと感じたnext/imageについてご紹介したいと思います。

next/imageとは

next/imageとは、画像の表示を自動で最適化してくれるコンポーネントです。

主に以下の最適化を行います。

  1. WebPフォーマット*1変換(対応ブラウザのみ)
  2. 画像遅延読み込み(Lazy Load)
  3. レスポンシブ対応

導入方法

Next.jsにデフォルトで備わっているため、next/imageをimportするだけで使用することができます。

基本的に<img>要素の書き方と変わりませんが、width・heightの記述は必須ですので注意が必要です。

import Image from "next/image"
~~~
<Image src="/free.jpg" width={600} height={400} />

画像比較

では実際に<img>next/imageの比較をし、どのように画像の最適化が行われているか確認してみましょう。

比較対象として、以下のフリー画像を使用します。(free.jpg 843kb)

林道

▼ コード

import Image from "next/image"

const ImageArea = () => {
  return (
    <>
      // 通常のimg
      <img src="/free.jpg" width={600} height={400} alt="通常のimg 林道"/>
      // next/imageのコンポーネント
      <Image src="/free.jpg" width={600} height={400} alt="next/imageのコンポーネント 林道"/>
    </>
  )
}

export default ImageArea

表示を見てみると、同じ見た目の画像が並んでいることが確認できます。

林道画像比較

こちらは最終的に吐き出されたnext/imageのHTMLです。

next/imageのHTML

src/srcset属性を見てみると、/_next/imageを参照していることが確認できます。

/_next/imageビルド時に生成される画像サーバーでして、こちらにパラメータを送ることで表示させる画像の切り替えを行っています。

以下は開発者ツールの画像読み込み箇所です。

開発者ツールの画像読み込み箇所

next/imageではWebPフォーマットでの表示となっており、ファイルサイズが70%程削減されています。

様々な画像で試してみたのですが、元々のファイルサイズが大きい画像ほど圧縮率が高い傾向があるようです。

次にレスポンシブ対応を確認してみましょう。

以下のように、next/imageの画像のみレスポンシブが適用されていることが確認できます。


width・heightの直接的な指定は、レイアウトまわりで色々と問題を引き起こす原因となるため避けがちです。

しかし、next/imageではアスペクト比に基づいて自動的にレスポンシブ対応となるため問題なく指定することができます。

最後に遅延読み込みによる最適化です。

左の開発者ツールを見てみると、画像から200px程近づいた時に読み込みを行っていることが確認できます。


このように、next/imageでは「サイズ最適化」「遅延読み込み」「レスポンシブ対応」の最適化を全て自動で行ってくれます。

外部ファイルの読み込み

外部サーバーから画像を読み込む場合、そのままURLを指定するとエラーが出てしまいます。

next.config.jsに以下の設定を追記することで、Next.js側がURLを認識し画像の読み込みをしてくれます。

module.exports = {
  images: {
    domains: ["example.com"], //ドメインを指定
  },
}

オプション引数

next/imageでは豊富なオプション引数が存在しています。

以下はオプション引数の設定例です。

<Image
  src={"/free.jpg"}           // 画像へのパスまたはURL ※必須
  width={600}                 // 画像の横幅 ※必須
  height={400}                // 画像の高さ ※必須
  layout={"fill"}             // レイアウト, "fill" | "fixed" | "intrinsic" | "responsive", default:"intrinsic"
  sizes={"80vw"}              // メディアクエリのマッピングサイズ, default:"100vh"
  quality={50}                // 画質, default:100
  priority={true}             // 表示優先度, default:false
  loading={"lazy"}            // 遅延読み込み, "lazy" | "eager", default:"lazy"
  unoptimized={false}         // 画像最適化, default:true
  objectFit={"cover"}         // object-fit ※layout='fill'の場合
  objectPosition={"50% 50%;"} // object-position ※layout='fill'の場合
/>

対象の画像に合わせて柔軟に設定できることもnext/imageの魅力の一つです。

それぞれの引数の詳細は下記の公式ドキュメントに記載されてますので、興味のある方は見てみてください。

nextjs.org

まとめ

以上next/imageの導入方法・最適化についての説明でした。

画像はWebページのトータルバイト数のうち50%を占めていると言われてますので、next/imageを積極的に活用しパフォーマンスを改善していきましょう!

最後になりますが、Wizではエンジニアを募集中です!

興味のある方は是非覗いてみてください↓

careers.012grp.co.jp

*1:Google開発の画像フォーマット。ファイルサイズを大幅に軽量化できる。

Lambda@EdgeでCloudFrontの配信をコントロールしたい

Lambda@Edgeとは

CloudFrontのエッジサーバでコードを実行するLambda関数です。

配信をカスタマイズ出来ます。

感覚としてはCloudFrontのフックで動くLambda、という感じですが考慮する点・制約がいくつかあります。

  • 番号付きバージョンのみをトリガーに設定出来る(エイリアスに設定出来ない)
  • バージニア北部(us-east-1)リージョンのみ

詳しくはドキュメント

設定出来るフック

f:id:wiz-yoshitomi:20210126161055j:plain

Lambda@Edgeがフックに設定出来る箇所は四つあります。

  • ビューワーリクエス
  • ビューワーレスポンス
  • オリジンリクエス
  • オリジンレスポンス

キャッシュがある時はオリジンリクエスト・オリジンレスポンスは発生しません。

例:webp画像を配信して表示速度高速化したい

  1. リクエストした画像(jpeg,png)のwebpファイルが存在したらwebpファイルを取得する
  2. 存在しない場合はwebpファイルを作成してから取得する

このようなLambda@Edgeを作成し、オリジンリクエストに設定してみます。

前提:S3+CloudFrontの構成は既に作成されているものとする。

Lambdaを用いたS3画像の取得・加工はドキュメントを参考にしています。

①IAMロールを作成

アクセス許可

AWSLambdaExecuteをアタッチ

信頼関係

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "edgelambda.amazonaws.com",
          "lambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

② Lambda関数を作成

最初に述べた制約に則り関数を作成します。 今回の設定は以下です。

リージョン:us-east-1 ランタイム言語:Node.js 12.x 実行ロール:前項目で作成したロールを設定

③関数コードを作成

index.jsファイルを作成

const AWS = require('aws-sdk');
const sharp = require('sharp');
const https = require('https');
const url = require('url');
const s3 = new AWS.S3();

const BASE_URL = 'CloudFrontURL';
const BUCKET_NAME = 'S3バケット名';

exports.handler = async (event, context) => {
    const request = event.Records[0].cf.request;
    
    // 画像のみ
    if (request.uri.match(/\.(jpe?g|png)$/) && request.uri.split[1] !== 'tmp') {

        const webpUri = request.uri + '.webp';
        let cloudfrontReq = url.parse(BASE_URL + webpUri);
        let webpExits = false;

        // webpファイルへ正常にアクセス出来るか
        await new Promise((resolve, reject) => {
            cloudfrontReq.method = 'HEAD';
            cloudfrontReq.timeout = 3000;
            https.request(cloudfrontReq, cloudfrontres => {
                if (cloudfrontres.statusCode >= 200 && cloudfrontres.statusCode < 300)
                    webpExits = true;

                resolve();
            }).on('error', reject).end();
        });

        // webpファイルが無い場合は作成する
        if (!webpExits) {

            const key = request.uri.substr(1)
            const webpKey = webpUri.substr(1)

            // オリジンの画像を取得
            try {
                const params = {
                    Bucket: BUCKET_NAME,
                    Key: key
                };
                let origimage = await s3.getObject(params).promise();

            } catch (error) {
                console.log(error);
                return;
            }

            // webpへ変換
            try {
                let buffer = await sharp(origimage.Body).webp().toBuffer();

            } catch (error) {
                console.log(error);
                return;
            }
            
            // S3へput
            try {
                const putParams = {
                    Bucket: BUCKET_NAME,
                    Key: webpKey,
                    Body: buffer,
                    ContentType: "image"
                };

                const putResult = await s3.putObject(putParams).promise();
                webpExits = true;
            } catch (error) {
                console.log(error);
                return;
            }
        }
        
        // webpが存在する or 変換が成功した場合webpのuriでrequestを上書きする
        if (webpExits)
            request.uri = webpUri;
    }

    return request;
};

④デプロイパッケージを作成

convert-webpフォルダ(名前は任意)を作成し、そこにindex.jsを入れ、そこにnpmでsharpライブラリをインストールします。

$ npm install --arch=x64 --platform=linux --target=12.13.0 sharp

今回の場合のファイル構造は、以下のようになります。

convert-webp
├ index.js
└ node_modules
    ├ sharp
    ├ ...

⑤.zipファイルをアップロード

作成したファイルをzipへ圧縮し、「関数コード」→「アクション」→「.zipファイルをアップロード」の手順でアップロードを行います。

⑥テストを行ってみる

こちらのドキュメントより、オリジンリクエストのイベント構造を確認出来ます。

⑦ Lambda@EdgeをデプロイしCloudFrontへ設定する

「デザイナー」→「トリガーの追加」をクリックします。

「トリガーを選択」→「CloudFront」で出現する「Lambda@Edgeへのデプロイ」項目を選択します。

f:id:wiz-yoshitomi:20210126181909p:plain

以上を入力し「デプロイ」をクリックで設定完了です。

⑧確認

DevToolを開き「Networkタブ」→対象の画像を選択

Responce headerのcontent-typeがimage/webpになっていればOKです!

注意点なのですが今回はオリジンリクエストにフックしているため、キャッシュが残っている状態だとオリジンリクエストが発生せず関数が発火しません。

確認の際はご注意ください。

まとめ

今回は省略していますが、CloudFrontのheaderのホワイトリストAcceptを追加し、webpを許可しているブラウザかの確認の処理があった方が良いかと思います。

Lambda@Edgeを使用せずにS3へのオブジェクト追加をフックにしたLambdaでwebpファイルを生成するという方法も考えられますが、同一バケットの「オブジェクト生成」フックで新規オブジェクト追加する構成は再起的に実行されてしまう危険性があるため推奨されていません。

やはりキャッシュがない時(オリジンリクエストが発生する時)の起動は体感でも遅く感じるので、ユースケースに合わせてキャッシュの有効期限を長くしたりなど対処した方が良いかなと思いました。

最近話題のLambdaのコンテナイメージフォーマットもどうやら使えるようなので、こちらも試してローカルまで一貫したサクサク開発にしてみたいです。

〜最後に〜

Wizではエンジニアを募集中です!

興味のある方は是非ご覧ください!

careers.012grp.co.jp

Snowpack 試してみました

皆さんこんにちは、フロントエンドエンジニアの高野です。

皆さんはJSのバンドラーは何を使われていますか?

私たちフロントエンドチームでは主にwebpack、一部browserifyを使用しています。

最近はReactやVueをブイブイ使いビジネスロジックをもつことも多くなっており、そのためJSのバンドルサイズは嵩む一方です。

そうなってくると気になるのはビルド時間です。

私の担当しているプロジェクトでもwebpackのビルドが34秒かかっており、今後さらにプロジェクトが大きくなれば1分越えも夢ではないです...

そこで、Snowpack v3をにチャレンジしてみました。

Snowpackとは

Snowpackは初回(v1.0.1)リリースが1年前と、比較的新しいフロントエンドのビルドツールです。

SnowpackはPikaが中心で開発しており、この団体は今よりもwebを90%早くする!!という大きな目標を掲げていて、その一環としてのプロジェクトがSnowpackのようです。

また、PikaはSkypackも開発しています。

www.pika.dev  

Pikaのaboutページに書かれている内容を一部抜粋します。

ES module syntax (ESM) is JavaScript's latest native module system. Officially ratified in 2015, its import / export syntax is more compact, more easily analyzed and more reliably optimized. This all results in smaller, faster JavaScript on the web.

Unfortunately, most sites still generate a single "oldest browser" bundle, which means that every user (even the majority using modern browsers) get over-compiled JavaScript bundles that don't support ESM.

Pika is working to build better tools and services that make modern, ESM-focused JavaScript more accessible to package authors and application developers.

 

deepL訳+細かい部分微調整したもの

ESモジュール構文(ESM)は、JavaScriptの最新のネイティブモジュールシステムです。2015年に正式に批准されたこのESMのimport/export構文は、よりコンパクトで、より解析が容易で、より確実に最適化されています。これにより、Web上でのJavaScriptの軽量化、高速化が実現します。

残念ながら、ほとんどのサイトではいまだに単一の「一番古いブラウザ用」バンドルが生成されており、これはすべてのユーザー(モダンブラウザを使用している大多数のユーザーでさえも)がESMをサポートしていない無駄にコンパイルされたJavaScriptバンドルを使用しなければならないことを意味します。

Pikaは、ESMに焦点を当てた最新のJavaScriptを、パッケージ作成者やアプリケーション開発者がより利用しやすいものにする、より良いツールやサービスの構築に取り組んでいます。

ということで、SnowpackもESMをフル活用する仕組みになっています。

ESMについて

ご存知の方はこちらの項目は読み飛ばしていただけたらと思います。

元々JSにはモジュールという仕様がなかったのですが、JSで中〜大規模な開発が行われ始めると、その必要性からモジュールの仕様がいくつか誕生しました。(CommonJS, AMDなど)

そしてさらに時は流れ、ES2015でESModule(略称ESM)という仕様が策定されました。

仕様が乱立していると色々と不便ですので、公式が仕様を定めてくれるのはありがたいことです。

当然ESModuleで一本化しようという流れになっています。

...IE(とその他レガシーブラウザ)以外は。

f:id:iricocco:20210119135256p:plain

 

つまり、ESMをフル活用する場合のSnowpackでは、IE11は切るしかありません。
(Snowpackでバンドルしてしまう方法もあります。)

JSモジュールの仕様についての歴史は、下記の記事がとても良くまとまっていたので紹介させていただきます。

uuuundefined.tokyo

最近のJSビルドツール事情

f:id:iricocco:20210119152900p:plain

www.npmtrends.com

npm trendsで確認すると、webpackの一人勝ちといった様子です。

Snowpackは新参者ということもあり、競争上はまだまだと言えます。

また、 2020年のstateofjsにおけるUsage(使用ランキング)でもwebpackはぶっちぎりです。

f:id:iricocco:20210119154403p:plain

2020.stateofjs.com

しかし特筆すべきなのは、満足度/関心で今熱いのはesbuildとSnowpackということです。

実は、何がそんなにユーザーを惹きつけているのか気になり、この記事を書くに至りました。

【Satisfaction: 満足度】

f:id:iricocco:20210119154527p:plain

【Interest: 関心】

f:id:iricocco:20210119154510p:plain

Snowpackの仕組み

通常設定では、Snowpackはバンドルを行いません。

先ほど説明したESMありきの構成なので、実際のバンドルはブラウザ標準のESMの方で行います。

これにより、従来のバンドルされた単一JSファイルでは出来ないことができるようになっています。

・全てを再ビルドする必要がない

単一JSバンドルの場合、たとえ1ファイルの一行を更新しただけだとしても、全てを再ビルドする必要があります。

しかし、ESMを利用したSnowpackであれば更新のあったファイルのみを更新し、その他はキャッシュを利用することができます。  

・自然にコードスプリッティングされる

依存パッケージが多数ある場合は、単一のJSだと初期ロードがとても重くなってしまいます。

それを解決するためにコードスプリッティングを行いますが、Snowpackだと依存ファイルはブラウザでバンドルされますので、自然にファイルが分割されます。

また、それぞれのビルドファイルは個別に作成され、無期限にキャッシュされます。  

そうはいってもnpm公開パッケージの中にはCommonJSというESM仕様でないものも多いです。

では、一つでもESMに準拠していないパッケージがあった場合は、Snowpack式を諦めてバンドルしなければいけないのでしょうか?

 

実はそうではなくて、Snowpackはそのようなパッケージを個別に処理してくれます。

一つ一つの依存関係のファイルを個別にバンドルし(例えばreact なら react.js、 react-domなら react-dom.js)、それをimportするというのです!

依存ファイルは滅多に更新されないので、こういったバンドル作業もほとんど発生しません。

 より詳しく知りたい方は公式の「How Snowpack Works」を参照ください。

www.snowpack.dev

また、小規模であればブラウザのESMによるバンドルでも問題ありませんが、大規模化してくるとやはりバンドルでの最適化が必要になってきます。

Snowpackではバンドルを行うように切り替えることも可能です。

www.snowpack.dev

Snowpackのビルドをめっちゃ早くできる話

実は2021年1月13日にSnowpack v3が公開されました🎉

このアップデートにより内部のバンドルツールにesbuildを採用できるようになりました。

esbuild.github.io

esbuildはGoで記述されたビルドツールで、本当に魔法のように早いのが売りです。

実際ベンチマークテストでは、他のツールより10~100倍早い結果となっています。

f:id:iricocco:20210119171739p:plain

私自身もesbuild単体でどれくらいスピードが上がるかテストしました。 下記がtimeコマンドで計測した結果です。

【Webpack】
real 0m4.018s 
user 0m3.989s
sys 0m0.585s

【esbuild】
real 0m0.451s
user 0m0.494s
sys 0m0.090s

real・・・プログラムの呼びだし~終了までの時間
user・・・プログラム自体の処理時間
sys・・・プログラムを処理するために、OSが処理をした時間

軽いプロジェクトで試したためなのか100倍は行きませんでした...が、

あきらかにスピードアップしています。

とは言っても、esbuildはまだバージョンがv0.8.33なので、これから成長していくプロジェクトです。

Snowpackがesbuildを採用できたのは、buildありきでバンドルはその後の最適化であるという設計に基づいていたからだと語られていました。

www.snowpack.dev

これからの成長に目が離せません。

Snowpack曰く、

esbuild is still a young project, but it’s future looks promising. In the meantime, we will also continue to invest in the existing Webpack & Rollup bundler plugins for a long time to come.

deepL訳+微調整

esbuildはまだ若いプロジェクトですが、将来性が見込めます。その間、既存のWebpack&Rollupバンドルプラグインへの投資も今後も長く続けていきます。

とのことです💰

Snowpackを実際に使ってみる

Snowpackに対するモチベーションが上がったところで、実際にReactのプロジェクトを下記のチュートリアルを参考にビルドしてみます。

www.snowpack.dev

 

まずは、craeate-snowpack-appコマンドを用いてアプリの土台を作成します。

テンプレートには今回使用するminimalのみでなく、lit-elementreactsveltevueなどが準備されています。

snowpack/create-snowpack-app/cli at main · snowpackjs/snowpack · GitHub

npx create-snowpack-app react-snowpack --template @snowpack/app-template-minimal

react-snowpackは作成するフォルダ名です。

--template @snowpack/app-template-minimal オプションをつけるとminimalというテンプレートが適用され、今回のチュートリアル用のシンプルな状態で構築してくれます。

 

下記コマンドでローカルサーバーを起動しましょう。

cd react-snowpack
npm run start

localhost:8080にて下記の画面が表示されればOKです。

f:id:iricocco:20210119161107p:plain

下記コマンドで依存パッケージをインストールします。

npm install react react-dom --save

Snowpackは.jsxextensionを見つけるとJSXをサポートしてくれます。

なので、index.jsindex.jsxにリネームします。

index.htmlのscriptタグのindex.jsはリネームする必要はありません。

なぜならコンパイル後は.js形式に変換してくれるからです。

 mv index.js index.jsx

index.htmlにidを振ったdivを配置し、reactを動かします。

-  <h1>Welcome to Snowpack!</h1>
+  <div id="root"></div>
- console.log('Hello World! You did it! Welcome to Snowpack :D');
+ import React from 'react'
+ import ReactDOM from 'react-dom'
+ ReactDOM.render(<div>"HELLO REACT"</div>, document.getElementById('root'))

 f:id:iricocco:20210119170449p:plain 無事マウントできました。

ビルドするためにほとんど設定ファイルを書かずに済んだのがわかるかと思います。

本記事ではここまでとしますが、公式チュートアルではHMRやFast Refresh、ファイル構成の変更などのやり方を学ぶことができるので、興味のある方はご確認ください。

Snowpack 3.0 で追加されたStreaming Imports

先日リリースされたver 3ですが、esbuildが組み込まれた以外にもStreaming Importsという面白い機能が追加されていました。

npm installしなくても、依存パッケージ使えるようにしちゃおうよ!

というとんでもない機能です。

import * as React from 'react'; //これを

import * as React from 'https://cdn.skypack.dev/react@17.0.1'; // こう解釈しちゃう

importを実行するとinstall済みのnode_modulesではなく、Skypack配信のCDNを読み込みに行く動作をします。

👨‍🦰「待て待て、そんなことしたらオフラインで動作できないじゃないか!」

と思われるかもしれませんが、一度ロードしたデータはキャッシュしますので、2回目以降はオフラインでも動作します。

かなり先進的な機能ですが、オプトイン式なので、使いたい人だけ使用することができます。

installしなくていい未来など想像していなかったので、個人的にとても衝撃的でした。

まとめ

シェアなどを見ると今すぐ採用するのはまだためらわれるところですが、非常に未来を感じるプロジェクトだと思いました!

今後も伸びていく予感がしています!

動向をチェックしつつ、機会があれば担当のプロジェクトに導入していきたいと思っています。

最後に...

Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp

Laravel8 使ってみた。

Laravel7との違い

①ルートファイルの書き方

Laravel7では以下の様な記述でしたが、

<?php

use Illuminate\Support\Facades\Route;

Route::get('/home', 'SampleController@index');

Laravel8ではuseでクラス名を指定し、[クラス名, 'メソッド名']で括る様な記述になりました。

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\SampleController;

Route::get('/home', [SampleController::class, 'index']);
②Modelsディレクトリの位置
php artisan make:model Post
  • Laravel7では

  • Laravel8からは

Jetstreamとは

Laravel8から新パッケージ「Jetstream」が導入されました。

何が出来るのかというと、Laravelにアプリケーションのスカフォールドを提供し、

ログイン、ユーザー登録、メール検証、2要素認証、セッション管理、Laravel Sanctumを介したAPIサポート、

およびオプションとしてチーム管理機能を含みます。

JetstreamはTailwindCSSを使用して設計されており、

フロントエンドスタックとして、LivewireとInertia.jsから選択して利用します。

Jetstreamを導入してみよう

まずLaravel8のプロジェクトを作成します。

f:id:shuto_komuro:20210118143323p:plain
Laravel8

ComposerでJetstreamをインストールします。

composer require laravel/jetstream

インストール後、jetstream:install Artisanコマンドを実行しフロントエンドスタックを導入します。(livewireかinertia)

簡単に説明すると、

  • livewire

    • PHP + Bladeで作成されたscaffolding(プロファイル画面など)
  • inertia

    • vue.jsで作成されたscaffolding(プロファイル画面など)

PHPでフロント部分、バックエンド部分共に実装したい方は、livewire。

この部分に関してはドキュメントを読むことをお勧めします。

php artisan jetstream:install livewire --teams
#チーム管理機能が不必要な場合、引数なし

インストールできたら、NPM依存をインストール・構築し、データベースをマイグレートします。

npm install && npm run dev

php artisan migrate

再度ブラウザをリロードします。

f:id:shuto_komuro:20210118151154p:plain
Jetstream

registerでユーザー登録しログインすると、「登録したユーザー名’s team」にオーナとして自動的に割り振られます。

f:id:shuto_komuro:20210118151422p:plain
home

「登録したユーザー名’s team」-> Team Settings からチームにメンバーを追加できます。

その際に役割、権限なども同時に割り振ることもできます。

f:id:shuto_komuro:20210118151941p:plain
team-setting

また役割、権限の追加、変更に関しては、 app/Providers/JetstreamServiceProvider.php より変更可能です。

<?php

protected function configurePermissions()
    {
        Jetstream::defaultApiTokenPermissions(['read']);

        Jetstream::role('admin', __('Administrator'), [
            'create',
            'read',
            'update',
            'delete',
        ])->description(__('Administrator users can perform any action.'));

        Jetstream::role('editor', __('Editor'), [
            'read',
            'create',
            'update',
        ])->description(__('Editor users have the ability to read, create, and update.'));

         -------  追記 -------
        Jetstream::role('checker', __('Checker'), [
            'read',
            'update',
            'delete',
        ])->description(__('Checker users have the ability to read, update, and delete.'));

    }

再度、「登録したユーザー名’s team」-> Team Settings を開くと、「Checker」が追加されました。

f:id:shuto_komuro:20210118153053p:plain
add role

〜最後に〜

Wizではエンジニアを募集しています!!

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp

【TypeScript】 HTMLElementに関する型推論

こんにちは。フロントエンド の仲本です。
今回はHTML要素に関する型推論について、一般的な型推論からデファルトでは型推論できない場合の対応方法までを紹介いたします。

要素に対する型推論

まずはHTML要素の一般的な型推論を紹介いたします。

<!DOCTYPE html>
<html lang='ja'>

  <head>
    <meta charset='utf-8'>
    <title>sample htmlelement typescript</title>
  </head>

  <body>
    <div class='example'>divタグです</div>
    <a id='exampleId' href='https://example'>リンクです</a>
    <p>pタグです</p>
    <input type='text' placeholder='textinput'/>
    <script src='bundle.js'></script>
  </body>

</html>

こちらのHTML内の要素を、querySelectorメソッドを使用して取得します。

const a = document.querySelector('a') 
// => HTMLAnchorElement | null 型

const div = document.querySelector('div')
// => HTMLDivElement | null 型

const input = document.querySelector('input')
// => HTMLInputElement | null型

またquerySelectorAllメソッド使用時の型推論も記述いたします。

const a = document.querySelectorAll('a') 
// => NodeListOf<HTMLAnchorElement>型

上記のような結果になります。

上記のコードでは要素名のみを指定していますが、 要素名直後にクラスセレクタやIDセレクタを付与すると型推論がうまく効かなくなります。

const div = document.querySelector('div.example')
// => Element | null 型

const a = document.querySelector('a#exampleId')
// => Element | null 型

上記の問題点を解決し型推論可能にしたライブラリが存在しますのでそちらも紹介いたします。

typed-query-selector

typed-query-selectorはTemplate Literal Typeを使ってdocument.querySelectorの引数から返す要素の型を推論するライブラリとなります。
導入から使用例までをいくつか紹介いたします。

導入

導入方法としてまずターミナルで以下を実行します。

$ npm i typed-query-selector

これで完了です。

使用例

次に先ほどと同じコードを使用いたします。

document.querySelector(要素名クラス or IDセレクタ)

import 'typed-query-selector'
// typed-query-selectorをimport

const div = document.querySelector('div.example')
// => HTMLDivElement | null 型

const a = document.querySelector('a#exampleId')
// => HTMLInputElement | null 型

document.querySelector(要素 > 要素)

import 'typed-query-selector'

const div = document.querySelector('body > div.example')
// => HTMLDivElement | null 型

const a = document.querySelector('body > a#exampleId')
// => HTMLInputElement | null 型

このようにtyped-query-selectorを使用すると、クラスセレクタやIDセレクタを付与した状態でも適切な型推論ができるようになりました。

他にも要素取得に関する色々なパターンでの型推論があります。
さらに詳しく学ばれる方は、typed-query-selectorのreadmeを確認していただくか、実際に導入してみて試してみるとより深く学べると思います。

最後に

以上がHTML要素に対する型推論の内容でした。

Wizではエンジニアを募集しています!!

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp

社内LT会はじめました。

wizでは現在ブランディング向上施策に力を入れています。 当ブログの開設も施策のひとつです。

さらに施策の一貫としてLT会を開催することになりました。

その狙いと、早速開催した第1回LT会の様子についてご紹介します。 LT会レポはフロントエンドエンジニアの仲本さんにまとめて頂きました!

LT会の狙い

社内でプレゼンを行うことのメリット

プレゼン会を定期的に行うことで以下の効果を狙っています。

  • 開発に関する知見の共有
  • コミュニケーションの活性化
  • 技術トレンドに敏感に

そしてこの取り組みを発信することで、wizのエンジニアの雰囲気を知ってもらう・ブランディング施策の効果があると考えています。

「LT会」であることのメリット

LT会は (5分以内のプレゼン + 質疑応答10分) × 4人 という形式で行います。

短い制限時間と決まった形式で行うことで発表者側の準備期間の短縮、視聴側のスケジュールの調整しやすさを狙っています。 テックブログ の運営同様に負担がかかりにくい死になない運営を目指しています!

第1回LT会レポ

こんにちは。フロントエンドの 仲本勇斗です。

 

12/4に第1回LT会をバックエンド、フロントエンドで行いました。

この記事ではそのLT会で発表した内容をご紹介していきます。

 

今回のLT会の内容は

発表者: 4名

制限時間: 発表時間 5分 + 質問時間 10分

テーマ: 自由

で行いました。

 

それでは1つずつ発表を紹介していきます。

 

LT会開催のすすめ

LT会開催のすすめ  

トップバッターの方には、第1回LT会をやるにあたって

なぜ開催するのかであったり、

どうしてLT会を行うのかなどを発表していただきました。

 

質問コーナーにて

募集人数が集まらなかった場合はどうするかなどの質問をいただきました。

答えとしては

LT会がそんなに必要性のないものとして判断され、

LT会を無くすという答えでした。

 

今回好評だったのでなくならないように運営を頑張っていきたいですね。

 

 

技術発信サイトで使われている技術について

 

二人目の方には技術発信サイトで使われている技術について発表していただきました。

 

Zennさん、noteさんなどで使用されている技術を紹介していただきました。

Wizでもオリジナルテックブログを作成する際には

技術選定は後悔しないようにしていきたいです。

 

2ヶ月サイネージ開発やってみた。

3人目(私です。)は2ヶ月サイネージ開発を行って、

その中で良かった点、改善すべき点について話してみました。

 

 

新規開発中に学んだこと(Token認証)

4人目の方には新規開発中で学んだToken認証について話していただきました。

 

今回は Laravel8のLaravel Sanctumを使用して実装したとのことでした。

 Laravel8の使いやすさなどのLTも次回以降どこかで発表していただきたいですね。

 

おわり  

初回はこのような内容でした。

 

この記事ではざっくりとした内容しか紹介できませんでしたが、

LT会の内容をもっと社外へ公開できるよう目指していきたいです。

Wizではエンジニアを募集中です。

興味のある方は是非覗いてみてください!↓

careers.012grp.co.jp