About Nue
鵺(ぬえ)。
猿の顔、タヌキの体、蛇の尻尾、虎の脚を持つ妖怪です。
日本人ならNueと聞いた場合、思い浮かべるのは↑かと思うのですが、
これは先日リリースされた、フロントエンド開発ツールセットです。
*「Nue」はドイツ語で、「新しい」という意味だそうな
*「Nue」はドイツ語の「neu」に由来しており、英語で「新しい」という意味だそうな
開発者はTero Piirainenという方で、
現在彼1人で開発しています。
ここを見ると、「Webの開発方法が変わるかも」と
かなりすごいことが書いてありますが、どんなものなのか見ていきましょう。
Nueとはなんなのか?
公式を見ると、↓のようなことを言っています。
- React、Vue、Next.js、Vite、Astroの代わりになる
- NueはSPAMPAの両方に対するサポートを備えたWebアプリケーションビルダーである
- Nueは最終的に完全なフロントエンド開発ツールセットとなる。目標は2024年3月
最終的にここにあるライブラリ・ツールがすべて実装されて
Nue Toolsの完成となります。
これが完成したとき、Nueは完全なフロントエンド開発ツールセットとなり、
Vite、Next.js、Astroなどをリプレイスする手段となるとのこと。
これがNueの最終目標であり、2024年3月を目処に開発しているようです。
Nue JS
今回リリースされたライブラリはNue JSで、
これはUIを構築するためのJavaScriptライブラリです。
React、Vue、Svelteの代替として使うことができ、
HTML、CSS、JavaScriptの基本がわかれば使えます。
その他Nue
Nue Js以外の、Nue css、SPAを構築するためのNue MVC、
UI開発のための再利用可能なコンポーネントであるNue UI、
MDX、MDCの代わりとなるNuemark、
NextやNuxt、Astroの代替となるNuekitなどについては
今後順次リリースしていく予定のようです。
Bun + Nue
現状、NueはNodeかbunで動作しますが、Bunがおすすめみたいです。
今回はBunをつかってNueのサンプルをうごかしてみましょう。
Environment
今回試した環境は以下のとおりです。
- MacBook Pro (13-inch, M1, 2020)
- OS : MacOS 13.5.2
- Bun : 1.0.2
Try Nue.js
まずはbunをインストールまたはupgradeしましょう。
% curl -fsSL https://bun.sh/install | bash
or
% bun upgrade
・・・
% bun --version
1.0.2
Nueを動かす方法は2つ紹介されています。
簡単なのはリポジトリをcloneする方法で、↓のようにすればすぐ動作確認できます。
# clone the repository
git clone https://github.com/nuejs/create-nue.git
# cd to your newly created app
cd create-nue
# install dependencies
npm install
# Build demo site and start a HTTP server
npm run start
# Open the demo on the browser
open "http://localhost:8080"
今回は↑のコードを参考に、最小のサンプルをつくって確認してみましょう。
適当なディレクトリをつくってpackage.jsonを作成します。
% mkdir hello-nue && cd hello-nue
% mkdir www
% npm init
% npm install nuejs-core js-yaml --save
package.jsonの中身はこんな感じです。
{
"name": "hello-nue",
"version": "1.0.0",
"type": "module",
"scripts": {
"serve": "cd www && ../scripts/server.js",
"compile": "./scripts/compile.js",
"render": "./scripts/render.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"js-yaml": "^4.1.0",
"nuejs-core": "^0.1.1"
}
}
ES Module方式にするのと、
コンパイル・レンダリング・サーバ起動用のスクリプトを記述します。
Componentの作成
Nueコンポーネントは再利用可能なUIパーツです。
src/foo.nueファイルを作成しましょう。
<div @name="foo-div" class="{ type }">
<img src="{ img }" height="{img_height}" width="{img_width}">
<aside>
<h3>{ title }</h3>
<p>{ desc }</p>
</aside>
</div>
コンポーネントは、@nameで指定された名前を持つHTMLのフラグメントです。
ファイルには任意の拡張子が使えますが「.nue」拡張子が推奨とのこと。
次に.nueをコンパイルするファイル(scripts/compile.js)を作成します。
このファイルはnuejs-coreモジュールを使ってさきほどのnueファイルをjsにコンパイルして
公開用ディレクトリに出力します。
#!/usr/bin/env node
import { compileFile } from 'nuejs-core'
const target_js = 'www/foo.js'
// compile nue source code for browser execution
await compileFile('src/foo.nue', target_js)
console.info('compiled', target_js)
次はレンダリング用ファイルを作成します。
サーバーコンポーネントはrenderor/renderFile関数でレンダリングし、
reactiveコンポーネントはmountをつかってレンダリングするとのことです。
※参考:https://nuejs.org/docs/nuejs/component-basics.html
#!/usr/bin/env node
import { parse, render } from 'nuejs-core'
import { promises as fs } from 'node:fs'
// read() function for reading assets
const read = async (name, dir='src') => await fs.readFile(dir + '/' + name, 'utf-8')
// define a component
const component = await read('foo.nue');
// render the component with some data
const html = '<!DOCTYPE html>\n\n' + render(component, {
title: '鵺サンプル',
desc: 'Hello 鵺!',
img: '<鵺画像のパス>',
img_width:"20%",
img_height:"20%",
type: 'banner',
});
// write index.html
await fs.writeFile('./www/index.html', html)
console.log('wrote', 'www/index.html')
ここまでできたらcompileとrenderをbunで実行します。
% bun compile
compiled www/foo.js
% bun render
$ ./scripts/render.js
wrote www/index.html
これでfoo.jsとindex.htmlが生成されました。
最後にscripts/serve.jsを作成して、
htmlサーバを起動します。 (create-nueほぼそのまま)
#!/usr/bin/env node
// a super minimal web server to serve files on the current working directory
import { join, extname } from 'node:path'
import http from 'node:http'
import fs from 'node:fs'
const TYPES = {
html: 'text/html; charset=UTF-8',
js: 'application/javascript',
svg: 'image/svg+xml',
ico: 'image/x-icon',
png: 'image/png',
jpg: 'image/jpg',
css: 'text/css'
}
const PORT = 8080
http.createServer(async (req, res) => {
let { url } = req
// favicon.icoのリクエストを無効化。
// これがないとエラーになってた
if (url === '/favicon.ico') {
res.writeHead(204, { 'Content-Type': 'image/x-icon' });
res.end();
return;
}
if (url.endsWith('/')) url += 'index.html'
const path = join('.', url)
const ext = extname(path).slice(1)
const head = { 'Content-Type': TYPES[ext] }
try {
res.writeHead(200, head)
fs.createReadStream(path).pipe(res)
} catch(e) {
res.writeHead(404, head)
res.end('')
}
}).listen(PORT)
console.log(process.isBun ? 'Bun' : 'Node', `HTTP server at http://localhost:${PORT}/`)
bun serveでHTTPサーバを起動してブラウザでアクセスしてみましょう。
% bun serve
$ cd www && ../scripts/server.js
Node HTTP server at http://localhost:8080/
nueで作成したHTMLが表示されました。
Summary
今回はとりあえずNue Jsでシンプルなコンポーネントを
ビルドして表示してみました。
いちいち手動で面倒だったのですが、今後このあたりも洗練されていくかとおもいます。