メインコンテンツまでスキップ

環境構築と単一ファイルコンポーネント — 開発を始める最小構成

Vue のプロジェクトを作るコマンドは 1 行です。ただし対話でいくつか質問され、選んだ内容で生成物が変わります。あとから足せるものと、最初に決めておくと楽なものがあるので、何を聞かれるのかを先に知っておくと迷いません。

この章では公式の scaffold である create-vue でプロジェクトを作り、生成されたファイルが何のためにあるかを読みます。そのあと、Vue の開発単位である単一ファイルコンポーネントの構造を見ます。

この章で学ぶこと

  • create-vue の対話で何を聞かれ、どれを選ぶべきかを判断できる
  • 生成されたディレクトリと設定ファイルの役割を説明できる
  • 単一ファイルコンポーネントの 3 つのブロックの使い分けが分かる

プロジェクトを作る

Vue 公式の Quick Start が案内しているコマンドはこれです1

npm create vue@latest

Node.js の要件は ^22.18.0 || >=24.12.0 です。22.18.0 以上の 22 系か、24.12.0 以上が必要で、23 系は対象外です。奇数系を使っている場合は切り替えます。

Vite のテンプレート (npm create vite@latestvue-ts) でも Vue プロジェクトは作れますが、生成されるのは最小構成です。create-vue のほうがルーターや状態管理、型検査、リンターの設定まで組み合わせてくれるので、実務で使う構成に近い形から始められます。

対話で聞かれること

質問はこの順に出ます。

#質問内容
1Project name (target directory)作成先のディレクトリ名。コマンドの引数で渡した場合は出ません
2Package namepackage.jsonname。ディレクトリ名がそのまま使える場合は出ません
3Use TypeScript?TypeScript を使うか。機能選択より先に聞かれます
4Select features to include in your project機能の複数選択。下の表を参照
5Select an End-to-End testing framework4 で End-to-End Testing を選んだ場合のみ。Playwright か Cypress
6Select experimental features to include in your project実験的な機能の複数選択。何も選ばずに進められます
7Skip all example code and start with a blank Vue project?サンプルコードを消して空の状態から始めるか

4 番の機能選択で選べるのは 7 つです。

選択肢内容本ガイドでの扱い
JSX Supportテンプレートの代わりに JSX を書けるようにする扱いません
Router (SPA development)Vue Router を入れる15 章16 章で扱います
Pinia (state management)状態管理ライブラリを入れる17 章で扱います
Vitest (unit testing)単体テストの環境を入れる扱いません
End-to-End TestingPlaywright か Cypress を入れる扱いません
Linter (error prevention)oxlint と ESLint を入れる扱いません
Prettier (code formatting)コード整形を入れる扱いません

6 番の実験的な機能では Vue 3.6 (Release Candidate) と、Prettier を Oxfmt に置き換える選択肢が出ます。本ガイドは安定版の 3.5 を基準にしているので、どちらも選びません。対話で作る場合はこの質問が必ず出るので、必要なければ何も選ばずに進めます。

学習用に最小の構成で始めるなら、TypeScript だけを有効にして他を外すのが分かりやすいです。本ガイドは Router と Pinia も後半で扱うので、通しで読むなら最初から入れておくと差分が少なくて済みます。

対話を飛ばす

フラグで指定すると対話が出ません。CI や、同じ構成を作り直すときに使えます。

# TypeScript + Router + Pinia の構成を対話なしで作る
npm create vue@latest my-app -- --ts --router --pinia

--default は追加機能なしの構成になりますが、TypeScript は有効になります--bare はサンプルコードを省く指定で機能フラグではないため、単独で渡すと対話が始まります。

依存をインストールして起動する

cd my-app
npm install
npm run dev

生成されたものを読む

TypeScript、Router、Pinia の 3 つを選んだ場合の構成です。

my-app/
├── index.html エントリの HTML。ここに #app がある
├── vite.config.ts Vite の設定。@ → ./src の別名もここ
├── env.d.ts Vite が注入する型の宣言
├── tsconfig.json 分割した設定を参照するだけの親
├── tsconfig.app.json ブラウザで動くコード向け
├── tsconfig.node.json 設定ファイルなど Node で動くコード向け
├── public/ 変換せずそのまま配信されるファイル
└── src/
├── main.ts アプリを組み立てて #app にマウントする
├── App.vue 最上位のコンポーネント
├── assets/ CSS や画像。ビルド時に変換される
├── components/ 再利用するコンポーネント
├── views/ ルートに対応する画面単位のコンポーネント
├── router/ ルート定義
└── stores/ Pinia のストア

選んだ機能で増減します。Linter を選ぶと eslint.config.ts.oxlintrc.json が、Prettier を選ぶと .prettierrc.json が加わります。Vitest を選ぶと vitest.config.tstsconfig.vitest.json が増え、親から参照される設定が 3 つになります。

composables/ は生成されません。ロジックを切り出すディレクトリなので、必要になった時点で自分で作ります (8 章で扱います)。

エントリポイント

src/main.ts がアプリを組み立てる場所です。

src/main.ts
import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')

createApp() でアプリのインスタンスを作り、use() でプラグインを登録し、mount()index.html#app に差し込みます。ルーターや状態管理を足すときはここに use() を追加します。

tsconfig が分かれている理由

tsconfig.json の中身は参照だけで、実際の設定は分割されています。ブラウザで動くコードと、vite.config.ts のように Node で動くコードでは、使える API と型が違うためです。混ぜると window を参照できない場所で参照が通ってしまったり、逆に Node の API が使えないことになります。

アプリ側の tsconfig.app.json には noUncheckedIndexedAccess が入っています。添字アクセスの結果に undefined が混ざる前提で型が付くので、const first = items[0] の型は string | undefined になります。読み出すだけならエラーになりませんが、items[0].length のように undefined を許さない位置で使うと止まります。安全側に寄せた設定です。

