20210228のNode.jsに関する記事は10件です。

Reduxの環境を5分で構築しよう!

Reduxの環境を5分で構築しよう!

Reduxの環境を5分で構築する方法を記します。

Reduxとは

Redux(リダックス)とは、Reactにおけるコンポーネントの状態を管理する機能を提供するライブラリです。

今回のゴール

Node.jsをインストールし、Reactプロジェクトを作成し、ReactプロジェクトにReduxをインストールすること

OS情報

  • Windows10

構築手順

  1. Node.jsをインストール
    https://nodejs.org/ja/にアクセスし、推奨版をダウンロードします。
    標準設定でインストールする場合は、デフォルトのまま「Next」をクリックすればよいです。

  2. Reactプロジェクトを作成
    コマンドプロンプトを開き、任意のディレクトリにReactプロジェクトを作成します。
    npx create-react-app <project_name>と入力します。

  3. Reduxのインストール
    作成したプロジェクトにReduxをインストールします。
    まず作成したプロジェクトのルート階層へ移動します。
    npm install --save reduxと入力します。

  4. React Reduxのインストール
    「React Redux」というパッケージをインストールすることで、ReactプロジェクトでReduxを使用できるようになります。
    npm install --save react-reduxと入力します。

まとめ

以上の手順でReduxが使用できるようになりました。
新しくプロジェクトを作成する際には、1の手順は飛ばし、2~4の手順で作成しましょう。

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.jsでデータベースをherokuに接続できないの解決法

Unable to connect to the database: ConnectionError [SequelizeConnectionError]: self signed certificate

