< Back

ReactなしでJSXを使う / JSX without React

JSXをReactなしで使ってみた

いろいろ実験したレポジトリはこちら

シンプルなページ

できたバンドルはこちら

スクリーンショット 2023-12-11 15.26.56.png

<!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バイトで収まりました!軽いですね。

スクリーンショット 2023-12-11 15.28.36.png

GoogleのPageSpeed Insights だと100点です!

スクリーンショット 2023-12-11 15.30.28.png

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リストを表示するだけのページ。

スクリーンショット 2023-12-11 15.32.42.png

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導入するやり方の参考