Global Vue Component Styles "Leaking" Over Entire Website

Viewed 318

What I mean by "Leaking" is the following. I have an About.vue with its own styling (About.scss) and it's own endpoint "/about". I also have the home page endpoint "/" and its corresponding Laravel blade template (Index.blade.php) with its own styling (Index.scss).

The Problem

The style from About.scss is affecting the Index.scss, and they're not even in the same html file that's returned by the server. And it's like this for my entire website. Everything was completely fine before I updated to Laravel Mix ^5.*.

composer.json

{
    "name": "laravel/laravel",
    "description": "The Laravel Framework.",
    "keywords": [
        "framework",
        "laravel"
    ],
    "license": "MIT",
    "type": "project",
    "require": {
        "php": "^7.4.1",
        "algolia/algoliasearch-client-php": "^2.2",
        "awobaz/compoships": "^2.0.0",
        "barryvdh/laravel-ide-helper": "^2.4",
        "benjaminhirsch/nova-slug-field": "^1.2",
        "dillingham/nova-conditional-fields": "^0.0.1",
        "doctrine/dbal": "^2.9",
        "easypost/easypost-php": "~3.4.4",
        "facade/ignition": "^2.3.6",
        "fideloper/proxy": "^4.0",
        "genealabs/nova-prepopulate-searchable": "^1.0.1",
        "guzzlehttp/guzzle": "^7.0.1",
        "integral/nova-theme": "*",
        "laravel/framework": "^8.0",
        "laravel/nova": "^3.0",
        "laravel/scout": "^8.3",
        "laravel/slack-notification-channel": "^2.0",
        "laravel/tinker": "^2.4.2",
        "laravel/ui": "^3.0",
        "predis/predis": "^1.1",
        "spatie/laravel-permission": "^3.17",
        "staudenmeir/eloquent-has-many-deep": "^1.7",
        "stripe/stripe-php": "^6.16"
    },
    "require-dev": {
        "barryvdh/laravel-debugbar": "^3.1",
        "filp/whoops": "^2.0",
        "fzaninotto/faker": "^1.4",
        "laravel/telescope": "^4.0",
        "mockery/mockery": "^1.0",
        "nunomaduro/collision": "^5.0",
        "phpunit/phpunit": "^9.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Database\\Factories\\": "database/factories/",
            "Database\\Seeders\\": "database/seeders/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "repositories": [
        {
            "type": "composer",
            "url": "https://nova.laravel.com"
        },
        {
            "type": "path",
            "url": "./nova-components/NovaTheme"
        },
        {
            "type": "vcs",
            "url": "https://github.com/EasyPost/easypost-php"
        }
    ],
    "extra": {
        "laravel": {
            "dont-discover": []
        }
    },
    "scripts": {
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate"
        ],
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover"
        ]
    },
    "config": {
        "preferred-install": "dist",
        "sort-packages": true,
        "optimize-autoloader": true
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

App.js

import headerComponent from './components/HeaderComponent.vue';
import aboutView from './components/About/About.vue';

Vue.component('header-component', headerComponent);
Vue.component('about-view', aboutView);

const app = new Vue({
    el: '#app'
});

package.json

 "devDependencies": {
        "axios": "^0.18.1",
        "bootstrap": "^4.5.0",
        "cross-env": "^5.2.1",
        "jquery": "^3.5.1",
        "laravel-mix": "^5.0.7",
        "lodash": "^4.17.20",
        "popper.js": "^1.16.1",
        "resolve-url-loader": "^3.1.2",
        "sass": "^1.27.2",
        "sass-loader": "^7.3.1",
        "vue": "^2.6.12",
        "vue-template-compiler": "^2.6.12"
    },
    "dependencies": {
        "@easypost/api": "^3.8.1",
        "algoliasearch": "^3.35.1",
        "fs": "0.0.1-security",
        "gsap": "^2.1.3",
        "node-vibrant": "^3.1.5",
        "npm": "^7.0.6",
        "rgbaster": "^2.1.1",
        "stickyfilljs": "^2.1.0",
        "update": "^0.7.4",
        "vue-carousel": "^0.11.0",
        "vue-instantsearch": "^2.7.0",
        "vue-moment": "^4.1.0",
        "vue-stripe-elements-plus": "^0.2.10",
        "vue2-collapse": "^1.0.15",
        "vue2-touch-events": "^2.2.1",
        "vuelidate": "^0.7.5"
    }

webpack.mix.js

let mix = require('laravel-mix');
mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');

web.php

<?php

Route::get('/', 'GeneralPagesController@index');

Route::get('/about', 'GeneralPagesController@about');

GeneralPagesController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\HomePageNews;


class GeneralPagesController extends Controller
{
  public function index()
    {
        $news = HomePageNews::all();

        return view("integral.index", compact("news"));
    }

    public function about()
    {
        return view("integral.about");
    }
}

About.vue

<template>
    <div class="wrapper"> More component stuff..</div>
</template>
<style src="./About.scss" scoped lang="scss"></style>
<style lang="scss">
    body, html {
        height: 100%;
        overflow: hidden;
    }

    #desktop-footer {
        display: none !important;
    }

    #articleViewMount {
        height: 100%;
    }
