-ao- ramune blog

©2024 unio / GO2直営からふるラムネ
2018年08月06日

はじめてのSymfony4 + Vue.js

スタイルのscopedについて

チュートリアル対象バージョン

スタイルのscopedについて

Vue SFCのscopedがどのように動くのか、実際に2つのコンポーネントを書いて試してみましょう。 まずはルートコンポーネントのScopedCssTestと、子コンポーネントScopedCssElemを用意します。

assets/js/vue/ScopedCssTest.vue
                
                    <template>
                        <div class="scoped-css">
                            <span class="text">scoped css test</span>
                            <scoped-css-elem/>
                        </div>
                    </template>

                    <script>
                        import ScopedCssElem from "./components/ScopedCssElem";

                        export default {
                            components: {ScopedCssElem}
                        }
                    </script>

                    <style scoped>
                        .scoped-css {
                            padding: 1rem;
                            border: solid 2px red;
                        }

                        .text {
                            margin-left: 13rem;
                            font-size: 2rem;
                            font-weight: bold;
                        }
                    </style>
                
            
assets/js/vue/components/ScopedCssElem.vue
                
                    <template>
                        <div class="scoped-css">
                            <span class="text">element</span>
                        </div>
                    </template>

                    <style scoped>
                        .scoped-css {
                            border: dotted 2px blue;
                        }

                        .text {
                            font-style: italic;
                        }
                    </style>
                
            

どちらのコンポーネントも scoped-csstext クラスを持っています。 scopedなので、コンポーネント毎にそれぞれ独立したスタイルがされるはずです。 動作確認をするために、Vueインスタンスを生成するjsとwebpack.config.jsの設定、コントローラとtwigを追加します。

assets/js/scopedCssTest.js
                
                    import Vue from 'vue'
                    import ScopedCssTest from './vue/ScopedCssTest'

                    new Vue({
                        el: '#tutorial',
                        template: '<scoped-css-test/>',
                        components: {ScopedCssTest}
                    });
                
            
webpack.config.js
                
                    Encore
                        ...
                        .addEntry('scopedCssTest', './assets/js/scopedCssTest.js')
                        ...
                    ;
                
            
TutorialController.php
                
                    /**
                     * @Route("/scopedCssTest", name="tutorial_scoped_css_test", methods="GET")
                     * @Template
                     */
                    public function scopedCssTest(): array
                    {
                        return [];
                    }
                
            
templates/tutorial/scoped_css_test.html.twig
                
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <title>CSS scoped 確認</title>
                        <script defer src="{{ asset('build/scopedCssTest.js') }}"></script>
                        <link rel="stylesheet" href="{{ asset('build/scopedCssTest.css') }}"/>
                    </head>
                    <body>
                    <div id="tutorial"></div>
                    </body>
                    </html>
                
            

コンパイル後に http://localhost:8000/tutorial/scopedCssTest へアクセスして実際の挙動を確認します。

コンソールコマンド
                
                    yarn dev
                    bin/console server:start
                
            

textはコンポーネント毎のスタイルが当たっていますが、scoped-cssはルートコンポーネントの padding: 1rem; が子コンポーネントにも適用されています。 このようにscopedのスタイルは対象のコンポーネントのみ影響しますが、 例外としてルート要素は親コンポーネントのスタイルも適用されます。

With scoped, the parent component's styles will not leak into child components. However, a child component's root node will be affected by both the parent's scoped CSS and the child's scoped CSS.

VueLoader: Child Component Root Elements

展開済みのDOMと、コンパイルされたCSSを見てみると、scopedがどのようにして実現されているのかが分かります。 カスタムデータ属性に対してCSSのセレクタ指定を行っているのですが、子コンポーネントのルート要素に親要素のカスタムデータ属性も指定されています。

HTML
                
                    <div data-v-50940778 class="scoped-css">
                        <div data-v-50940778 class="scoped-css">
                            <span data-v-50940778 class="text">scoped css test</span>
                            <div data-v-33bdf7cc data-v-50940778 class="scoped-css">
                                <span data-v-33bdf7cc class="text">element</span>
                            </div>
                        </div>
                    </div>
                
            
public/build/scopedCssTest.css
                
                    .scoped-css[data-v-50940778] {
                        padding: 1rem;
                        border: solid 2px red;
                    }
                    .text[data-v-50940778] {
                        margin-left: 13rem;
                        font-size: 2rem;
                        font-weight: bold;
                    }

                    .scoped-css[data-v-33bdf7cc] {
                        border: dotted 2px blue;
                    }
                    .text[data-v-33bdf7cc] {
                        font-style: italic;
                    }
                
            

ルート要素で同様のスタイルが当たるように調整することができる反面、子コンポーネントに対する意図しないスタイル適用が起こる可能性があるので注意しましょう。