1
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

とりあえずHTML直書きをやめるためのVue3入門①:基本編

Last updated at Posted at 2023-11-17

基本編(コンポーネントの作り方)です。
この記事を読めば「膨大なindex.html」から解放されます

命名規則(スネークケース、ケバブケース、キャメルケースの使い分け)がバックエンド言語に寄せていまして、推奨とはかなり違います。動作は問題ありません。
https://ja.vuejs.org/style-guide/rules-strongly-recommended.html

はじめに

HTML直書き、めんどくさいですよね

フロントエンド初心者のみなさん、HTML直書き楽しんでますか??
楽しんでないですよね(断定)

ページが大きくなればなるほど辛くなってきますよね

なぜ辛くなるのか当てましょう。

  • 全てを1ファイルに書き、内容が膨れ上がるほどだんだん視認性・メンテナンス性が悪くなるから
  • 同じような要素を再利用できないから
    (templateを使うというのもありますが)
  • 変数が多くなればなるほど、更新時のコールバックなりのやりとりが多くなるから

当たりですか?
もう、苦しむのはやめませんか?やめましょう

Vueを使おう

Vueを使いましょう。Vueはいいものです。 こら、Reactの方を向くんじゃない。今からVueをするんや。

Vueは先述した3論点を全て解決します。
すなわち、

  • 部品ごとにHTMLもJavaScriptもCSSもまとめてファイルを分けられるため、視認性がとても良い
  • 同じ部品を気楽に再利用でき、なんならfor文まで用意されている
  • 変数の更新時、依存したデータ自動で更新させることができる

ということができます。素敵ですね。

フレームワークは覚えること多いってよく聞きますよね?大丈夫!
今回は

  • テキトーに!とりあえずVueを扱えるように!
  • 環境構築は簡単(というか無し)に!

簡略化してお届けします。

使い方

環境構築

VSCodeをインストールして、拡張機能に

  • HTMLをリアルタイムでブラウザに映すlive server
    image.png
  • Vueの記入がやりやすくなるVue Language Features (Volar)
    image.png

の2つを入れてください。終わり。

npm? やりたかったらやってください。

理解不要テンプレート

こちらのレポジトリのディレクトリ構成でそのまま動きます:https://github.com/konbraphat51/Vue3_CDN_template

こんなディレクトリ構成
image.png

こちらにもスクリプトを掲載します
メインのHTMLとなるindex.html理解不要

index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>TITLE</title>

    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script src="https://unpkg.com/vue-i18n@9"></script>
    <script src="https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>
</head>

<body>
    <div id="app"></div>

    <script>
        //initialize i18n
        const i18n = VueI18n.createI18n({
            legacy: false,
            locale: navigator.language,
            fallbackLocale: "en",
        });

        //initialize vue3-sfc-loader
        const options = {
            moduleCache: {
                vue: Vue
            },

            //read subdirectory vue component file.
            async getFile(url) {

                const res = await fetch(url);
                if (!res.ok)
                    throw Object.assign(new Error(res.statusText + ' ' + url), { res });
                return {
                    getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
                }
            },

            addStyle(textContent) {

                const style = Object.assign(document.createElement('style'), { textContent });
                const ref = document.head.getElementsByTagName('style')[0] || null;
                document.head.insertBefore(style, ref);
            },


            //read <i18n> tag
            customBlockHandler(block, filename, options) {
                if (block.type !== 'i18n')
                    return
                const messages = JSON.parse(block.content);
                for (let locale in messages)
                    i18n.global.mergeLocaleMessage(locale, messages[locale]);
            }
        }
        const { loadModule } = window["vue3-sfc-loader"];

        //Mount App.vue
        const app = Vue.createApp(
            Vue.defineAsyncComponent(
                () => loadModule("./src/components/App.vue", options)
            ),
        )
        app.use(i18n);
    </script>
    <script src="./src/scripts/router.js"></script>
    <script>
        app.use(router)
        app.mount('#app');
    </script>
</body>

</html>

特に変更しなくともVueは動作します。
ページタイトル変えたいときなどはどうぞ編集してください。

別にこれが何をするhtmlか理解しなくとも大丈夫なのですが、気になる人のために。
めっちゃ簡単に言えば、

  • ネット上からVueライブラリを引っ張ってきて(CDNといいます)、
  • 後述するコンポーネントをdiv(id="app")にマウント(ひっつけ)しています

いろいろスクリプトが長いのは、vue3-sfc-loaderのセットアップをしています。ファイル分けする場合Vue CDNは本来使うものではないですが、無理やり可能にしています。

こちらが我々が今から操作するコンポーネントのテンプレート

TEMPLATE
<template>
    <!-- <div id="COMPONENT_NAME">
        any HTML you want

        <button id="example" @click="select('example')">{{ t("Example.button") }}</button>

        <SUB_COMPONENT :sub_component_props_name:"example_parameter" @event_name="function_name()"/>

        <SOMETHING v-for="example in example_list" :key="example.id" :example_props_name="example">
    </div> -->
</template>

