terence55 / themes-switch

Toolset for switch multiple themes in application based on webpack
54 stars 18 forks source link

Question: Has anyone tried this in a Vue application? #4

Closed WayneHiller closed 3 years ago

WayneHiller commented 4 years ago

Has anyone tried this in a Vue application build with the vue-cli 3? I would like to give it a try. I think it is easy enough to get the Plugin working in the webpack config. I am using bundling so I guess I need to turn that off (somehow) for css/sass files?

terence55 commented 4 years ago

You need to use mini-css-extract-plugin to extract css from your code. I add a sample for vue, and I hope that would be useful to you. https://github.com/terence55/themes-switch/blob/master/samples/simple-less-vue/webpack.config.js

WayneHiller commented 4 years ago

@terence55 Do you have any experience with webpack chunking? I have a large Vue app so I am using chunking for JS/Vue files. If I use themes I think I need some way to exclude css/scss files from chunking. Any ideas?

This is what I have in the vue.config file.

    configureWebpack: {
        optimization: {
            splitChunks: {
                cacheGroups: {
                    vendors: {
                        minSize: 100000,
                        maxSize: 1000000
                    },
                    common: {
                        chunks: "all",
                        minChunks: 3
                    }
                }
            }
        }
    },
The-Wood commented 4 years ago

@WayneHiller I have problems integrating this plugin as well. I really like the idea of the plugin though, so I still try to find a solution. In my case, the root problem of the plugin not working as expected, doesn't seem to be chunking however.

I was wondering what exactly didn't work in your case. For me, the generated CSS files were empty.

I am currently trying to narrow-down the problem. I started from the Vue case that was provided by @terence55 and integrated the SCSS example. It now stopped working when i switched to .vue files (together with the vue-loader). The "main" CSS file still works fine; the theme files are now empty however. This is the same behavior as in my real build case.

EDIT: It looks like the problem in my case might originate in the entry points that are created in the plugin. After integrating the .vue-file-templates mentioned above, i removed the "global style import" that can be seen in the main.js in the examples. Instead the only things that generate .css now, are the .vue files. Now; these only get read when the entry point is "main.js", and not when the entry point is "dark.scss" - but this is the entry point the the plugin generates. All the .scss code in there is only variable (and mixin) declaration - therefore my CSS files remain empty.

WayneHiller commented 4 years ago

@The-Wood I have not been able to get it working at all yet with chunking. Webpack still drives me crazy when trying to figure things like this out. So many plugins etc added by the Vue-cli so it is hard to trace the order of execution. We need a Webpack guru to take a look at it :)

WayneHiller commented 4 years ago

I am getting closer.

vue.config.js

const ThemesGeneratorPlugin  = require("themes-switch/ThemesGeneratorPlugin");

module.exports = {
    outputDir: "./wwwroot",
    runtimeCompiler: true,
    configureWebpack: {
        plugins: [
            new ThemesGeneratorPlugin({
                srcDir: 'src',
                themesDir: 'src/styles/themes',
                outputDir: '../wwwroot/css',
                defaultStyleName: 'default.scss',
                importAfterVariables: true,
                useStaticThemeName: true
            })
        ]
    }
};

Output image

Switching themes at runtime (ts)

setTheme(theme: string) {
    console.log("Switching theme to " + theme);
    changeTheme("theme-" + theme, "css/theme-" + theme + ".css");
}

This is all working so far. Next I need to see if I can get all the common css into a separate css file. Currently my theme scss files contain:

@import "~bootswatch/dist/darkly/variables";
@import "~bootstrap/scss/bootstrap";
@import "~bootswatch/dist/darkly/bootswatch";
@import "~bootstrap-vue/src/index.scss";
The-Wood commented 4 years ago

@WayneHiller I kept looking into my case for another two days or so. I am glad that you are making progress. I however think I hit a dead end.

It looks like this plugin creates another entry point for each theme (which seems to be the right approach in general). However, these entry points are the theme files (.scss in my case) and not the main.js file. This is a problem for me (as far as I understand it whatsover), because the main.js file instantiates Vue which in turn loads all the .vue files which contain the actual styling.

So this explains why, in my case, the theme .css files remained empty: the actual files which define all the styles that I need are just not loaded.

Maybe this information helps in your quest ;)

WayneHiller commented 4 years ago

@The-Wood Mine is working correctly when I include styling in .vue files. The styles are extracted into the app.css file by the vue-loader. I am however using css in the vue components and not scss. I don't think it is possible to extract those styles into the theme files. Are you using scss in your component styles, if so you should be able to use variables for the themed parts and then set those variables in the actual theme scss files.

The-Wood commented 4 years ago

@WayneHiller I use both .scss and .css in the .vue files. In the normal build, the vue-loader is also able to correctly collect the .scss and in a later step it is converted to .scss. With the plugin-setup I am also able to use the style variables in the themes (or the default.scss respectively) and again it works correctly for the main build; not for the themes. I would really like to provide a codepen to illustrate my case better (because i am now really intrigued 😄 ) but sadly i just lack the time for that right now. Maybe I can provide it in some weeks, when I revisit theming.

chetan commented 4 years ago

FWIW, I ran into issues which I believe are related to compiling my app via vue-cli build rather than webpack directly, as in the sample app using vue. The issue was that the value returned by process.themes and stored in my JS did not match the filenames for the generated CSS files. Rather than debug this too much, I came up with the following solution as a workaround:

loadThemes() {
  const themes = {};
  $('link').each((i, el) => {
    // <link href="/static/css/theme-white.27d90b5e.css" rel="preload" as="style">
    const href = $(el).attr('href');
    if (!href) {
      return;
    }
    const m = href.match(/\/(theme-[a-z]+)\..*\.css$/);
    if (!m) {
      return;
    }
    themes[m[1]] = href;
  });
  this.themes = themes;
},

I have this component in one of my base components. It creates the same {key: url} structure as provided by process.themes so the rest works as shown in the examples.

Also note that hot code reloading does not work with this plugin, as it necessarily requires the css to be extracted in files. Only production builds will work.

WayneHiller commented 4 years ago

@chetan I was able to solve the dev (Hot Reload) issue by adding an alias to the vue.config.js file

pluginOptions: {
        "resolve-alias": {
            alias: {
                components: path.resolve(__dirname, "./src/components"),
                _corecss: path.resolve(__dirname, "./src/css/" + (process.env.NODE_ENV === "production" ? "default.scss" : "dev.scss"))
            }
        }
    }

The dev.scss file just @import's one of the theme scss files.

chetan commented 4 years ago

@WayneHiller, I'm not sure how that will allow it to be loaded though? The changeTheme() call adds the CSS file to the page directly, but currently it 404s in the dev server, since all CSS is injected via JS code. Do you have some other workaround to be able to serve the CSS files?

WayneHiller commented 4 years ago

In my main.js I do:

import "_corecss";

This will import ./src/css/dev.scss during development or ./src/css/default.scss in production. This does mean I can only use one theme at a time when developing and cannot switch themes but is does work ok.

terence55 commented 4 years ago

Sorry for replying late, are there any unavailable problems? I add a sample for Vue with Webpack chunking, you can see if it helps. The known issue is that the plugin cannot find them if you write styles in Vue component <style>, so it should be a separated file. Actually the plugin does not support hot-reloading because currently styles of all themes will be generated only at entryOption. The reason is that the generation of styles is a time-consuming operation, I had hoped to avoid generating styles frequently. I will try to add a config item to control whether it is activated.