8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

シングルページアプリケーションのライブラリでマルチページしたい!

Last updated at Posted at 2023-04-27

A.viteならサクッとできた!

Q.なぜそんなことを...

A.使ったことないライブラリの試作やちょっとしたwebアプリを作るときに、都度環境構築とか面倒くさいなぁ~、lintなどなどは共有しつつ、別ページとして構築できないかなぁという思いがありました。また、試作等個人の小規模な利用を意図しているので、あまり壮大なモノレポの仕掛けを用意したくないという思いもありました。いつも使ってるvite + αで上記のような要望を実現したいというのが始まりです。

今回作ったもの

github上で公開しています。以下の「やってみよう」と、一部内容が相違しています。具体的には下記の説明ではlintなどを省いてます。

vite公式の説明を見てみましょう

以下、上記のページから引用です。

以下のようなソースコード構造があるとします:

├── package.json
├── vite.config.js
├── index.html
├── main.js
└── nested
  ├── index.html
  └── nested.js

開発時には、/nested/ に移動またはリンクするだけで、通常の静的ファイルサーバと同じように期待通りに動作します。
ビルド時には、エントリーポイントとして複数の .html ファイルを指定するだけです:

// vite.config.js
import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
 build: {
   rollupOptions: {
     input: {
       main: resolve(__dirname, 'index.html'),
       nested: resolve(__dirname, 'nested/index.html'),
     },
   },
 },
})

引用終わり。

つまり、開発時はaタグなどで遷移すれば良さそう、ビルドはconfigに手を入れたら良さそう、という感じですね。

やってみよう

とりあえず箱を作る

# npmなりyarnなりお好きなパッケージマネージャで
pnpm create vite

色々な質問をされるのでCUIと対話します。
著者はviteの扱うライブラリの中ではReactが一番書けるのでreact-tsで進めています。

中身を見てみる

.
├── index.html
├── node_modules
├── package.json
├── pnpm-lock.yaml
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   │   └── react.svg
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

テンプレートからReactのコードが出力されました。いつも見るやつですね。

早速2つのページに分けてみよう

src配下をいじっていきます

ディレクトリはこんな感じにしました。

src/
├── assets
│   └── react.svg
├── sample1
│   ├── index.css
│   ├── index.html
│   └── main.tsx
├── sample2
│   ├── index.css
│   ├── index.html
│   └── main.tsx
└── vite-env.d.ts

それぞれのディレクトリの中身は以下のようにしてみました。
index.htmlはmain.tsxを呼ぶだけ、main.tsxでは相互に対してaタグでリンクを張っています。cssは雑に背景色をつけています。

sample1

sample1/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <div>
      <div>sample1</div>
      <div>
        <a href="../sample2/index.html">to sample2</a>
      </div>
    </div>
  </React.StrictMode>,
);
sample1/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="./main.tsx"></script>
  </body>
</html>
sample1/index.css
html {
    background-color: cornsilk;
}

sample2

sample2/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <div>
      <div>sample2</div>
      <div>
        <a href="../sample1/index.html">to sample1</a>
      </div>
    </div>
  </React.StrictMode>,
);
sample2/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="./main.tsx"></script>
  </body>
</html>
sample2/index.css
html {
    background-color: aqua;
}

configはこんな風に

公式ページに書いてあったように、エントリーポイントを指定しています。

vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  root: 'src',
  build: {
    rollupOptions: {
      input: {
        sample1: resolve(__dirname, 'src/sample1/index.html'),
        sample2: resolve(__dirname, 'src/sample2/index.html'),
      },
    },
  },
});

起動してみよう

pnpm dev

http://localhost:5173/sample1/index.htmlを見ると
image.png

http://localhost:5173/sample2/index.htmlを見ると
image.png

リンクを押下すると相互に遷移できることも確認できます。

ビルドしてみよう

pnpm build
../dist/sample1/index.html            0.54 kB
../dist/sample2/index.html            0.54 kB
../dist/assets/index-780b1a3e.css     0.03 kB │ gzip:  0.05 kB
../dist/assets/index-040d043c.css     0.03 kB │ gzip:  0.05 kB
../dist/assets/sample2-cddba5cd.js    0.36 kB │ gzip:  0.22 kB
../dist/assets/sample1-4decda64.js    0.36 kB │ gzip:  0.22 kB
../dist/assets/client-2ce6d88c.js   142.82 kB │ gzip: 45.84 kB

html/js/cssいずれもページごとに生成されていることがわかります。マルチページになっていますね。
超デカいライブラリがいても、それを使わない(importしない)ページのjsのサイズなどは抑えられます。また、cssも1ページじゃないので競合しないですね。

何に使えるんでしょうか?

SPAやりつつ巨大な仕掛けは用意せずマルチページにしたいという需要を叶えてくれそう。それが仕事などで具体的にどのような要件と合致するかはイメージついてないですが。

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?