<script>
export default Vue.defineComponent({
    name: 'COMOPNENT_NAME',
    components: {
        //"SUB_COMPONENT": Vue.defineAsyncComponent(() => loadModule("src/components/SUB_COMPONENT.vue", options)),
    },
    setup() {
        //一番最初に走るjavascript

        //i18n設定
        const { t } = VueI18n.useI18n()
        return { t }
    },
    mounted() {
        //マウントされたときに走るjavascript。setup()より基本的にこれを使う。
    },
    updated() {
        //表示内容が更新されたときに走るjavascript
    },
    data() {
        //プライベート変数
        return {
            //example_variable: "example"
        }
    },
    props: {
        //コンポーネントの引数

        // example_parameter: {
        //     type: String,
        //     required: true
        // }
    },
    methods: {
        // このコンポーネントのメソッド
        // select(example) {
        //     this.$emit("onSelected", example)
        // },
        // example_method() {
        //   
        // }
    },
    watch: {
        // 変化監視
        // example_variable: {
        //     handler: function (newVal, oldVal) {
        //         //script called when example_variable is changed
        //     },
        //     deep: true
        // }
    },
    computed: {
        // プライベート変数の一種。
        // 中身の計算に使われている変数が更新されると、これを検知してこちらも更新される。
        // example_computed() {
        //      
        //     return this.example_variable + " computed"
        // }
    }
})
</script>

<i18n>
    {
        "en": {
            "Example": {
                "button": "example"
            }
        },
        "ja": {
            "Example": {
                "button": "例"
            }
        }
    }
</i18n>

<style>
/*.COMPONENT_NAME {
    font-size: 1.5em;
    font-weight: bold;     
}*/
</style>

これ見てヴゥエってなりましたか? 大丈夫です。このテンプレートをコピペして、お好きな箇所を付け足すだけで 大丈夫です。
難しそうに見えて、頭空っぽでできます

コンポーネントとは?

簡単に言うと 「部品」 であり、自作HTMLタグ ともいえます。

例えば、標準HTMLでは<button>と書けば、「クリックするとイベントを発し、四角い枠で囲まれた文字列」がセットで設置されますよね?
こんなタグが作れます。例えば「クリックすると振動する説明文タグ」でもみたいな誰得HTMLタグでも。
例えばUserListというコンポーネントを作れば、<UserList />というタグを書けるようできるわけです。

これを使うことで、 デザインの再利用も簡単 になりますね。
そして、コンポーネントごとにファイルを作るので、 ファイルがかさばらずに、見たいところだけ見えて視認性が良く なりますね。

コンポーネントの作り方

まずは、拡張子が.vueのファイルを良い感じのところに置いてください。慣習的には、src/componentsディレクトリに設置しますが、どこでもいいです。

上記TEMPLATE.vueの中身をコピペしてください。

そして、下記の2点の操作をしてください:

  • <template>の中身を、欲しいHTMLに書き換えてください。
  • <script>の中、defineComponentの中、nameを欲しいコンポーネント名にしてください。

これでコンポーネントの完成です。

ここでは例として、こんな例を用意しました。(不要な箇所は省略していますが、あってもいいです)

Tamago.vue
<template>
    <div id="Tamago">
        Tamago
    </div>
</template>

<script>
    export default Vue.defineComponent({
        name: 'Tamago',
    })
</script>

コンポーネントを表示させる

ドラクエでは装備を持つだけでは効果がないのと同じで、コンポーネントも作るだけでは意味がありません。(表示されません)
表示させるには、Appコンポーネントに追加させる必要があります

App.vueをこのように編集(不要な箇所は削除していますが、あってもいいです)

App.vue
<template>
    <div id="app">
+        <Tamago />
    </div>
</template>

<script>
export default Vue.defineComponent({
    name: 'App',
    components: {
+        "Tamago": Vue.defineAsyncComponent(() => loadModule("src/components/Tamago.vue", options)),
    },
})
</script>

loadModule()のパスに注意。
optionsはグローバル変数なので思考停止で書いてください。

表示されているはずです。
image.png

ちなみに、コンソールがこのような感じで表示されますが、健全です。(無理してファイル分けしているのでね。しかし全然問題ありません)
image.png

重要なところは3点。

  • App.vue<template>ページ全体のHTML。
    index.htmlをそのままにしていたら、ですけど。
  • コンポーネントAにコンポーネントBを取り込むには、components
    "Tamago": Vue.defineAsyncComponent(() => loadModule("src/components/Tamago.vue", options))
    を追加する。
    コピペ呪文で大丈夫です。 キー名をコンポーネント名に、パスはもちろん.vueファイルの場所、optionsはグローバル変数です。
  • コンポーネントを作れば、<(コンポーネント名) />タグを使える
    最後の/に注意してください。

コンポーネントにスタイルを付加する

もうお気づきかとおもいますけど、<styles>に **そのままCSSを書けば大丈夫です。

image.png

Copilotを使っていれば、普通にCSS書くよりもかなり当意即妙のスタイリングをしてくれます。
例えば、このサイトのCSSはほぼ全てCopilotが書いてくれました:
https://konbraphat51.github.io/HSS_examples/

今回はここまで

今あなたができるようになったこと

超簡単に、HTMLを部品に分割することができるようになりました。
デカいindex.htmlとはもうおさらばです。
これからは、積極的にwebページをコンポーネントに分解し、気持ちよく、頭空っぽでページを作りましょう。
Vue最高!!

次回予告

テンプレートにある通り、まだまだ機能がいっぱいあります。
次回は、変数・データのコンポーネント間の受け渡しを扱います。

いいね頂けると泣きながら喜びます><

1
5
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
1
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?