型検査は npm run type-check で走ります。中身は vue-tsc --build で、分割された設定をまとめて処理します。素の tsc ではなく vue-tsc を使うのは、.vue ファイルの中身を解析する必要があるからです。

用意されるコマンド

コマンド内容
npm run dev開発サーバーを起動する
npm run build型検査とビルドをまとめて実行し dist/ に出力する
npm run build-only型検査を飛ばしてビルドだけ実行する
npm run previewビルド結果をローカルで配信して確認する
npm run type-check型検査だけを実行する
npm run test:unitVitest で単体テストを実行する (Vitest を選んだ場合)
npm run lintoxlint と ESLint を順に実行する (Linter を選んだ場合)
npm run formatPrettier で整形する (Prettier を選んだ場合)

Linter を選ぶと lint が oxlint と ESLint を順に呼ぶ 2 段構成になります。ESLint 側の設定には oxlint と重複する規則を無効化する仕組みが入っているので、同じ指摘が二重に出ることはありません。

単一ファイルコンポーネントの 3 ブロック

Vue のコンポーネントは .vue という拡張子の 1 ファイルで、テンプレートとロジックとスタイルを同じ場所に書きます。

src/components/Greeting.vue
<script setup lang="ts">
import { ref } from 'vue'

const name = ref('Vue')
</script>

<template>
<p class="greeting">こんにちは、{{ name }}</p>
</template>

<style scoped>
.greeting {
font-weight: bold;
}
</style>

3 つのブロックの役割はこうなっています。

ブロック役割
<script setup lang="ts">このコンポーネントのロジック。ここで宣言した変数と関数はテンプレートから使えます
<template>画面の構造。HTML に Vue の記法を足したものを書きます
<style scoped>このコンポーネント用のスタイル

順序は決まっていません。create-vue の生成物は <script setup> を先に書いているので、本ガイドもそれに合わせます。

script setup が省いているもの

<script setup> を付けると、トップレベルで宣言したものがテンプレートから使えます。return を書く必要はありません。インポートしたコンポーネントもそのまま使えるので、登録の記述も要りません。

登録なしで子コンポーネントを使える
<script setup lang="ts">
import Greeting from './Greeting.vue'
</script>

<template>
<Greeting />
</template>

scoped が何をするか

<style scoped> を付けると、Vue がコンポーネントごとに固有の属性を要素へ振り、セレクタにその属性を足します。これでクラス名の衝突を自分で管理しなくて済むので、コンポーネントごとに素直な名前を付けられます。

範囲は「そのコンポーネントの中だけ」ではありません。単一のルート要素を返す子コンポーネントには、そのルート要素に親の属性も付きます。 子自身が <style scoped> を持つなら両方の属性が付き、持たないなら親の属性だけです。どちらでも親のセレクタはそのルート要素に当たります。一方、子が複数のルート要素を返す場合は、どの要素にも親の属性は付きません。

単一ルートの子に親のスタイルが届くのは、レイアウト上の余白を親が決めたい場面で役に立ちます。一方で意図せず子の見た目を変えてしまうこともあるので、この経路があることは頭に入れておきます。

子の内部 (ルートより深い要素) にスタイルを当てたい場合は :deep() で貫通させます。ただし子の内部構造に依存する書き方になるので、使いすぎると変更に弱くなります。

Vue CLI をどう扱うか

Vue には webpack ベースの Vue CLI という古いツールチェーンがありますが、公式は メンテナンスモードだと位置づけています。新規プロジェクトは Vite で始めることを推奨し、webpack 固有の機能が必要な場合だけ例外としています2

すでに Vue CLI で動いているプロジェクトが壊れるわけではありません。ただし新しく作るなら create-vue を使います。

エディタの設定

create-vue は .vscode/extensions.json に推奨拡張を書き込みます。中心になるのは Vue - Official (識別子は Vue.volar) で、.vue ファイルの構文解析と型情報を担当します。以前は Volar という名前でしたが、現在は Vue 公式の拡張として提供されています。

まとめ

  • プロジェクトは npm create vue@latest で作ります。Vite のテンプレートでも動きますが、公式の scaffold は create-vue です
  • Node は 22.18.0 以上の 22 系か 24.12.0 以上が必要で、23 系は対象外です
  • 対話は TypeScript を使うかが先で、機能の複数選択はそのあとです。実験的な機能の質問も対話では必ず出るので、Vue 3.6 の候補版を使わないなら何も選ばずに進めます
  • フラグを渡すと対話を飛ばせます。ただし --default でも TypeScript は有効になり、--bare は単独では対話を止められません
  • tsconfig.json が分かれているのは、ブラウザ向けと Node 向けで使える API が違うためです。アプリ側には noUncheckedIndexedAccess が入っています
  • 型検査は vue-tsc が担います。.vue の中身を解析する必要があるので、素の tsc では足りません
  • 単一ファイルコンポーネントは <script setup><template><style scoped> の 3 ブロックです。<script setup> を使えば return とコンポーネント登録が要りません
  • scoped の範囲はコンポーネントの内部だけではありません。単一ルートの子には、そのルート要素に親の属性も付きます (複数ルートの子には付きません)
  • Vue CLI は廃止ではなくメンテナンスモードです。新規プロジェクトでは使いません
関連リファレンス

次に読む

Footnotes

  1. 出典: Quick Start — Creating a Vue Application。Node の要件も同ページに記載があります。

  2. 出典: Tooling — Project Scaffolding。Vue CLI を "currently in maintenance mode" と位置づけています。そのうえで "It is recommended to start new projects with Vite" と書き、例外を "unless specific webpack-only features are required" としています。