上記エラーが出たときloaderに下記コードを追加したら治りました。

    dialectOptions: {
  ssl: {
    require: true,
    rejectUnauthorized: false // <<<<<< YOU NEED THIS
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeGraphQLでN+1問題を解決した話

はじめに

GraphQLをサービスで使い始めて、N+1問題にぶち当たったのでその解決策を紹介する。

プロジェクト構成

  • Node.js
  • TypeScript
  • Express
  • GraphQL(Apollo, TypeGraphQL)

実際何が起こったか

DBにはとあるレコードが入っており、それぞれにuseridを保持している。
useridからユーザー名やメアドなどのユーザー情報を取り出すには、別の内部APIに問い合わせる必要がある。

GraphQLのスキーマはこのように定義している。

schema.gql
type Query {
  record(id: Int!): Record
  records(name: String): [Record!]!
}

type Record {
  id: Int!
  name: String!
  user: User
  userid: Int!
}

type User {
  userid: Int!
  username: String!
}

レコードはこのように取得しているとする。

RootResolver.ts
@Resolver()
export class RootResolver {
    @Query(returns => [Record])
    async records(): Promise<Record[]> {
        const records = await conn.query(`
            SELECT
                id,
                name,
                userid
            FROM
                xxx
        `);

        return records;
    }

    @Query(returns => Record, { nullable: true })
    async record(
        @Arg('id', type => Int) id: number
    ): Promise<Record | undefined> {
        const records = await conn.query(`
            SELECT
                id,
                name,
                userid
            FROM
                xxx
            WHERE
                id = ?
        `, [id]);

        return records[0];
    }
}

このとき、N+1問題を気にせずにuserリゾルバを書くことこのようになる。
fetchUsersは内部APIにリクエストを送ってユーザー情報を返す関数とする。

@Resolver(of => Record)
class RecordResolver {
    @FieldResolver()
    user(@Root() record: Record) {
        return fetchUsers([record.userid])[0];
    }
}

例えばRecordを1件だけ取得する場合は、内部APIへのリクエストは1回で済むが、
一覧画面などで100件取得する場合はfetchUsersがほぼ同時に100回呼ばれることとなり、内部APIサーバーやDBの負荷が上がってしまう。

10件取得した場合のログ

fetchUsers(0)
fetchUsers(1)
fetchUsers(2)
fetchUsers(3)
fetchUsers(4)
fetchUsers(5)
fetchUsers(6)
fetchUsers(7)
fetchUsers(8)
fetchUsers(9)

改善方法

リゾルバで即座にリクエストを送るのではなく、問い合わせたいIDを溜めて、バッチ処理で一つのリクエストに複数IDを載せて送ることでリクエストの量を削減させる。
※この場合、内部APIの方を複数IDに対応させる必要がある。

DataLoaderTypeGraphQL-DataLoaderを使うことでこれを簡単に実現できる。
DataLoaderは遅延読み込みをするためのFacebook製のライブラリで、TypeGraphQL-DataLoaderはDataLoaderをTypeGraphQLに適用させたライブラリである。

組み込み方法

ライブラリをインストールする。

npm i -S dataloader type-graphql-dataloader

プラグインを読み込む。

const server = new ApolloServer({
    schema: await makeSchema(),
    validationRules: [depthLimit(7)],
    plugins: [
        // これを追加
        ApolloServerLoaderPlugin({}),
    ]
});

userリゾルバをこのように修正する。

@Resolver(of => Record)
class RecordResolver {
    @FieldResolver()
    @Loader<number, User | undefined>(async (ids) => {
        const users = await fetchUsers([...ids]);
        return ids.map((id) => users.find((user) => user.userid === id));
    })
    user(@Root() record: Record) {
        return async (dataloader: DataLoader<number, User | undefined>) => {
            const user = await dataloader.load(record.userid);
            return user;
        };
    }
}

10件取得するとこのようなログになる。

fetchUsers(0,1,2,3,4,5,6,7,8,9)

おわりに

N+1問題は気づかずにDBや他のサーバーに負荷をかけてしまう可能性があるので注意して設計してほしい。
DataLoaderを使えば、意外と簡単に改善できるのでこれからも活用していきたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

話題のesbuildをさっくりと調査してみた

ブログからの転載です。

今大注目のesbuildに関してさっくり調べて見ました。
ゆるめの記事です。

調査のきっかけ

私がフロントを本格的に勉強し始めたのは2016-2017年のAngular2が出たころです。モダンフロントエンド開発の選択肢が増え、
PWAが提唱されるなどWEBフロントエンドが一気にリッチ化すると同時にカオス化した時期だったと思います。
つまり、本当に面白い状況だったのですが最近になってその状況も落ち着いてきたと感じている方も少なくないと思います。

昔よくあった、React vs Angular vs Vue論争の熱は冷めたし、
SSRだけでペラペラだった(ドキュメントもダサかったw)Next.jsも2019~2020年の大規模機能拡張でフルスタックフレームワークとして堂々たるツールに成長し、今では画像配信サーバを内臓し、画像の最適化まで面倒をみてくれます。
この背景として、先立って機能拡充が進んでいたNuxt.jsからのいいとこ取りができたところも大きいと思います。

私はAnguler, Nuxt, Nextを業務で使用したことがありますが、どのライブラリやフレームワークを使っても大体のことはできるし、
お互いを比較した時に、突出した優位性などはほとんどないと思っています。(個人的にreactが書いていて一番しっくりくるという好き嫌いくらいの話です。)

この成熟したフロントエンド環境の最後の大課題といっていいのがコンパイラとバンドラのパフォーマンスといってもいいと思います!
2020年は上述の背景の中、SPA系のプロジェクトの注目度も依然高い状況ですが、新たにビルドツール系のstar数の急激な伸びが印象的です。

ビルドツールのトレンドランキング!こちらをご参考

順位 名前 増加数
1 esbuild +16.6k
2 Rome +14.2k
3 Vita +14.1k
4 Snowpack +10.1k
5 Webpack +4.5k

う〜ん、とはいえみなさん、ビルドツールってなんか地味な印象ありませんか?私はそうでした。
ビルドツールといえば、フロントオタクがwebpackをゴリゴリチューニングしたり、rollupなどの色々ツールがあるもいまいち根本的にビジネスメリットがあるわけでもないし、
下手に手を出すと技に溺れやすい世界だし(公式ドキュメントにシンプルに保ちなさい的なこと書いてあるよね!)、第一最適化はフレームワークに任せればいいじゃん!っと考えていました。

しかし、今回注目したいesbuildはまさにフロントエンドの次の時代を作ってくれる様な、「すごい」ツールであるとキラキラとした期待を寄せてしまいます。

esbuildとは

webpackなどに変わるフロントエンドのコード変換+バンドルツールで、なんとWebpackと比較して10-100倍速度が早いそうです。

これが本当だとすると、フロントの開発効率は根本的に変わってくると思います。

当然の話ですが、現在のWebpackを使った各フレームワークのビルドはめちゃめちゃ遅いです。
nextやnuxtなどフレームワークで初期プロジェクトを構築し、デザインフレームワークを一つでも挿そうものなら、既に数十秒のビルド時間を覚悟する必要があります。
開発効率の向上のためのdev serverは、バンドルサイズが非常に重くブラウザのパフォーマンスが非常に厳しいものがあります。
また、HMRも実装が複雑らしくうまく反映されないことがあったり、SSRと相性が悪かったりと有効なシチュエーションが限られている現状です。
特に本番系でしか出ないエラーに遭遇した際は本当に泣くしかありませんw 数分(数十分)かけてビルドして、修正して、もう一回ビルドという経験をされている方も少なくないと思います。

この様に中規模以上のフロントエンド開発経験者であれば、「ビルドの遅さ」との戦いは開発プロセスの根本的な課題であることが実感としてあり、解決可能であれば正にエポックメイキングで、夢の様な話だなぁと感じます。
(本当かなぁ?)

esbuildの調査に当たってはアーキテクチャーなども見てみましたが、
the-super-tiny-compilerを読んだ程度では全く知識が足りずさっぱりでした、、w
そのせいか、開発は殆どEvan一人で進めている様です。

ちなみに、esbuildは2021/01時点でスター数が19kなので、本当に彗星のごとく現れたということが分かります。
さらにランキング3vitaはesbuildに依存しています。vueはビルドプロセスが独特なので、esbuildとは別のレイヤーで対応するべきとVueのEvanとesbuildのEvanがgit上で議論しています。
(kabukuさんの記事 より。)
4位のSnowpcakもesbuildに依存しています。

試してみた

create-next-appでプロジェクトを作成し、起動方法をreact-scriptからesbuildに変更してみました。
具体的にはプロジェクトのルートに下記のスクリプトを作成し、node build.jsを実行することでビルドします。

build.js
const watch = process.env.WATCH === 'true';
console.log( process.env.WATCH, watch );
require('esbuild').build({
  entryPoints: ['src/index.tsx'],
  bundle: true,
  outfile: 'dist/out.js',
  define: {
    "process.env.NODE_ENV": '"development"'
  },
  loader: {
    '.svg': 'dataurl'
  },
  minify: false,
  sourcemap: true,
  target: ['chrome70', 'firefox57', 'safari11', 'edge16'],
  watch,
}).catch(() => process.exit(1))

webpackを使用したことがある方であれば、なかなかシンプルで分かりやすい設定だと思います。
これだけの設定でバンドルを生成してくれます。

実際に実行してみました。

time node build.js
> real  0m0.282s

これはっっ、本当に早い!
初期状態のプロジェクトでもdev-serverを立ち上げるまでに数秒は待たされる所なので、春の日差しの様な清々しさを感じます。

esbuildできないこと

esbuildはまだ実験段階のプロダクトであるということと、パフォーマンスのため出来るだけシンプルな機能群に絞っているということもあり、出来ないこともはっきりしています。

私が確認した限りですと、下記はesbuildで実行することが出来ません。

AMEBAさんの記事もご参考

HMRに関しては対応の優先順は相当低そうです。(議論 => https://github.com/evanw/esbuild/issues/97)

こちらの議論によると、HMRはそもそもビルドが遅く、検証バンドルが重いWebpack環境ではデザインの編集など一部のユースケースで有効なこともありますが、
esbuildはそもそもビルドが爆速なので需要がそこまで高くないのと、HMRは複雑で実装コストが非常に高いということがあげられる様です。

esbuildができること

差分ビルド

watch: trueをすることで差分ビルドを走らせることができます。無論超高速です。

Base Pathの変更、Path Aliasの使用

TypeScriptを使用しているとベースパスの変更、パスエイリアスを使いたいことがあります。

// tsconfig.json
{
  "baseUrl": "./src/",
  "paths": {
    "@/*": ["./*"]
  },
}

// index.tsx
import App from 'components/App';
// or
import App from '@/components/App';

これは、自動的にパスを解釈してくれる様です。webpackだと、resolve.aliasを調整しないといけなかったりするので便利ですね。

CSS import

よくプロジェクトのルートでcssをimportしたいことがあります。

index.ts
import App from 'assets/styles/global.css';

この場合、index.tsxをバンドルすると、自動的にimportしているcssを全てまとめて、一つのcssファイルとして書き出してくれます。
今回のビルド設定だと、dist/out.cssが生成されます。

デザインライブラリの利用

material-uiと、私が最近贔屓にしているantdを読み込んでみましたが普通に使えました。
antdはcssを別途読み込む必要があり、公式ページのgetting startedではcssファイル内にimportを記述する方法が記載されていますが、
うまく行かないので、index.tsxでimportしてあげる様にすると動きました。

本当に早いのか

antdのボタンを一つ配置した状態でビルドをおこなってみました。

import { Button } from 'antd';

まずはWebpack選手。下記の通りwebpcak.configを書いて実行しています。
計測には time と speed-measure-webpack-pluginを利用しました。

webpack.config.js
const path = require( 'path' );
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();

module.exports = smp.wrap({
  mode: 'development',
  entry: path.resolve( __dirname, 'src/index.tsx' ),
  output: {
    path: path.resolve( __dirname, 'dist' ),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.(jsx|tsx|ts|js)$/,
        exclude: /node_modules/,
        loader: 'ts-loader'
      },
      {
        test: /\.(svg)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
      {
        test: /\.css$/,
        loaders: ['style-loader', 'css-loader'],
      },
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx', '.tsx', '.ts'],
    alias: {
      '@': path.resolve( __dirname, './src' ),
    },
    modules: [
      path.resolve( __dirname, './src' ),
      path.resolve( __dirname, './node_modules' ),
    ]
  },
  plugins: [],
});

実行

time npx webpack --config webpack.config.js

結果

 SMP  ⏱
General output time took 8.73 secs

 SMP  ⏱  Loaders
ts-loader took 5.24 secs
  module count = 4
modules with no loaders took 2.99 secs
  module count = 1030
css-loader took 0.449 secs
  module count = 3
url-loader took 0.066 secs
  module count = 1
style-loader, and
css-loader took 0.037 secs
  module count = 3

real    0m10.665s

10秒ちょっとなので、そんなもんかなぁという感じですね。

続いてesbuildです!

実行!

time node build.js

結果

real    0m0.608s

再び、春の日差しの様な清々しさと額の辺りに微かな清涼感を感じました。
esbuildの方が約18倍高速です

さらに esbuild-loaderなるものがあって、
webpcakのbabel-laoderやts-loaderを置き換える様にして使用することできます。

esbuild-loaderではcss importの解決ができない様で、ビルドを通すことができなかったのですが途中までのログはこんな感じです。

SMP  ⏱
General output time took 4.055 secs

 SMP  ⏱  Plugins
ESBuildPlugin took 0.01 secs

 SMP  ⏱  Loaders
esbuild-loader took 3.67 secs
  module count = 1033
modules with no loaders took 0.019 secs
  module count = 3

むむ?esbuild-loaderの所で3.6秒も使ってますね。実際にesbuild-loaderのissueをみても、
倍程度にしか高速化していないので、こんなものなのかもしれません。(それでも最高ではありますが。)

まとめるとこんな結果になりました。

ビルド方法 時間(約) ファイルサイズ
Webpack(dev) 10.6秒 6.92Mb
Webpcak(prd) 18.2秒 2.16Mb
esbuild(dev) 0.6秒 1.1Mb + 710kb
esbuild(prd) 0.6秒 420kb + 720kb

esbuildの場合、jsとcssは別バンドルになるので、ファイルサイズはjs, cssの順で記載しています。
これを見ると、本番ビルドではなんと30倍も高速であることが分かります!またファイルサイズもいずれもWebpackより小さくなっている様で、
開発時のブラウザの負荷も段違いだと思います。

実践投入に関して

まだesbuildは実験段階に位置付けられていますが、vita, snowpackなどすでに多くの新興ツールが依存している状態になり、今後の発展は非常に期待ができると思われます。

しかしながら、やっぱり本番運用まだ怖い場合は、ABEMAさんの記事でも提案されている様に、
検証系のビルド方法に一手間加えて、DXの爆上げという目的が使いやすいと思います。

将来的にエビデンスなどが揃いstableなツールとなれば、超高速CI/CDの実現など夢は広がります。

まとめ

esbuildはやっぱり早かった!SPAページを作成する際はあえて、nextを断念してでも導入を検討したいと思いました。

また、DenoやRomeなどのエコシステムのネクストジェネレーション?的なツールがどんどん出てきていますので、
近いうちに調査、比較を行ってみたいと思います。

長文になりましたが、最後まで読んでくださった方は、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

さっくりとesbuildを調査してみた

ブログからの転載です。

今大注目のesbuildに関してさっくり調べて見ました。
ゆるめの記事です。

調査のきっかけ

私がフロントを本格的に勉強し始めたのは2016-2017年のAngular2が出たころです。モダンフロントエンド開発の選択肢が増え、
PWAが提唱されるなどWEBフロントエンドが一気にリッチ化すると同時にカオス化した時期だったと思います。
つまり、本当に面白い状況だったのですが最近になってその状況も落ち着いてきたと感じている方も少なくないと思います。

昔よくあった、React vs Angular vs Vue論争の熱は冷めたし、
SSRだけでペラペラだった(ドキュメントもダサかったw)Next.jsも2019~2020年の大規模機能拡張でフルスタックフレームワークとして堂々たるツールに成長し、今では画像配信サーバを内臓し、画像の最適化まで面倒をみてくれます。
この背景として、先立って機能拡充が進んでいたNuxt.jsからのいいとこ取りができたところも大きいと思います。

私はAnguler, Nuxt, Nextを業務で使用したことがありますが、どのライブラリやフレームワークを使っても大体のことはできるし、
お互いを比較した時に、突出した優位性などはほとんどないと思っています。(個人的にreactが書いていて一番しっくりくるという好き嫌いくらいの話です。)

この成熟したフロントエンド環境の最後の大課題といっていいのがコンパイラとバンドラのパフォーマンスといってもいいと思います!
2020年は上述の背景の中、SPA系のプロジェクトの注目度も依然高い状況ですが、新たにビルドツール系のstar数の急激な伸びが印象的です。

ビルドツールのトレンドランキング!こちらをご参考

順位 名前 増加数
1 esbuild +16.6k
2 Rome +14.2k
3 Vita +14.1k
4 Snowpack +10.1k
5 Webpack +4.5k

う〜ん、とはいえみなさん、ビルドツールってなんか地味な印象ありませんか?私はそうでした。
ビルドツールといえば、フロントオタクがwebpackをゴリゴリチューニングしたり、rollupなどの色々ツールがあるもいまいち根本的にビジネスメリットがあるわけでもないし、
下手に手を出すと技に溺れやすい世界だし(公式ドキュメントにシンプルに保ちなさい的なこと書いてあるよね!)、第一最適化はフレームワークに任せればいいじゃん!っと考えていました。

しかし、今回注目したいesbuildはまさにフロントエンドの次の時代を作ってくれる様な、「すごい」ツールであるとキラキラとした期待を寄せてしまいます。

esbuildとは

webpackなどに変わるフロントエンドのコード変換+バンドルツールで、なんとWebpackと比較して10-100倍速度が早いそうです。

これが本当だとすると、フロントの開発効率は根本的に変わってくると思います。

当然の話ですが、現在のWebpackを使った各フレームワークのビルドはめちゃめちゃ遅いです。
nextやnuxtなどフレームワークで初期プロジェクトを構築し、デザインフレームワークを一つでも挿そうものなら、既に数十秒のビルド時間を覚悟する必要があります。
開発効率の向上のためのdev serverは、バンドルサイズが非常に重くブラウザのパフォーマンスが非常に厳しいものがあります。
また、HMRも実装が複雑らしくうまく反映されないことがあったり、SSRと相性が悪かったりと有効なシチュエーションが限られている現状です。
特に本番系でしか出ないエラーに遭遇した際は本当に泣くしかありませんw 数分(数十分)かけてビルドして、修正して、もう一回ビルドという経験をされている方も少なくないと思います。

この様に中規模以上のフロントエンド開発経験者であれば、「ビルドの遅さ」との戦いは開発プロセスの根本的な課題であることが実感としてあり、解決可能であれば正にエポックメイキングで、夢の様な話だなぁと感じます。
(本当かなぁ?)

esbuildの調査に当たってはアーキテクチャーなども見てみましたが、
the-super-tiny-compilerを読んだ程度では全く知識が足りずさっぱりでした、、w
そのせいか、開発は殆どEvan一人で進めている様です。

ちなみに、esbuildは2021/01時点でスター数が19kなので、本当に彗星のごとく現れたということが分かります。
さらにランキング3vitaはesbuildに依存しています。vueはビルドプロセスが独特なので、esbuildとは別のレイヤーで対応するべきとVueのEvanとesbuildのEvanがgit上で議論しています。
(kabukuさんの記事 より。)
4位のSnowpcakもesbuildに依存しています。

試してみた

create-next-appでプロジェクトを作成し、起動方法をreact-scriptからesbuildに変更してみました。
具体的にはプロジェクトのルートに下記のスクリプトを作成し、node build.jsを実行することでビルドします。

build.js
const watch = process.env.WATCH === 'true';
console.log( process.env.WATCH, watch );
require('esbuild').build({
  entryPoints: ['src/index.tsx'],
  bundle: true,
  outfile: 'dist/out.js',
  define: {
    "process.env.NODE_ENV": '"development"'
  },
  loader: {
    '.svg': 'dataurl'
  },
  minify: false,
  sourcemap: true,
  target: ['chrome70', 'firefox57', 'safari11', 'edge16'],
  watch,
}).catch(() => process.exit(1))

webpackを使用したことがある方であれば、なかなかシンプルで分かりやすい設定だと思います。
これだけの設定でバンドルを生成してくれます。

実際に実行してみました。

time node build.js
> real  0m0.282s

これはっっ、本当に早い!
初期状態のプロジェクトでもdev-serverを立ち上げるまでに数秒は待たされる所なので、春の日差しの様な清々しさを感じます。

esbuildできないこと

esbuildはまだ実験段階のプロダクトであるということと、パフォーマンスのため出来るだけシンプルな機能群に絞っているということもあり、出来ないこともはっきりしています。

私が確認した限りですと、下記はesbuildで実行することが出来ません。

AMEBAさんの記事もご参考

HMRに関しては対応の優先順は相当低そうです。(議論 => https://github.com/evanw/esbuild/issues/97)

こちらの議論によると、HMRはそもそもビルドが遅く、検証バンドルが重いWebpack環境ではデザインの編集など一部のユースケースで有効なこともありますが、
esbuildはそもそもビルドが爆速なので需要がそこまで高くないのと、HMRは複雑で実装コストが非常に高いということがあげられる様です。

esbuildができること

差分ビルド

watch: trueをすることで差分ビルドを走らせることができます。無論超高速です。

Base Pathの変更、Path Aliasの使用

TypeScriptを使用しているとベースパスの変更、パスエイリアスを使いたいことがあります。

// tsconfig.json
{
  "baseUrl": "./src/",
  "paths": {
    "@/*": ["./*"]
  },
}

// index.tsx
import App from 'components/App';
// or
import App from '@/components/App';

これは、自動的にパスを解釈してくれる様です。webpackだと、resolve.aliasを調整しないといけなかったりするので便利ですね。

CSS import

よくプロジェクトのルートでcssをimportしたいことがあります。

index.ts
import App from 'assets/styles/global.css';

この場合、index.tsxをバンドルすると、自動的にimportしているcssを全てまとめて、一つのcssファイルとして書き出してくれます。
今回のビルド設定だと、dist/out.cssが生成されます。

デザインライブラリの利用

material-uiと、私が最近贔屓にしているantdを読み込んでみましたが普通に使えました。
antdはcssを別途読み込む必要があり、公式ページのgetting startedではcssファイル内にimportを記述する方法が記載されていますが、
うまく行かないので、index.tsxでimportしてあげる様にすると動きました。

本当に早いのか

antdのボタンを一つ配置した状態でビルドをおこなってみました。

import { Button } from 'antd';

まずはWebpack選手。下記の通りwebpcak.configを書いて実行しています。
計測には time と speed-measure-webpack-pluginを利用しました。

webpack.config.js
const path = require( 'path' );
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const smp = new SpeedMeasurePlugin();

module.exports = smp.wrap({
  mode: 'development',
  entry: path.resolve( __dirname, 'src/index.tsx' ),
  output: {
    path: path.resolve( __dirname, 'dist' ),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.(jsx|tsx|ts|js)$/,
        exclude: /node_modules/,
        loader: 'ts-loader'
      },
      {
        test: /\.(svg)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
            },
          },
        ],
      },
      {
        test: /\.css$/,
        loaders: ['style-loader', 'css-loader'],
      },
    ]
  },
  resolve: {
    extensions: ['.js', '.jsx', '.tsx', '.ts'],
    alias: {
      '@': path.resolve( __dirname, './src' ),
    },
    modules: [
      path.resolve( __dirname, './src' ),
      path.resolve( __dirname, './node_modules' ),
    ]
  },
  plugins: [],
});