</style>

about.blade.php

@extends("integral.integralLayouts.master")

@section("title")
About
@endsection

@section("Styles")
 <meta name="csrf-token" content="{{ csrf_token() }}">

@endsection

@section("sectionOne")

 <div id="articleViewMount">
  <about-view></about-view>
 </div>

@endsection

index.blade.php AKA Homepage

@extends("integral.integralLayouts.master")


@section("Styles")
   
    <link type="text/css" rel="stylesheet" href="/css/index.css"/>
@endsection

// Home page html stuff...

integral.integralLayouts.master.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <title>@yield("title")</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link type="text/css" rel="stylesheet" href="/css/master.css">
    <link type="text/css" rel="stylesheet" href="/css/mobilemenu.css">


    @yield("Styles")
</head>

<body>
    @yield("LoadingLogo")

    <!-- HEADER & NAV BAR -->
    @include("integral.integralLayouts.header")

<!--SECTION ONE-->
    @yield("sectionOne")

<!-- Flash Messaging -->

    <div id="flashComponentMount">
        <flash message="{{ session('flash') }}"></flash>
    </div>

<!-- FOOTER -->
    @include("integral.integralLayouts.footer")

<script src="{{ mix('js/app.js') }}"></script>

@yield("Scripts")

</body>
</html>

Things I've tried:

Adding the "scoped" attribute to my styles and it fixes the issue sort of, but then this also breaks the styling of other pages on my website.

This is what I get in the console before commenting overflow: hidden; and display: none !important;of the unscoped styling.

index/home page

This shows the home page's styling being affected by About.scss. Here, overflow is hidden, just like in About.scss. This is the index/home page. It shows the styling being affected by About.scss

Here's the footer component, which is implemented by integral.integralLayouts.master.blade.php, being affected by About.scss as well. You can see the display: none !important; present on the footer tag. Here's the footer component being affected by About.scss as well.

When I comment overflow: hidden; and display: none !important; of the unscoped styling in About.vue, this is what happens.

About.vue

<template>
    <div class="wrapper"></div>
</template>
<script src="./About.js"></script>
<style src="./About.scss" scoped lang="scss"></style>
<style lang="scss">
    body, html {
        height: 100%;
        /*overflow: hidden;*/
    }

    #desktop-footer {
        /*display: none !important;*/
    }

    #articleViewMount {
        height: 100%;
    }
</style>

The overflow: hidden; is no longer applied to the html tag of index.blade.php.

enter image description here

And neither is display: none !important; being applied from About.vue to the footer component.

enter image description here

Edit

Per @matticustard's suggestion, I also just tried registering my vue components globally like so but to no avail.

Vue.component('header-component', require('./components/HeaderComponent.vue').default);
Vue.component('about-view', require('./components/About/About.vue').default);

const app = new Vue({
    el: '#app'
});

Why would this happen now and not before? It's been working fine for a few years now.

2 Answers

Well, I just had to bite the bullet and convert ALL of my styles to "scoped," scanning all pages on my web site to fix any breaking changes. It sucked, but so far I think I found all the styling issues and was able to fix them.

I have a feeling that Laravel-Mix ^5.0 compiles its assets a little differently compared to previous versions. That'd be my best guess.

    const app = new Vue({
      el: '#app',
      components: {
        'header-component' : () => import('./components/HeaderComponent.vue'),
        'about-view' : () => import('./components/About/About.vue'),
      }
    });

I recently have the same problem. You can lazy load components instead so that component is loaded only when called including its styles.

Related