JSXをReactなしで使ってみた
いろいろ実験したレポジトリはこちら
シンプルなページ
できたバンドルはこちら
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>My App</title>
<script defer="defer" src="bundle.js"></script>
</head>
<body>
<h1>Hello World</h1>
<div id="root"></div>
</body>
</html>
bundle.js
(()=>{
"use strict";
const e = (t,n)=>{
Array.isArray(n) ? n.forEach((n=>e(t, n))) : ((e,t)=>{
e.appendChild(t?.nodeType ? t : document.createTextNode(t))
}
)(t, n)
}
, t = (t,n)=>{
const {children: r} = n;
if ("function" == typeof t)
return t(n, r);
const o = document.createElement(t);
return Object.entries(n || {}).forEach((([e,t])=>{
e.startsWith("on") && e.toLowerCase()in window ? o.addEventListener(e.toLowerCase().substr(2), t) : o.setAttribute(e, t)
}
)),
e(o, r),
o
}
, n = ({children: e, onClick: n})=>t("button", {
onClick: n,
children: e
});
document.getElementById("root").appendChild(t((()=>t("div", {
children: t(n, {
onClick: ()=>alert(1),
children: "Click 1"
})
})), {}))
}
)();
2つのファイルあわせて801バイトで収まりました!軽いですね。
GoogleのPageSpeed Insights だと100点です!
jsxのランタイムは参考URLからお借りしました。
jsx-runtime.js
const add = (parent, child) => {
parent.appendChild(child?.nodeType ? child : document.createTextNode(child));
};
const appendChild = (parent, child) => {
if (Array.isArray(child)) {
child.forEach((nestedChild) => appendChild(parent, nestedChild));
} else {
add(parent, child);
}
};
export const jsx = (tag, props) => {
const { children } = props;
if (typeof tag === "function") return tag(props, children);
const element = document.createElement(tag);
Object.entries(props || {}).forEach(([name, value]) => {
if (name.startsWith("on") && name.toLowerCase() in window) {
element.addEventListener(name.toLowerCase().substr(2), value);
} else {
element.setAttribute(name, value);
}
});
appendChild(element, children);
return element;
};
export const jsxs = jsx;
ちょっと複雑なページ
200件のTODOリストを表示するだけのページ。
pagespeedも問題なさそう。
import { Button } from "./Button";
import { todos } from "./data";
const App = () => {
// 代入だと動かない。fetchしたものを代入するにはdocumentAPI使うしかない?
// let todo = [];
// fetch("https://jsonplaceholder.typicode.com/todos")
// .then(response => response.json())
// .then(json => {
// console.log(json);
// todo = json;
// })
return (
<div>
<Button onClick={() => alert(1)}>Click 1</Button>
{todos.length > 0 && <table>
{todos.map((item) => {
return <tr><td>{item.id}</td><td>{item.title}</td></tr>;
})}
</table>}
</div>
)
}
const rootElement = document.getElementById("root");
rootElement.appendChild(<App />);
export default App;
ただ fetch とかして見て動かす方法がない・・・?
既存データだけをいれる静的なページなら問題ないかも。
useState()とか代入する手段がわからなかった。
React
$ npx create-react-app react-app
Reactの一番シンプルな構成で作ったページ
buildフォルダのサイズは518kbでしたがパフォーマンスは満点でした。
ここからいろいろ追加したときにパフォーマンスが悪化するんですね。。。すべては人の手によるもの。
結論
518キロバイトが801バイトになるのでためしてみてもよいかも・・・?
setStateとかが使えなくなるので本当に静的なページなら問題ないと思うが、reactとreact-staticとかで吐き出すとかでも軽いと思うし、なんならpreactとか使ってもよいかもしれない。
参考資料
import { Page } from '@nakedjsx/core/page'
上記でSimpleにかけそうと思ったが結局トランスパイルやバンドル周り云々をしないとQiita用にソースコードのアップロード(GitHub Pages)できなさそうだったので参考だけに使った
babel/webpack周りはこの資料を参考にした。
react/jsx-runtimeの周りの理解は旧公式ドキュメントを参考にした。
素のReactにWebpack導入するやり方の参考