実行

time npx webpack --config webpack.config.js

結果

 SMP  ⏱
General output time took 8.73 secs

 SMP  ⏱  Loaders
ts-loader took 5.24 secs
  module count = 4
modules with no loaders took 2.99 secs
  module count = 1030
css-loader took 0.449 secs
  module count = 3
url-loader took 0.066 secs
  module count = 1
style-loader, and
css-loader took 0.037 secs
  module count = 3

real    0m10.665s

10秒ちょっとなので、そんなもんかなぁという感じですね。

続いてesbuildです!

実行!

time node build.js

結果

real    0m0.608s

再び、春の日差しの様な清々しさと額の辺りに微かな清涼感を感じました。
esbuildの方が約18倍高速です

さらに esbuild-loaderなるものがあって、
webpcakのbabel-laoderやts-loaderを置き換える様にして使用することできます。

esbuild-loaderではcss importの解決ができない様で、ビルドを通すことができなかったのですが途中までのログはこんな感じです。

SMP  ⏱
General output time took 4.055 secs

 SMP  ⏱  Plugins
ESBuildPlugin took 0.01 secs

 SMP  ⏱  Loaders
esbuild-loader took 3.67 secs
  module count = 1033
modules with no loaders took 0.019 secs
  module count = 3

むむ?esbuild-loaderの所で3.6秒も使ってますね。実際にesbuild-loaderのissueをみても、
倍程度にしか高速化していないので、こんなものなのかもしれません。(それでも最高ではありますが。)

