diff --git "a/source/_posts/20241125a_Vue.js\351\200\243\350\274\211\345\247\213\343\202\201\343\201\276\343\201\231_&_Nuxt\343\201\256\351\200\232\344\277\241\343\203\221\343\202\277\343\203\274\343\203\263\343\202\202\350\246\213\343\201\246\343\201\277\343\202\213.md" "b/source/_posts/20241125a_Vue.js\351\200\243\350\274\211\345\247\213\343\202\201\343\201\276\343\201\231_&_Nuxt\343\201\256\351\200\232\344\277\241\343\203\221\343\202\277\343\203\274\343\203\263\343\202\202\350\246\213\343\201\246\343\201\277\343\202\213.md" index 4ce91d6459f..ae8ec3b3494 100644 --- "a/source/_posts/20241125a_Vue.js\351\200\243\350\274\211\345\247\213\343\202\201\343\201\276\343\201\231_&_Nuxt\343\201\256\351\200\232\344\277\241\343\203\221\343\202\277\343\203\274\343\203\263\343\202\202\350\246\213\343\201\246\343\201\277\343\202\213.md" +++ "b/source/_posts/20241125a_Vue.js\351\200\243\350\274\211\345\247\213\343\202\201\343\201\276\343\201\231_&_Nuxt\343\201\256\351\200\232\344\277\241\343\203\221\343\202\277\343\203\274\343\203\263\343\202\202\350\246\213\343\201\246\343\201\277\343\202\213.md" @@ -24,7 +24,7 @@ Vue.js連載企画を始めます。今年は勤労感謝の日が土曜日で | 11/26(火) | 村田靖拓さん | [2015年頃のフロントエンジニアだってvoid(0)のワクワクを理解したい](/articles/20241126a/) | | 11/27(水) | 大岩潤矢さん | [Vue3・Nuxt3アプリをPWA化する方法 2024年版](/articles/20241127a/) | | 11/28(木) | 永井優斗さん | [Vue Fes Japan 2024報告](/articles/20241128a/) | -| 11/29(金) | 山本竜玄さん | Deno × Vueを触ってみた(2024年冬) | +| 11/29(金) | 山本竜玄さん | [Deno × Vueを触ってみた(2024年冬)](/articles/20241129a/) | # Nuxtの通信パターンも見てみる diff --git "a/source/_posts/20241129a_Deno_\303\227_Vue\343\202\222\350\247\246\343\201\243\343\201\246\343\201\277\343\201\237(2024\345\271\264\345\206\254).md" "b/source/_posts/20241129a_Deno_\303\227_Vue\343\202\222\350\247\246\343\201\243\343\201\246\343\201\277\343\201\237(2024\345\271\264\345\206\254).md" new file mode 100644 index 00000000000..7eb3230c2e2 --- /dev/null +++ "b/source/_posts/20241129a_Deno_\303\227_Vue\343\202\222\350\247\246\343\201\243\343\201\246\343\201\277\343\201\237(2024\345\271\264\345\206\254).md" @@ -0,0 +1,469 @@ +--- +title: "Deno × Vueを触ってみた(2024年冬)" +date: 2024/11/29 00:00:01 +postid: a +tag: + - Vue.js + - Deno +category: + - Infrastructure +thumbnail: /images/20241129a/thumbnail.png +author: 山本竜玄 +lede: "eno v2とVue.jsを組み合わせた公式チュートリアルの実施を通して、2024年冬時点での現状や特徴について解説していきます。" +--- + + +本記事は[Vue.js連載](/articles/20241125a/)5本目の記事です。 + +## はじめに + +HealthCare Innovation Group(HIG)所属の山本です。 + +Vue.jsは素晴らしいフロントエンドのフレームワークとして、世界中で広く使用されています。公式ドキュメントでも非常に整っておりインストール手順やチュートリアルについて記載されていますが、Node.jsを利用することが前提となっているものが多いです。 + +実際に私が関わったVue.jsを使用したプロジェクトでも、Node.jsおよびnpmを使用しており、かなり一般的な選択肢ではないかと感じています。 + +そんな中、Node.jsに替わるJavaScriptランタイムとして新たに注目を集めているのがDenoです。2024年にはDeno v2がリリースされ、フォーマットやテストなどなどのDenoの機能の改善以外にも、Node.jsおよびnpmとの下位互換性を取り入れたことがとても大きな変更としてあります。 + +この記事では、Deno v2とVue.jsを組み合わせた公式チュートリアルの実施を通して、2024年冬時点での現状や特徴について解説していきます。 + +## Denoについて + +JavaScript実行環境のNode.jsの開発者であるRyan Dahlが2018年に発表した、JavaScript/TypeScript実行環境がDenoです。 + +Node.jsの設計上の問題点を解決するために0から作り直されたことが特徴です。 + +実際に、JSConf 2018にて「Node.jsに関する10の反省点」としてRyan Dahl自身が登壇し、以下のようなことを述べています。 + +https://www.youtube.com/watch?v=M3BM9TB-8yA + +1. Promiseを使い続けなかったため、Nodeの非同期APIに課題があること +2. セキュリティ設計について。例えばlinterのnetworkフルアクセスなど +3. ビルドシステム(GYP)を継続使用したこと +4. package.jsonの存在、肥大化について +5. node_modulesの存在、モジュール解決アルゴリズムの複雑化について +6. require("module")で.jsの拡張子なしで読み込み可能としたこと +7. index.jsの存在、モジュール読み込みが複雑化したこと + +これらの設計に関する課題について述べたあと、Ryan Dahlはまだまだプロダクトレベルであることを前置きしたうえで、新しく開発したDenoを紹介しています。 + +そこからときが経ち2024年、Deno v2のリリースが発表されました。 + +https://deno.com/blog/v2.0 + +Denoの設計思想に基づき、ネイティブのTypeScriptのサポート、Promiseなどのサポート、組み込みのフォーマッタや型チェック、セキュリティの考慮などが特徴である他、v2からはNode.jsおよびnpmの下位互換性が追加されています。 + +## Deno × Vue.jsについて + +ここからは、実際にDeno × Vue.jsでアプリ構築を実際に試していきます。 +記事執筆時点では、公式ドキュメントのチュートリアルとして以下があるので、その手順に従っていきます。 + +https://docs.deno.com/runtime/tutorials/how_to_with_npm/vue/ + + + +### 1. インストール + +Deno自体のインストールについては、下記のドキュメントに従います。 + +https://docs.deno.com/runtime/getting_started/installation/ + +``` +curl -fsSL https://deno.land/install.sh | sh +``` + +シェルスクリプトの一発でインストールできてお手軽ですね。記事執筆時点では、v2.1.2がインストールされました。 + +Denoのインストール後、以下のコマンドでプロジェクトのセットアップを進めます。 + +``` +deno run -A npm:create-vite +``` + +フレームワークの選択が求められるので、Vueを選択します。 + +``` +❯ deno run -A npm:create-vite +✔ Project name: … vite-project +? Select a framework: › - Use arrow-keys. Return to submit. + Vanilla +❯ Vue + React + Preact + Lit + Svelte + Solid + Qwik + Angular + Others +``` + +こちらも選択が求められますが、せっかくなので、DenoのネイティブのTypeScriptのサポートを確認するために、TypeScriptを選択します。 + +TypeScriptがネイティブでサポートされているため、トランスパイル設定などの手間が不要なのが嬉しいポイントです。 + +```sh +❯ deno run -A npm:create-vite +✔ Project name: … vite-project +✔ Select a framework: › Vue +? Select a variant: › - Use arrow-keys. Return to submit. +❯ TypeScript + JavaScript + Customize with create-vue ↗ + Nuxt ↗ +``` + +次に、プロジェクトフォルダに移動して依存関係のインストールを行います。 + +```sh +deno install +``` + +これでインストールは完了です。`deno install`の実行時点で、`node_modules`フォルダが作成されます。 +フォルダの内容としては以下のようになっています。 + +```sh +node_modules/ +├── typescript -> .deno/typescript@5.6.3/node_modules/typescript +├── vite -> .deno/vite@6.0.1/node_modules/vite +├── @vitejs +│   └── plugin-vue -> ../.deno/@vitejs+plugin-vue@5.2.1/node_modules/@vitejs/plugin-vue +├── vue -> .deno/vue@3.5.13/node_modules/vue +└── vue-tsc -> .deno/vue-tsc@2.1.10/node_modules/vue-tsc + +``` + +`npm install`などのコマンド実行を行うことなく、npmレジストリよりパッケージをインストールすることができていますね。 + + +ここまででテンプレートは作成できており、以下のコマンドでアプリ起動ができます。 + +```sh +deno task dev +``` + + + +### 2. バックエンドの追加 + +ここからもチュートリアあるに従っていきます。以下コマンドで、バックエンドに必要なものを追加していきます。 + +```sh +deno add jsr:@oak/oak jsr:@tajpouria/cors +``` + +構成としては、プロジェクト配下にapiフォルダを作成して、以下2つのファイルを作成します。 + +- main.ts +- data.json + +```ts main.ts +import { Application, Router } from "@oak/oak"; +import { oakCors } from "@tajpouria/cors"; +import data from "./data.json" with { type: "json" }; + +const router = new Router(); + +router + .get("/", (context) => { + context.response.body = "Welcome to dinosaur API!"; + }) + .get("/dinosaurs", (context) => { + context.response.body = data; + }) + .get("/dinosaurs/:dinosaur", (context) => { + if (!context?.params?.dinosaur) { + context.response.body = "No dinosaur name provided."; + } + + const dinosaur = data.find((item) => + item.name.toLowerCase() === context.params.dinosaur.toLowerCase() + ); + + context.response.body = dinosaur ? dinosaur : "No dinosaur found."; + }); + +const app = new Application(); +app.use(oakCors()); +app.use(router.routes()); +app.use(router.allowedMethods()); + +await app.listen({ port: 8000 }); +``` + +サンプルの量が多かったので一部抜粋しますが、以下のようなjsonファイルを作成します。 + +```json data.json +[ + { + "name": "Aardonyx", + "description": "An early stage in the evolution of sauropods." + }, + { + "name": "Abelisaurus", + "description": "\"Abel's lizard\" has been reconstructed from a single skull." + } +] +``` + +これで恐竜のデータを取得するバックエンドapiの構築は完了です。以下のコマンドでサーバーを起動することができます。 + +```sh +deno run --allow-env --allow-net api/main.ts +``` + +起動できると、例えば`localhost:8000/dinosaurs/Aardonyx`へのアクセスで以下のようなレスポンスが帰ってくることが確認できます。 + +```js +{name: "Aardonyx", description: "An early stage in the evolution of sauropods."} +``` + +ここまでで無事にバックエンドサーバーの構築・起動までチュートリアルで確認できました。 + +--- + +先程実行した、起動コマンドについてですが起動時に与えている引数がDenoの大きな特徴の一つです。 + +デフォルトではホストマシンまたはネットワークへのアクセス権限は最小限にする思想ですね。 + +これらの明示的に与えられるPermissionについては以下のようなものがあります。 + +- --allow-read : ファイルの読み取りアクセス +- --allow-write : ファイルシステムの書き込みアクセス +- --allow-net : ネットワークアクセス +- --allow-env : 環境変数へのアクセス +- --allow-run : サブプロセスの実行 + +```sh +deno run --allow-env --allow-net api/main.ts +``` + +例えば上記のコマンドでは、`--allow-env`では環境変数へのアクセス権、`--allow-net`ではネットワークアクセスの許可を`api/main.ts`に与えるように指定しています。 + +試しにネットワークアクセスを許可しないまま起動しようとすると、以下のようにエラーが発生します。 + +```sh +❯ deno run api/main.ts +Permission flags have likely been incorrectly set after the script argument. +To grant permissions, set them before the script argument. For example: + deno run --allow-read=. main.js +❌ Denied net access to "0.0.0.0:8000". +error: Uncaught (in promise) NotCapable: Requires net access to "0.0.0.0:8000", run again with the --allow-net flag + this.#httpServer = serve?.({ + ^ + at listen (ext:deno_net/01_net.js:504:35) + at Object.serve (ext:deno_http/00_serve.ts:555:16) + at Object.start (https://jsr.io/@oak/oak/17.1.3/http_server_native.ts:84:28) + at Module.invokeCallbackFunction (ext:deno_webidl/00_webidl.js:1105:16) + at startAlgorithm (ext:deno_web/06_streams.js:3661:14) + at setUpReadableStreamDefaultController (ext:deno_web/06_streams.js:3625:23) + at setUpReadableStreamDefaultControllerFromUnderlyingSource (ext:deno_web/06_streams.js:3691:3) + at new ReadableStream (ext:deno_web/06_streams.js:5160:7) + at Server.listen (https://jsr.io/@oak/oak/17.1.3/http_server_native.ts:82:20) + at Application.listen (https://jsr.io/@oak/oak/17.1.3/application.ts:840:35) + +``` + +別の例としては、先程の実装ではimportでdata.jsonを読み込んでいたため、`--allow-read`の権限許可は必要としていませんでした。 + +以下のように読み込み部分を変更してみましょう。 + +```diff +- import data from "./data.json" with { type: "json" }; + ++ let data: Array<{ name: string; description: string }> = []; + ++ // ファイルシステムからJSONデータを読み込む関数 ++ async function loadData() { ++ const rawData = await Deno.readTextFile("./api/data.json"); ++ data = JSON.parse(rawData); ++ } + ++ // サーバー起動時にデータをロード ++ await loadData(); +``` + +再度同様にサーバー起動をしてみようとしましたが、以下のエラーにより弾かれてしまいました。 + +```sh +❯ deno run --allow-env --allow-net api/main.ts +❌ Denied read access to "/home/penryu/dev/projects/deno-vue/vite-project/api/data.json". +error: Uncaught (in promise) NotCapable: Requires read access to "./api/data.json", run again with the --allow-read flag + const rawData = await Deno.readTextFile("./api/data.json"); + ^ + at Object.readTextFile (ext:deno_fs/30_fs.js:779:24) + at loadData (file:///home/penryu/dev/projects/deno-vue/vite-project/api/main.ts:8:32) + at file:///home/penryu/dev/projects/deno-vue/vite-project/api/main.ts:13:7 + +``` + +このように、デフォルトで安全性が確保されているのは嬉しいポイントですね。 + +### 3. フロントエンドの構築 + +Vue Routerモジュールを以下のように追加します。 + +```sh +deno add npm:vue-router +``` + +チュートリアルでは、このあといくつかのファイルを実装することになります。対象としては、以下の7ファイルです。 + +```sh +├── src +│   ├── App.vue +│   ├── components +│   │   ├── Dinosaurs.vue +│   │   ├── Dinosaur.vue +│   │   └── HomePage.vue +│   ├── main.ts +│   ├── router +│   │   └── index.ts +│   ├── type.d.ts +``` + +実際の内容の紹介です。 main.tsとindex.tsでは以下のようにルーターを実装しています。 + +```ts main.ts +import { createApp } from "vue"; +import router from "./router/index.ts"; + +import "./style.css"; +import App from "./App.vue"; + +createApp(App) + .use(router) + .mount("#app"); +``` + +```ts router/index.ts +import { createRouter, createWebHistory } from "vue-router"; +import HomePage from "../components/HomePage.vue"; +import Dinosaur from "../components/Dinosaur.vue"; + +export default createRouter({ + history: createWebHistory("/"), + routes: [ + { + path: "/", + name: "Home", + component: HomePage, + }, + { + path: "/:dinosaur", + name: "Dinosaur", + component: Dinosaur, + props: true, + }, + ], +}); +``` + +App.vueの内容について、以下のようにします。 + +```html + +``` + +あとはコンポーネントたちを以下のように実装します。 + +```html HomePage.vue + + +``` + +```html Dinosaur.vue + + + +``` + +```html Dinosaurs.vue + + + +``` + +ここまででフロントエンド側の実装も完了です。以下のコマンドにて、フロントエンド側のサーバーが起動できます。 + +```sh +deno run -A npm:vite +``` + +image.png + +これでチュートリアルは完了です! + +--- + +今回のチュートリアルでは、Vueの構築についてはNode.jsおよびnpmの下位互換性を活用する形で行われていました。 + +npmを用いない形で利用できる候補として、以下のリポジトリでは「vno」としてライブラリ開発がされていましたが、3年前ほどでコミットが途絶えているようです。。 + +https://github.com/open-source-labs/vno + +## まとめ + +この記事では、Deno v2の簡単な紹介と、Vue.jsアプリの構築のチュートリアルを触ってみました。 + +TypeScriptのネイティブサポートや、セキュリティの概念など優れた特徴があり、Node.jsおよびnpmとの下位互換性についてもサポートされるものが増えてきています。 + +そのため、TypeScriptを使用したアプリケーション開発には優れた体験ができそうだと感じました。また、フロントエンドについても本体機能・ライブラリの様々な開発が進んでいるようです! + +チュートリアル以上のアプリ構築についても、試してみたいと感じた2024年の冬でした。 + +## 参考文献 + +- https://www.youtube.com/watch?v=M3BM9TB-8yA +- https://docs.deno.com/ +- https://github.com/open-source-labs/vno diff --git a/source/images/20241129a/image.png b/source/images/20241129a/image.png new file mode 100644 index 00000000000..272aecf2678 Binary files /dev/null and b/source/images/20241129a/image.png differ diff --git a/source/images/20241129a/image_2.png b/source/images/20241129a/image_2.png new file mode 100644 index 00000000000..e7610d19f34 Binary files /dev/null and b/source/images/20241129a/image_2.png differ diff --git a/source/images/20241129a/image_3.png b/source/images/20241129a/image_3.png new file mode 100644 index 00000000000..df0ec5aa6b0 Binary files /dev/null and b/source/images/20241129a/image_3.png differ diff --git a/source/images/20241129a/thumbnail.png b/source/images/20241129a/thumbnail.png index 54c9f50f133..8698ac665f9 100644 Binary files a/source/images/20241129a/thumbnail.png and b/source/images/20241129a/thumbnail.png differ