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

# JSXをReactなしで使ってみた

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

https://github.com/ykhirao/qiita/tree/master/jsx-without-react


## シンプルなページ

できたバンドルはこちら

https://ykhirao.github.io/qiita/jsx-without-react/jsx/build/

![スクリーンショット 2023-12-11 15.26.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/47e5286e-5e2e-183d-7702-316cf9612e4d.png)

```html
<!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"
        })
    })), {}))
}
)();
```

２つのファイルあわせて801バイトで収まりました！軽いですね。

![スクリーンショット 2023-12-11 15.28.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/dc440862-de49-3b47-03c8-5f27d743fac4.png)


GoogleのPageSpeed Insights だと100点です！

https://pagespeed.web.dev/analysis/https-ykhirao-github-io-qiita-jsx-without-react-jsx-build/6h7bufbagp?hl=ja&form_factor=mobile

![スクリーンショット 2023-12-11 15.30.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/999697c9-aa42-0c44-d3f2-7acfaf8975d6.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;

```


## ちょっと複雑なページ

https://ykhirao.github.io/qiita/jsx-without-react/jsx-template/build

200件のTODOリストを表示するだけのページ。

![スクリーンショット 2023-12-11 15.32.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112929/668dcf4b-50de-d29d-46a2-d05bc2e8f9ef.png)


pagespeedも問題なさそう。

https://pagespeed.web.dev/analysis/https-ykhirao-github-io-qiita-jsx-without-react-jsx-template-build/3sbmdfnx9j?hl=ja&form_factor=mobile


```jsx
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


```terminal
$ npx create-react-app react-app
```

Reactの一番シンプルな構成で作ったページ

https://ykhirao.github.io/qiita/jsx-without-react/react/build/

buildフォルダのサイズは518kbでしたがパフォーマンスは満点でした。

https://pagespeed.web.dev/analysis/https-ykhirao-github-io-qiita-jsx-without-react-react-build/nctu9unbxg?hl=ja&form_factor=mobile

ここからいろいろ追加したときにパフォーマンスが悪化するんですね。。。すべては人の手によるもの。

## 結論

518キロバイトが801バイトになるのでためしてみてもよいかも・・・？
setStateとかが使えなくなるので本当に静的なページなら問題ないと思うが、reactとreact-staticとかで吐き出すとかでも軽いと思うし、なんならpreactとか使ってもよいかもしれない。



## 参考資料

```jsx
import { Page } from '@nakedjsx/core/page'
```

上記でSimpleにかけそうと思ったが結局トランスパイルやバンドル周り云々をしないとQiita用にソースコードのアップロード(GitHub Pages)できなさそうだったので参考だけに使った

https://chriscoyier.net/2023/08/07/jsx-without-react/

babel/webpack周りはこの資料を参考にした。

https://dev.to/devsmitra/how-to-create-the-app-using-jsx-without-react-k08

react/jsx-runtimeの周りの理解は旧公式ドキュメントを参考にした。

https://ja.legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html

素のReactにWebpack導入するやり方の参考

https://zenn.dev/spacemarket/articles/23e5401a074ccc