こんにちは。
今回は前々から気になっていたNode.jsの後継Denoについて調べましたので、Node.jsと比較しながら紹介していきたいと思います。
Denoとは
Node.jsの作者Ryan Dahlによって作られた、Rust製の新しいJavaScript/TypeScript実行環境です。
Node.jsと同じくV8エンジンを採用しています。
以下動画をみると"ディノ"と発音されるようです。
How to pronounce Deno (officially) - YouTube
環境
以下バージョンで動作確認を行なっています。
- Deno: v1.12.0
- deno_std: v0.101.0
開発環境のセットアップ
Deno CLIのセットアップ
公式サイトのGetting StartedをみながらDeno CLIをインストールします。
今回はHomebrew経由でインストールします。
$ brew install deno $ deno --version
各種IDEのセットアップ
Deno CLIのインストールが完了したら、以下公式ページを参考に各IDEでのセットアップを行います。 https://deno.land/manual@v1.12.1/getting_started/setup_your_environment#editors-and-ides
VSCodeの場合にはdenoland.vscode-deno
をインストールし、プロジェクトを開いた後、
Ctrl+Shift+P
(MacであればCmd+P
)よりDeno: Initialize Workspace Configuration
を実行し、.vscode/settings.json
を作成すれば補完などが効くようになります。
Denoの特徴
Deno公式に記載がありますので、1つ1つ見ていきましょう。
Secure by default. No file, network, or environment access (unless explicitly enabled).
Denoのまず大きな特徴として、Node.jsとは異なり実行時に権限を細かく制御できる点が挙げられます。
デフォルトではfetch APIを利用したネットワークアクセスすら行うことができません。
$ echo 'const res = await fetch("https://example.com");' > main.ts $ deno run main.ts error: Uncaught PermissionDenied: Requires net access to "example.com", run again with the --allow-net flag
実行時に明示的にネットワークアクセスを許可する必要があります。
$ deno run --allow-net=example.com main.ts
Supports TypeScript out of the box.
Node.jsではモジュールのインストールなどが必要でしたが、Denoは設定なしでTypeScriptを実行できます。
const add = (a: number, b: number) => { return a + b; }; console.info(add(1, 2));
$ deno run main.ts 3
Ships a single executable (
deno
).
実行環境は単一のバイナリとして提供されるため、単純にdenoのバイナリをDLするだけでdenoを実行できます。
denoのバイナリはgithub.com/denoland/deno/releasesからDLできます。
Has built-in utilities like a dependency inspector (deno info) and a code formatter (deno fmt).
Linter(deno_lint)やFormatter(dprint), テストランナー(Deno.test
)といった、今や開発時には欠かせないツールが標準で準備されています。
それぞれdeno lint
、deno fmt
、deno test
といったコマンドで実行することが可能です。
Has a set of reviewed (audited) standard modules that are guaranteed to work with Deno.
Denoはdeno_stdという標準モジュール群を有しています。
deno_stdのREADMEにも記載がありますが、Go's standard libraryを大いに参考にしているようです。
2021年7月現在、deno_stdの最新は0.102.0
であり、まだメジャーリリースされていません。変更が加わる可能性もあるため、バージョンを固定してimportすることが強く推奨されています(バージョンを固定したimportについては後述)。
Can bundle scripts into a single JavaScript file.
エントリーポイントとなるファイルを指定して、単一のjsファイルにバンドルすることができます。
// add.ts export const add = (a: number, b: number) => { return a + b; }; // main.ts import { add } from "./add.ts"; console.info(add(1, 2));
$ deno bundle main.ts main.bundle.js
// main.bundle.js const add = (a, b)=>{ return a + b; }; console.info(add(1, 2));
Node.jsとの違い
TypeScriptにネイティブ対応
Denoは以下のコマンド一発でTypeScriptを実行できます。
$ deno run main.ts
Node.jsの場合TypeScriptの実行のために依存パッケージを追加しなければならないのに対し、DenoはDenoのバイナリさえあればTypeScriptファイルを実行可能なので、
さくっとスクリプトを書きたいときなどにも非常に便利だと思います。
Promiseファースト
Denoが提供する非同期APIはPromiseを返しますので、callbackによりネストが深くなってしまう心配もありません。
しかしNode.jsの非同期APIでも徐々にPromiseを利用できるようになってきており(fs/promises
など)、そこまで大きな差ではなくなってくるかもしれません。
モジュールシステム
Node.jsとの一番大きな違いはこの点です。
package.jsonとnpmの廃止
Denoはnpm、node_modules、package.jsonを使用しません。
URLを利用したimport
Node.jsのようにnode_modulesを使用する代わりに、URLを指定してモジュールをインポートします。
バージョンの違いによる意図しない変更を防ぐため、バージョンを指定してのimportが強く推奨されています。
// 常にstdの最新版からimport import { copy } from "https://deno.land/std/io/util.ts"; // v0.101.0のstdからimport(recommended) import { copy } from "https://deno.land/std@0.101.0/io/util.ts";
また、Node.jsのようなpackage.jsonでの依存管理ではなく、deps.ts
とimport maps
で依存管理を行います。
deps.ts
deps.tsで外部モジュールをimport、re-exportし、依存モジュールを管理する方式です。
// deps.ts export { copy, readAll, writeAll, } from "https://deno.land/std@0.101.0/io/util.ts"; // main.ts import { copy, readAll, writeAll } from "./deps.ts";
import maps
import_map.json
{ "imports": { "io/": "https://deno.land/std@0.101.0/io/" } }
// main.ts import { copy, readAll, writeAll } from "io/util.ts";
実行時には--import-map
フラグでjsonを指定して実行します。
$ deno run --import-map=./import_map.json main.ts
上の通り、import mapsは実行時に1ファイルのみ指定できます。
CommonJSからESModuleへ
DenoはNode.jsとは異なりrequire
をサポートせず、import
export
のみをサポートしています。
モジュールの名前解決のルールも異なるため、基本的にNode.jsの資産をそのまま使用することはできません。
その他
deno cache
でモジュールをキャッシュする
Node.jsはnpm install
を実行することでローカルにモジュールを保存しますが、Denoは初回実行時にローカルにモジュールをキャッシュします。
キャッシュの保存先はdeno info
で確認することができます。
$ deno cache main.ts $ deno info
依存モジュールのcacheの更新を行いたい場合にはdeno cache --reload main.ts
で更新できます。
deno compile
で実行可能バイナリを生成する
個人的に嬉しいのが、deno compile main.ts
でスクリプトをスタンドアロンの実行可能バイナリにコンパイルしてくれる機能です。
Node.jsにもこのようなライブラリはありますが、Denoは標準機能として有しています。
以下のようなファイルを読み込んでコピーするスクリプトをシングルバイナリにコンパイルしてみたいと思います。
{ "imports": { "io/": "https://deno.land/std@0.101.0/io/" } }
import { copy } from "io/util.ts"; const fileName = Deno.args[0]; const src = await Deno.open(fileName); const dst = await Deno.create(`${fileName}_copy`); await copy(src, dst);
コンパイルはdeno compile
コマンドで実施します。
fileのreadとwriteを行っているので--allow-read
、--allow-write
フラグが必要です。
$ deno compile --allow-read --allow-write --import- map=./import_map.json -o main main.ts $ ./main test.txt
--target
フラグを使用してクロスコンパイルもできます。
シングルバイナリにコンパイルすることでNode.jsと比べスクリプトの配布も非常に楽になりありがたいですね。
Denoまとめ 感想
stdがまだstableでなかったり、Node.jsと比べるとやはりライブラリが少なかったりという点はありますが、
APIの安定化が進み、Node.jsライブラリのDeno対応が進めばさらにDenoの勢いは増していくのではないかと思います。
まだまだNode.jsを代替するまでは遠いかと思いますが、今後も注目していきたいと思います。
参考
- Deno - A secure runtime for JavaScript and TypeScript
- Denoとはなにか - 実際につかってみる - Qiita
- Effective Deno - Zenn
- File system | Node.js v16.6.1 Documentation
冒頭の画像について:Photo on iStock
エンジニア中途採用サイト
ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
ご興味ありましたら是非ご確認をお願いします。
https://career-recruit.rakus.co.jp/career_engineer/カジュアル面談お申込みフォーム
どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
以下フォームよりお申込みください。
rakus.hubspotpagebuilder.comラクスDevelopers登録フォーム
https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/イベント情報
会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!
◆TECH PLAY
techplay.jp
◆connpass
rakus.connpass.com