概要
Vue3でのVueファイル(SFCファイル)の書き方とトランスパイル方法(ビルドするための環境構築方法)を説明します。
- ※本記事では、以降「トランスパイル」を「build」と呼称します。
- ※ほぼ「自身向けのメモ」なので、悪しからず。
目的
本記事は、次を目的とします。
- Vue単一ファイルコンポーネント(
*.vue
, Vue SFC)を用いてVueを記述する際の、buildコマンドと記述方法を分かった気になること。 - Vue2系と比較してVue3では何が変わったのか?を分かった気になること。
- なお、Vue2とVue3の違いそのものは、すでに良質の解説記事があるので、そちらを参照ください(後述)。
- Vue2と同様の記法である「Options API」と、Vue3で推奨される「Composition API」とで、それぞれの記法の特徴的な差を理解すること
Options API(Vue2での記法)の扱い
Vue3でも「Options API」は「引き続きサポート」されます。
Options API は Vue の不可欠な要素であり、多くの開発者が Vue を愛する理由にもなっています。 Composition API の利点の多くは大規模プロジェクトでこそ現れるものであり、多くの低~中程度の複雑性のシナリオにおいては Options API が堅実な選択肢であり続けることも理解しています。
ref. https://ja.vuejs.org/guide/extras/composition-api-faq.html#will-options-api-be-deprecated
Vue2とVue3の違い
次のサイトでの解説が分かり易いのでお勧めです。
- 【Vue 3】Vue 2との違いについて
- 正式リリース前に総予習!! Vue3の変更点まとめ
- 【Vue3】Vue2ユーザー向けVue3入門
Vue3から推奨となったComposition APIの解説は次のサイトがお勧めです。
- 【Vue 3】Composition API の基本
想定読者
想定読者は次の方です。
- Vue2で、Vue CLIコマンド(
@vue/cli
)を用いてWebアプリを作成していた方 - Vue2からVue3への移行を始めようとする方
サンプルコード
次のリポジトリから取得できます。
https://github.com/hoshimado/qiita-notes/tree/main/qiita-vue3-diff-options-composition-api
動作環境(検証環境)
サンプルコードの動作確認は次のバージョンで実施しました。
"dependencies": {
"bootstrap": "^5.2.3",
"date-fns": "^2.29.3",
"vue": "^3.2.47"
},
サンプルコードの実装の都合でBootStrapとdate-fansを利用しています。BootStrap-Vueではなく素のBootStrapを直接に使う形式としています1。
Vue3での作成手順
次の順で説明します。
- Vueプロジェクトの導入方法
- Vueファイルの記法の違い
- Vue2相当のOptions API
- Vue3推奨のComposition API
Vueプロジェクトの導入方法(CLIでbuildするコマンドを準備)
Vue3では、次のコマンドでVueプロジェクト(SFC形式の一式)を作成します。
npm init vue@latest
上記のコマンドを実行すると、「プロジェクト名」を最初に聞かれます。そこへ入力した「プロジェクト名」を用いてフォルダが作成されて、その配下にVueプロジェクトが作成される仕様です。
Vue2の時の下記のように事前にVue-CLI( '@vue/cli` )をインストールする必要はありません。
npm install @vue/cli
npx vue create [作成するプロジェクトのフォルダ名]
ここで
「 npm init <initializer>
」
は、initializer
で指定したnpmパッケージで定義されている
初期化処理を実行するコマンドです。
ref. https://docs.npmjs.com/cli/v9/commands/npm-init
npm init can be used to set up a new or existing npm package.
initializer in this case is an npm package named create-,
which will be installed by npm-exec, and then have
its main bin executed -- presumably creating or updating package.json
and running any other initialization-related operations.The init command is transformed to a corresponding npm exec operation as follows:
- npm init foo -> npm exec create-foo
- npm init @usr/foo -> npm exec @usr/create-foo
「 npm init vue@latest
」コマンドを実行した際に入力する内容の説明に、戻ります。
初回実行時は、vueの初期化用パッケージの取得を確認してくるので、
yを押下します。
Need to install the following packages:
create-vue@latest
Ok to proceed? (y)
その後に続く質問は、Vueプロジェクトのオプション設定です。本記事では簡単のため、Lintのみ「Yes」としてそれ以外は全て「No」とします。なおデフォルト値は何れも「No」です。
Vue.js - The Progressive JavaScript Framework
√ Project name: ... frontend
√ Add TypeScript? ... -No- / Yes
√ Add JSX Support? ... -No- / Yes
√ Add Vue Router for Single Page Application development? ... -No- / Yes
√ Add Pinia for state management? ... -No- / Yes
√ Add Vitest for Unit Testing? ... -No- / Yes
√ Add an End-to-End Testing Solution? ≫ -No-
√ Add ESLint for code quality? ... No / -Yes-
√ Add Prettier for code formatting? ... -No- / Yes
Scaffolding project in D:\[XXX]\vue-project...
Done. Now run:
cd frontend
npm install
npm run dev
コマンド実行が完了すると、「Project name」で入力したフォルダが作成され、その配下にVueプロジェクトが作成されています。Vue2の際と同様にデモコードが格納されているので、上記の表示に従って npm run dev
で動作を確認できます。
Vue3でのVueファイルの記述方法(SFCファイルの記述方法)
デモ画面の説明は他のサイトに譲り2 3、本記事では次の機能を有したサンプルコードを用いて記述方法を説明します。
- 入力したテキストの文字列をカウントするWebアプリ
- 入力したテキストはブラウザのローカル領域に自動保存される
実装の設計概要は次の構造の通りです。
Componet間での親→子のデータ渡しを含む例とするため、
App.vue
で、utils/localStorageClinet.js
を読み込んで
new
したインスタンスを子コンポーネントに渡して、
TweetDrop.vue
で利用する形としています。
frontend
├─node_modules\
├─public\
└─src
├─App.vue
├─assets\
├─components\
| ├─MyClient.vue
| └─TweetDrop.vue
└─utils\
└─localStorageClient.js
サンプルコードのUIはこちらです。
Ver2準拠のOptions API記法
Vue3でも、Vue2と同じ記法で記述できます。
この記述方法を「Options API」と呼びます。
今回のサンプルではApp.vue
とTweetDrop.vue
を次のように実装しています(Css部分は省略)。
コードの全体像は、「サンプルコード」の節に記載のリポジトリを参照ください。
ここで注目してほしいのは、実装方法ではなく、次の2点です。
- Vue3でも、Vue2と全く同じ記法を用いれること(Options API)
- 次の節で例示する、Composition API記法との違い
<script>
import LocalStorageClient from './utils/localStorageClient.js';
import MyClient from './components/MyClient.vue';
export default {
name: 'app',
components: {
MyClient
},
data: function () {
return {
localStorageClient: new LocalStorageClient(window)
}
}
}
</script>
<template>
<div id="app">
<MyClient
v-bind:localStorageClient="localStorageClient"
>
</MyClient>
</div>
</template>
<script>
import { format } from 'date-fns'
export default {
name: 'TweetDrop',
props: {
localStorageClient : {
type: Object,
required : true
}
},
data: function () {
return {
tweetText: '',
lastTweetText: '',
lastLocalStoredTimeStr: ''
}
},
mounted: function () {
const localStoredText = this.localStorageClient.getLocalStoredText();
this.tweetText = localStoredText;
setInterval(() => {
if(this.lastTweetText != this.tweetText){
this.lastTweetText = this.tweetText;
this.localStorageClient.setLocalStoredText(this.lastTweetText);
this.lastLocalStoredTimeStr = this.getNowWithFormat();
}
}, 5000);
},
computed: {
tweetTextLength: function () {
return (this.tweetText) ? this.tweetText.length : 0;
}
},
methods: {
getNowWithFormat: function () {
const now = new Date()
const result = format(now, 'yyyy/MM/dd HH:mm')
return result;
},
clearText: function () {
this.tweetText = '';
this.localStorageClient.setLocalStoredText('');
}
}
};
</script>
<template>
<div id="id-root">
<textarea
v-model="tweetText"
rows="10" cols="40"
class="textarea-width100"
placeholder="ここにツイート文を入力"
></textarea>
<div class="div-textarea-footer container-space-between" style="height: 50px;">
<div class="container-flex">
<div class="div-label">
<label>文字数:{{ tweetTextLength }} </label>
</div>
<div class="div-label">
<label>保存日時:{{ lastLocalStoredTimeStr }} </label>
</div>
</div>
<div class="container-flex">
<div>
<button type="button" class="btn btn-warning" v-on:click="clearText">クリア</button>
</div>
</div>
</div>
</div>
</template>
Ver3から推奨となったComposition API記法
Vue3では「Composition API」という記法が推奨され、
デモ画面の実装もこのComposition APIで記述されています。
今回のサンプルを、Composition APIで書き直すと、以下となります(こちらもCss部分は省略)。
<script setup>
import { reactive } from 'vue';
import LocalStorageClient from './utils/localStorageClient.js';
import MyClient from './components/MyClient.vue';
const localStorageClient = reactive(new LocalStorageClient(window));
</script>
<template>
<div id="app">
<MyClient
v-bind:localStorageClient="localStorageClient"
>
</MyClient>
</div>
</template>
<script setup>
import { computed } from 'vue';
import { onMounted, ref } from 'vue';
import { format } from 'date-fns'
const props = defineProps({
localStorageClient : {
type: Object,
required : true
}
});
const tweetText = ref('');
const tweetTextLength = computed(()=>{
return (tweetText.value) ? tweetText.value.length : 0;
});
var lastTweetText = '';
const getNowWithFormat = function() {
const now = new Date()
const result = format(now, 'yyyy/MM/dd HH:mm')
return result;
};
var lastLocalStoredTimeStr = ref('');
const clearText = function () {
tweetText.value = '';
props.localStorageClient.setLocalStoredText('');
}
onMounted(()=>{
const localStoredText = props.localStorageClient.getLocalStoredText();
tweetText.value = localStoredText;
setInterval(() => {
if(lastTweetText != tweetText.value){
lastTweetText = tweetText.value;
props.localStorageClient.setLocalStoredText(lastTweetText);
lastLocalStoredTimeStr.value = getNowWithFormat();
}
}, 5000);
});
</script>
<template>
<div id="id-root">
<textarea
v-model="tweetText"
rows="10" cols="40"
class="textarea-width100"
placeholder="ここにツイート文を入力"
></textarea>
<div class="div-textarea-footer container-space-between" style="height: 50px;">
<div class="container-flex">
<div class="div-label">
<label>文字数:{{ tweetTextLength }} </label>
</div>
<div class="div-label">
<label>保存日時:{{ lastLocalStoredTimeStr }} </label>
</div>
</div>
<div class="container-flex">
<div>
<button type="button" class="btn btn-warning" v-on:click="clearText">クリア</button>
</div>
</div>
</div>
</div>
</template>
Options API記法と Composition API記法の比較
それぞれの記法の特徴などの説明は、他のサイトに譲ります。節「Vue2とVue3の違い」で紹介した記事が良くまとまっているのでお勧めです。
詳細は上記の記事を参照いただきたいのですが、ザッとサンプルコードの
TweetDrop.vue
で比較してみると、次のようになります。
左がComposition API記法で、右がOptions API記法です。
見ての通り、<template>
タグ内は同一(後半は省略)です。異なるのは
<script>
タグの中となります。
このサンプルの範囲での移行時の注意点としては、次の部分です。
-
defineProps()
で親コンポーネントから受け取ったプロパティに<script setup>
タグ内でアクセスするには、明示的にconst props = defineProps()
で宣言しておき、props.XXX
の形でアクセスする必要があること。 - 一方で、
<template>
タグ内でアクセスする場合は、props
への代入を省略して、XXX
に直にアクセス可能であること。たとえば(先ほどは記載を省略した)MyClient.vue
では次のように<template>
タグ内のみのアクセスのため、代入を省略した表記が可能。
<script setup>
import TweetDrop from './TweetDrop.vue';
import { onMounted, ref } from 'vue';
defineProps({
localStorageClient : {
type: Object,
required : true
}
});
const isCreating1st = ref(true);
const isSignup = ref(false);
onMounted(()=>{
setTimeout(() => {
isCreating1st.value = false;
isSignup.value = true;
}, 1000);
});
</script>
Propsの扱い
公式ガイドでは、<script setup>
タグ内でPropsにアクセスする場合の記法として次のサンプルコードが記載されています。
<script setup>
const props = defineProps({
foo: String
})
ref. https://vuejs.org/api/sfc-script-setup.html#defineprops-defineemits
<template>
タグ内のみでのPropsアクセスの例は記載が見当たりませんが、仕様として次の記載があります。実際、definePoprs()
の戻り値変数を経由することなく、Propsに直接アクセスが可能です。
Top-level bindings in <script setup> are automatically exposed to the template. For more details
ref. https://vuejs.org/api/sfc-spec.html#script-setup
実行方法(build方法)
Vue3では、Vue2でのdebug実行時のコマンドの設定が「npm run serve
」から「npm run dev
」へ変更となります。
npm
で呼び出されるコマンドの実体も次の通りにVue CLIコマンドから、Viteコマンドへ変更となります4。
- Vue2: Vue CLIコマンド
"serve": "vue-cli-service serve"
- Vue3: Viteコマンド
"dev": "vite"
ref. https://ja.vuejs.org/guide/scaling-up/tooling.html#project-scaffolding
サンプルコードでは
frontend-options-api
フォルダ、
frontend-composition-api
フォルダ、
それぞれで次のコマンドを実行するとデバッグ実行ができます(※npm install
コマンドは初回のみの実行で以降はスキップしてください)。
npm install
npm run dev
正式buildのコマンド設定はnpm run build
で変更ありませんが、build時にViteを使う場合(標準設定)は、buildオプションの定義ファイルが vite.config.js
ファイルで指定するように変更になっているので注意ください5。
以上ー。
-
Vue3系への対応が未だ暫定的と思われるため。「 Important limitations: @vue-compat support is currently limited to { MODE: 2 } configuration both for compiler and Vue.js itself. You can find more details in compat configuration section of migration guide. 」https://bootstrap-vue.org/vue3 ↩
-
今日から始めるVue.js 3を学んで本格的なWebフロントエンド開発(基礎編), https://reffect.co.jp/vue/beginner-vue ↩
-
【完全解説】create-vueでVue3プロジェクト作成~Vite爆速開発~, https://reffect.co.jp/vue/create-vue ↩
-
Vue CLIコマンドも引き続き利用可能ですが、「メンテナンスモード」となっているため、Viteの利用が推奨されています。 https://ja.vuejs.org/guide/scaling-up/tooling.html#vue-cli ↩
-
Build Options, https://vitejs.dev/config/build-options.html ↩