Windows環境にあれこれ設定するのも嫌・・・
というかそもそもWindowsで開発するのが嫌なので、
うちに常時稼働しているRaspberryPiで開発することにする。
(別にWSLでもいいんだけど)
うちのRaspberryPiにはUbuntuが入っているので、VSCodeからリモートでつないでターミナルを起動
■全部すっ飛ばしてNuxtを使ってみるまずはnpmを入れる
sudo apt install npm
終わったら次はプロジェクトを作成するので、適当なフォルダをつくる
mkdir nuxt_test
cd nuxt_test
続いて、プロジェクトを作成する
npx nuxi@latest init timer
プロジェクト名はtimerにしてみた
(タイマーを設定して動かすだけのページでも作ろうかと思ってみた)
しばらくするとプロジェクトができるので、ターミナルから起動してみる
npm run dev -- -o
しばらく待つと、http://localhost:3000で起動される
ラズパイのIPじゃなくてlocalhostなんだけど、なぜか動く(なんで?)
nuxtのGetting Startedのページを読み進めると、Nuxtのフォルダ構造が出てきた
pagesフォルダを作るのがいいらしい
timer/pages/index.vueを作る
中身はコレ
<template>
<h1>Index page</h1>
</template>
つづいて、pagesを使う時にはapp.vueを書き換えるらしい
timer/app.vueはすでに存在しているので、中身を変える
<template>
<div>
<!-- Markup shared across all pages, ex: NavBar -->
<NuxtPage />
</div>
</template>
NuxtPageはpagesフォルダを使う設定らしい
これによって、pages/index.vueがhtmlとして解釈されて表示されるようになる。
ブラウザでIndex pageと表示されていればオッケー
続いて、今動いているターミナルを一度Ctrl+Cで止め、primeVueを入れる
npm install primevue
npm install --save-dev @primevue/nuxt-module
primeVueのテーマを入れる
npm install @primevue/themes
アイコンも入れとく
npm install primeicons
つづいて、nuxt.config.tsを編集
// https://nuxt.com/docs/api/configuration/nuxt-config
import Aura from '@primevue/themes/aura';
export default defineNuxtConfig({
compatibilityDate: '2024-04-03',
devtools: { enabled: true },
modules: [
'@primevue/nuxt-module'
],
primevue: {
options: {
ripple: true,
inputVariant: 'filled',
theme: {
preset: Aura,
options: {
prefix: 'p',
darkModeSelector: 'system',
cssLayer: false
}
}
}
}
})
ついでにpages/index.vueへボタンを追加してみる
<template>
<h1>Index page</h1>
<Button label="Check" icon="pi pi-check" />
</template>
ターミナルからnpm run devsして、
こんな感じで表示されればオッケー
ん?なんかCheckボタン形偏ってない?わざと?
まぁ気を取り直して
pages/index.vueを編集
IftaLabelというのを使ってみる
<script lang="ts">
import IftaLabel from 'primevue/iftalabel';
</script>
<template>
<h1>Index page</h1>
<Button label="Check" icon="pi pi-check" />
<IftaLabel>
<InputText id="username" v-model="value" />
<label for="username">Username</label>
</IftaLabel>
</template>
動かしっぱなしでもホットスワップ?
ページが変わってくれるのは便利
にしてもこの緑、気に入らない・・・
なんとかならんのかと説明を読んでいたら、Noirモードの定義をしているところがあった。
テーマのAuraをベースにして、あれこれカスタマイズしてNoirを設定している
nuxt.config.tsに結構書き込むことになるけどこんな感じ
// https://nuxt.com/docs/api/configuration/nuxt-config
import PrimeVue from 'primevue/config';
import { definePreset } from '@primevue/themes';
import Aura from '@primevue/themes/aura';
const Noir = definePreset(Aura, {
semantic: {
primary: {
50: '{zinc.50}',
100: '{zinc.100}',
200: '{zinc.200}',
300: '{zinc.300}',
400: '{zinc.400}',
500: '{zinc.500}',
600: '{zinc.600}',
700: '{zinc.700}',
800: '{zinc.800}',
900: '{zinc.900}',
950: '{zinc.950}'
},
colorScheme: {
light: {
primary: {
color: '{zinc.950}',
inverseColor: '#ffffff',
hoverColor: '{zinc.900}',
activeColor: '{zinc.800}'
},
highlight: {
background: '{zinc.950}',
focusBackground: '{zinc.700}',
color: '#ffffff',
focusColor: '#ffffff'
}
},
dark: {
primary: {
color: '{zinc.50}',
inverseColor: '{zinc.950}',
hoverColor: '{zinc.100}',
activeColor: '{zinc.200}'
},
highlight: {
background: 'rgba(250, 250, 250, .16)',
focusBackground: 'rgba(250, 250, 250, .24)',
color: 'rgba(255,255,255,.87)',
focusColor: 'rgba(255,255,255,.87)'
}
}
}
}
});
export default defineNuxtConfig({
compatibilityDate: '2024-04-03',
devtools: { enabled: true },
modules: [
'@primevue/nuxt-module'
],
primevue: {
autoImport: true,
options: {
ripple: true,
inputVariant: 'filled',
theme: {
preset: Noir,
options: {
darkModeSelector: false,
}
},
}
}
})
ついでに、アイコンも試そう
pages/index.vueを以下のように編集
<script setup lang="ts">
import Button from 'primevue/button';
import IftaLabel from 'primevue/iftalabel';
import 'primeicons/primeicons.css'
var value = "aiueo";
</script>
<template>
<h1>Index page</h1>
<Button label="Check" icon="pi pi-check" />
<div class="card flex flex-col items-center gap-4">
<div class="flex flex-wrap gap-4 justify-center">
<Button icon="pi pi-home" aria-label="Save" />
<Button label="Profile" icon="pi pi-user" />
<Button label="Save" icon="pi pi-check" iconPos="right" />
</div>
<div class="flex flex-wrap gap-4 justify-center">
<Button label="Search" icon="pi pi-search" iconPos="top" />
<Button label="Update" icon="pi pi-refresh" iconPos="bottom" />
</div>
</div>
<IftaLabel>
<InputText id="username" v-model="value" />
<label for="username">Username</label>
</IftaLabel>
</template>
こんな感じの表示になる
あーそうか、Checkの左にチェックマークがあったから偏ってたのか・・・
わたしがPrimeVueすごいなと思ったのは、カレンダーとか補完テキストボックスとか、
自分で作るのがめちゃくちゃ嫌なパーツがそろっていた点
■ちょっと落ち着いてどうなってるのか考察
そもそもVueとはJavaSctiptのフレームワークで、UIを構築できる。
UIはどうもブラウザUIだけではないらしい。
では、vueのアプリを作ってみる
mkdir ~/vue_test
cd vue_test
npm create vue@latest
途中プロジェクト名を聞かれるので、firstProjと入力しあとはただエンターを押した
cd firstProj
vscodeでfirstProjを開いてみた
ターミナルを開いて
npm install
npm run dev
ターミナルにlocalhost:xxxxと出るので、Ctrl+クリックで開くとページが表示される
次にターミナルからCtrl+cで止め、
npm run build
distフォルダにあれこれファイルが出来上がる
これを適当なWEBサーバにおいてあげればきっと動くのだろう
こうしてみるとNuxtの説明とあまり変わらないのだけど
さきほどの画面を見ると、vite+vue3と書かれている
どうやらviteというのがimport なんたら from なんたらという書き方をしているもので、
viteの作者とvueの作者は同じ人であり、viteはvueのビルドツール?という位置づけらしい
本気でわからなくなってきた
出来上がったものだけで判断すると・・・
nuxtはサーバサイドも開発できるもので、サーバはNitroを使っているという。
根っこはvueで、さらにビルドツールとしてviteがあるって感じなのだろうか
しかし、実際のビルドはnpmでやってるわけで・・・
npmの作者はたしか、npmの爆発問題を何とかするためにGoでdenoを作っていて、
途中からRustに移行してるんじゃなかったか?
コンポーネントベースで開発するというやり方を勉強するには、
まずはvueを勉強した方がよさそうなのだけど、
Nuxtでも結局同じようにコンポーネント単位で機能を実装していくことになるため、
vueのテストはここまでにして、
このままNuxtでコンポーネントの作り方を調査することとする。
■JSONのキーとバリュー再帰的にIftaLabelで表示したい
ではIftaLabelを簡単に表示するコンポーネントを作る
components/dispjs.vueを作成する
中身は
<template>
<IftaLabel>
<InputText id={{keyStr}} v-model=valStr readOnly=true />
<label for={{keyStr}}>{{ keyStr }}</label>
</IftaLabel>
</template>
<script setup>
const keyStr = defineModel('keyStr')
const valStr = defineModel('valStr')
</script>
続いて、index.vueにJSON入れたりあれこれしてdispjsを呼び出す
<script setup lang="ts">
import Button from 'primevue/button';
import IftaLabel from 'primevue/iftalabel';
import 'primeicons/primeicons.css'
import dispjs from '~/components/dispjs.vue';
const contentJSON = {
Subject: "件名",
date: "2024-10-19",
entry: [
{ resource: { resourceType: "目次" } },
{ resource: { resourceType: "はじめに" }, display: "本書を書くにあたり、多くの方にご助力いただきました事を感謝します。", author: ["Aさん", "Bさん", "Cさん"] },
{ resource: { resourceType: "一章" }, display: "まずは動かしてみましょう。" },
{ resource: { resourceType: "二章" }, display: "つぎは応用してみましょう。" },
{ resource: { resourceType: "あとがき" }, display: "最後までお読みいただきありがとうございます。" },
]
}
// function dispRecurse(d: any) {
// if (typeof d === 'object' && d !== null) {
// for (const key in d) {
// if (d.hasOwnProperty(key)) {
// console.log(`${key}:`)
// dispRecurse(d[key]);
// }
// }
// } else {
// console.log(`${d}`)
// }
// }
// dispRecurse(contentJSON);
</script>
<template>
<h1>Index page</h1>
<div v-for="(val, key) in contentJSON">
<dispjs :keyStr="key" :valStr="val" />
</div>
</template>
ちょっとJavaScriptでどうなるのか試したコードがあるけどコメントアウトしとく・・・
v-forでやってるんで再帰できてない・・・
画面表示はこんな感じ
entryの中は配列で入っているので、上のソースの再帰処理できちんと出せるんだけど、
これをコンポーネント化する方法がまだよくわかってない。
dispjsはラベルとインプットを一緒くたにしたIftaLabelというものを使って表示している。
この際、引数ですごく悩んだ。
export propsとかinterfaceとかやったけど、InputTextでv-model使っててうまくいかなかった。
そこでコンポーネントでもv-modelで引数を設定してやると、
双方向でバインドできるらしく、
Vue3.4以降はdefineModelを使うと楽よ?という情報は手に入ったのでやってみたらうまくいった。
■まとめ
正直全然わかってない・・・
コンポーネントとの引数のやりとりは、v-modelでひと段落したということになるのだろうか?それともまだまだ変わるのだろうか?
Componentで関数書いて再帰処理とかのやり方もまだわからない。
いや、分かった
しかし、よく考えたらそんなことせずとも
scriptで再帰してデータをフラット化して
DataTableやv-forで表示した方がVueとしては正当なやり方だと考えついた。
ので、再帰はできるけど敢えてやらない