まとめるとこんな結果になりました。

ビルド方法 時間(約) ファイルサイズ
Webpack(dev) 10.6秒 6.92Mb
Webpcak(prd) 18.2秒 2.16Mb
esbuild(dev) 0.6秒 1.1Mb + 710kb
esbuild(prd) 0.6秒 420kb + 720kb

esbuildの場合、jsとcssは別バンドルになるので、ファイルサイズはjs, cssの順で記載しています。
これを見ると、本番ビルドではなんと30倍も高速であることが分かります!またファイルサイズもいずれもWebpackより小さくなっている様で、
開発時のブラウザの負荷も段違いだと思います。

実践投入に関して

まだesbuildは実験段階に位置付けられていますが、vita, snowpackなどすでに多くの新興ツールが依存している状態になり、今後の発展は非常に期待ができると思われます。

しかしながら、やっぱり本番運用まだ怖い場合は、ABEMAさんの記事でも提案されている様に、
検証系のビルド方法に一手間加えて、DXの爆上げという目的が使いやすいと思います。

将来的にエビデンスなどが揃いstableなツールとなれば、超高速CI/CDの実現など夢は広がります。

まとめ

esbuildはやっぱり早かった!SPAページを作成する際はあえて、nextを断念してでも導入を検討したいと思いました。

また、DenoやRomeなどのエコシステムのネクストジェネレーション?的なツールがどんどん出てきていますので、
近いうちに調査、比較を行ってみたいと思います。

長文になりましたが、最後まで読んでくださった方は、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Node.js】promiseの使い方

プログラミング勉強日記

2021年2月28日

基本的な書き方

 promise処理を作るには任意の関数の中でnew Promise()を返すのが基本となる。

return new Promise(resolve) {
    // 処理を記述する
}
具体例
function myFunction() {
  return new Promise(function (resolve) {
      resolve("Hello World");
  })
}

thenを使ったメソッドチェーン

 promiseによる非同期処理の結果を取得するにはthenを使ったメソッドチェーンを使用することができる。

// dataにpromiseの結果が格納されている
// resolve()に設定した文字列になる
myFunction().then(function(data) { console.log(data) })
実行結果
Hello World

promiseの並列処理

 allメソッドとraceメソッドを使った方法がある。
 allメソッドは、違うpromise処理が記述された関数をまとめて実行して、すべての結果が得られたタイミングでthenを実行できるようにする。なので、複数のpromise処理の結果をまとめて取得したい場合に向いてる。
 raceメソッドも複数のpromise処理を実行できるが、最初に結果が得られたpromiseの結果だけをthenメソッドで取得できる。

エラーハンドリング

 promiseの引数には結果を格納するresolveだけではなく、エラー情報を格納するrejectを利用できる。

function myFunction() {
  return new Promise(function (resolve, reject) {
      reject(new Error("エラーが発生しました"));
  })
}
myPromise()
  .then(function(data) {
    console.log(data);
  }, function(error) {
    console.log(error.message);
  })
実行結果
エラーが発生しました

参考文献

非同期処理:コールバック/Promise/Async Function
【node.js入門】Promiseによる非同期処理の使い方まとめ!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js版CCXTでbitFlyerのPrivate APIを使う

はじめに

ccxt.js経由でbitFlyerの現在のポジション(建玉)を知りたかったのだが、ちょっと調べてもなかった。
詰まったので同様の問題に遭遇した人のためにも解決方法残しておこうと思う。

コード

const ccxt = require('ccxt')
const bitflyer = new ccxt.bitflyer({
    apiKey: env.apiKey, // APIキーを入れる
    secret: env.secret // シークレットをいれる
})
bitflyer.privateGetGetpositions({ product_code: 'FX_BTC_JPY' }).then(data => console.log(data))

ちょっと解説

ccxtでPrivate APIを使うには、メソッドの命名に独自の法則を使っている

private + {メソッドがgetならGet、postならPost} + {private APIの名前}

上の例ではGetメソッドでgetpositionsAPIを使いたいがために、privateGetGetpositionsとなる。
またクエリパラメータが必須なので、それはメソッドの引数としてオブジェクトを渡す。

参考

Python版だが以下の説明がわかりやすかった。
Bitflyerや各取引所の個別APIをCCXTライブラリ経由で直接利用する方法

使いたいbitFlyerのPrivate APIは以下を参照
API Documentation

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Node.js][LINE] 毎朝 LINE に花粉情報を通知する

概要

に引き続き、LINE Notify を使って LINE に通知するシリーズです!

花粉 (デス・パウダー)が辛い季節ですね :disappointed_relieved: 花粉情報が非常に気になる毎日なので、毎朝当日の花粉情報を LINE に通知するようにしました。

方法

バージョン情報

  • Node.js 14.15.5
  • Playwright 1.9.1
  • axios 0.21.1

コード

まず Playwright を使ってヘッドレス Chrome を操作し、Yahoo! JAPAN の花粉情報のスクリーンショットを撮影します。花粉情報のページの URL は任意の地域のものに書き換えてください。本当は 日本気象協会 tenki.jp の花粉情報を通知したかったのですが、Playwright でのアクセスがタイムアウトになってしまうので諦めました (スクレイピング対策されているのかも) 。

そして axios を使って LINE Notify の API を叩き、LINE に通知します。LINE Notify はメッセージを送信できるだけでなく、画像を送信することもできます。

~/workspace/death_powder_forcast/main.js
const { chromium } = require('playwright');
const fs = require('fs');
const axios = require('axios');
const FormData = require('form-data');

// Yahoo! JAPAN の福岡市中央区の花粉情報。
const POLLEN_FORCAST_URL = 'https://weather.yahoo.co.jp/weather/pollen/10/40/40133/'; 
const SCREENSHOT_FILEPATH = './today.png';
const LINE_NOTIFY_TOKEN = 'ここに LINE Notify のトークンを入力する。';

// 指定した要素のスクリーンショットを撮影する。
const takeScreenshot = async ({ url, selector, filepath }) => {
  // Raspberry Pi では ARM 用の Chromium を使用する必要があるので executablePath を指定する。
  const browser = await chromium.launch({ executablePath: '/usr/bin/chromium-browser' });
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto(url);
  const element = await page.$(selector);
  await element.screenshot({ path: filepath })
  await browser.close();
};

// LINE Notify に通知する。
const notifyLINE = async ({ token, message, imageFilepath = null }) => {
  const LINE_NOTIFY_API_URL = 'https://notify-api.line.me/api/notify';

  const formData = new FormData();
  formData.append('message', message);
  if (imageFilepath) {
    formData.append('imageFile', fs.createReadStream(SCREENSHOT_FILEPATH));
  }

  const headers = {
    'Authorization': `Bearer ${token}`,
    ...formData.getHeaders()
  };

  await axios.post(LINE_NOTIFY_API_URL, formData, { headers });
};

(async () => {
  await takeScreenshot({
    url: POLLEN_FORCAST_URL,
    selector: '.pollenFlying_city_day:first-child',
    filepath: SCREENSHOT_FILEPATH
  });

  if (!fs.existsSync(SCREENSHOT_FILEPATH)) {
    process.exit(1);
  }

  await notifyLINE({
    token: LINE_NOTIFY_TOKEN,
    message: '今日の花粉情報です。',
    imageFilepath: SCREENSHOT_FILEPATH
  })

  fs.unlinkSync(SCREENSHOT_FILEPATH);
})();

上記の Node.js ファイルを cron で定期実行します。時刻はリマインドしてほしい時刻を指定してください。

crontab
# 設定例
0 7 * * * /bin/bash -c 'export PATH=$HOME/.nodenv/bin:$PATH; eval "$(nodenv init -)"; cd $HOME/workspace/death_powder_forcast; node main.js'

これで通知が届きました ?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Node.js][LINE] 毎日 LINE に花粉情報を通知する

概要

に引き続き、LINE Notify を使って LINE に通知するシリーズです!

花粉 (デス・パウダー)が辛い季節ですね :disappointed_relieved: 花粉情報が非常に気になる毎日なので、当日の花粉情報を LINE に通知するようにしました。

方法

バージョン情報

  • Node.js 14.15.5
  • Playwright 1.9.1
  • axios 0.21.1

コード

まず Playwright を使ってヘッドレス Chrome を操作し、Yahoo! JAPAN の花粉情報のスクリーンショットを撮影します。花粉情報のページの URL は任意の地域のものに書き換えてください。なお、本当は 日本気象協会 tenki.jp の花粉情報を通知したかったのですが、Playwright でのアクセスがタイムアウトになってしまうので諦めました。スクレイピング対策されているのかもしれませんね。

そして axios を使って LINE Notify の API を叩き、LINE に通知します。LINE Notify はメッセージを送信できるだけでなく、画像を送信することもできます。

~/workspace/death_powder_forcast/main.js
const { chromium } = require('playwright');
const fs = require('fs');
const axios = require('axios');
const FormData = require('form-data');

// Yahoo! JAPAN の福岡市中央区の花粉情報。
const POLLEN_FORCAST_URL = 'https://weather.yahoo.co.jp/weather/pollen/10/40/40133/'; 
const SCREENSHOT_FILEPATH = './today.png';
const LINE_NOTIFY_TOKEN = 'ここに LINE Notify のトークンを入力する。';

// 指定した要素のスクリーンショットを撮影する。
const takeScreenshot = async ({ url, selector, filepath }) => {
  // Raspberry Pi では ARM 用の Chromium を使用する必要があるので executablePath を指定する。
  const browser = await chromium.launch({ executablePath: '/usr/bin/chromium-browser' });
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto(url);
  const element = await page.$(selector);
  await element.screenshot({ path: filepath });
  await browser.close();
};

// LINE Notify に通知する。
const notifyLINE = async ({ token, message, imageFilepath = null }) => {
  const LINE_NOTIFY_API_URL = 'https://notify-api.line.me/api/notify';

  const formData = new FormData();
  formData.append('message', message);
  if (imageFilepath) {
    formData.append('imageFile', fs.createReadStream(imageFilepath));
  }

  const headers = {
    'Authorization': `Bearer ${token}`,
    ...formData.getHeaders()
  };

  await axios.post(LINE_NOTIFY_API_URL, formData, { headers });
};

(async () => {
  await takeScreenshot({
    url: POLLEN_FORCAST_URL,
    selector: '.pollenFlying_city_day:first-child',
    filepath: SCREENSHOT_FILEPATH
  });

  if (!fs.existsSync(SCREENSHOT_FILEPATH)) {
    process.exit(1);
  }

  await notifyLINE({
    token: LINE_NOTIFY_TOKEN,
    message: '今日の花粉情報です。',
    imageFilepath: SCREENSHOT_FILEPATH
  });

  fs.unlinkSync(SCREENSHOT_FILEPATH);
})();

上記の Node.js ファイルを cron で定期実行します。通知してほしい時刻を指定してください。

crontab
# 設定例
0 7 * * * /bin/bash -c 'export PATH=$HOME/.nodenv/bin:$PATH; eval "$(nodenv init -)"; cd $HOME/workspace/death_powder_forcast; node main.js'

これで通知が届きました ?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

nodebrewを利用したnodeのインストール

Homebrewを使ってインストール。

$ brew install nodebrew
  • バージョン確認
$ nodebrew -v
  • セットアップコマンド。出力されたパスを通す。
$ nodebrew setup

# Fetching nodebrew...
# Installed nodebrew in $HOME/.nodebrew
# 
# ========================================
# Export a path to nodebrew:
# 
# export PATH=$HOME/.nodebrew/current/bin:$PATH <= コイツを記述
# ========================================
$ vi ~/.bash_profile
  • インストール可能なバージョンの確認
$ nodebrew ls-remote
  • stableをインストール。installコマンドと比較してinstall-binaryの方が速いらしい。
$ nodebrew install-binary stable
  • インストールしたバージョンの確認。及び実行するバージョンを指定。
$ nodebrew ls
# v14.16.0
# 
# current: none
$ nodebrew use v14.16.0
  • バージョンの確認。
$ nodebrew ls
# v14.16.0
# 
# current: v14.16.0
$ node -v

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む