CSS Modules and multiple layouts/themes?

[亡魂溺海] 提交于 2020-01-24 05:01:27

问题


In my application, I have multiple theme styles (you can think of them as different, separate CSS styles files). I would like to start using the CSS modules, but I don't know even how to import my first file.

Lets assume the following (simple) directory structure:

layouts/
    themeA/
        myComponent.css
    themeB/
        myComponent.css
    themeC/
        myComponent.css
components/
    myComponent.js

Depending on the user settings, I would like to pick a different CSS. That's easy to do in the browser (or on the server). But how can I include myComponent.css into myComponent.js?

According to CSS modules, I should import the file I'm using. So import styles from 'theme/myComponent.css. The problem is that I don't have one true theme, but 3 different, parallel themes.

import styles from '' // <<<< from what?

return `<div class=${styles.caption></div>`

Is it even possible to work with multiple layouts/themes when using CSS modules?


回答1:


You can dynamically import modules using commonjs syntax the following way.

config.js:
   export default function(key){
    var themes = ['themea','themeb','themec'];
    return themes[key] || theme[0];
   }

main.js:
   import getConfig from './config.js';
   var styles = require('../css/' + getConfig(2) + '.css');

Make sure to use babel-loader and style-loader/css-loader

I came up with the below code for dynamic file loading

var even = 0;
var themes = ['themea', 'themeb', 'themec'];
var currentConfig = 'themea';
require.context("../css", true, /.css$/)
var cache = Object.assign({}, require.cache);
require('../css/themea.css');
$('#small-button').click(function(){
  setConfig(themes[even]);
  even = (even+1) % 3;
});

function setConfig(config) {
  var modulename = '../css/' + currentConfig + '.css';
  Object.keys(require.cache).forEach(function(cacheKey) {
        Object.keys(cache).indexOf(cacheKey) < 0 ? delete require.cache[cacheKey] : null;
  });
  currentConfig = config;
  cache = Object.assign({}, require.cache);
  require('../css/' + currentConfig + '.css');
}



回答2:


If you bundle all 3 themes in one file. You can easily pick one of them and render component with it. You must have the same schema for all .css themes, for example:

.wrapper {
  // example content
}

.image {
  // example content
} 

In myComponent.js you will import all themes and assign to object (it will be easier to pick one of them):

import themeA from './themeA.css';
import themeB from './themeB.css';
import themeC from './themeC.css';

const themes = {
  light: themeA,
  dark: themeB,
  pink: themeC
}

Your themes will look something like this:

{ 
    light: {
        wrapper: "themeA---wrapper---2IVWH",
        image: "themeA---image---3omJ7"
    },
    dark: {
        wrapper: "themeB---wrapper---fHfAZ",
        image: "themeB---image---17erf"
    },
    pink: {
        wrapper: "themeC---wrapper---2i9L2",
        image: "themeC---image---3OKIG"
    }
}

Since css-modules are simple object with pointer to new class names you can dynamically pick one of them:

const render = themeName => {
  const theme = themes[themeName];
  return $(`
    <div class="${theme.wrapper}">
      <img 
          class="${theme.image}"
          src="http://exmoorpet.com/wp-content/uploads/2012/08/cat.png" 
      />

      <p>Lorem ipsum </p>
    </div>
  `);
};

I used jQuery only for simplicity of mockups. You can see all working code here: webpackbin


Load styles asynchronously in runtime (edit)

If you use require.ensure (great explanation here) you can download style in runtime. Change myComponent.js to async require:

import $ from 'jquery';

const render = (wrapper, theme) => {
  const template = $(`
    <div class="${theme.wrapper}">
      <img 
          class="${theme.image}"
          src="http://exmoorpet.com/wp-content/uploads/2012/08/cat.png" 
      />

      <p>Lorem ipsum </p> 
    </div>
  `);
  wrapper.html(template);
};

export default (wrapper, themeName) => {
  switch(themeName) { // this will produce 3 chunks with styles
    case 'light':
      require.ensure([], () => {
        render(wrapper, require('./themeA.css'));
      });
      break;
    case 'dark':
      require.ensure([], () => {
        render(wrapper, require('./themeB.css'));
      });
      break;
    case 'pink':
      require.ensure([], () => {
        render(wrapper, require('./themeC.css'));
      });
      break;
  }
};

Webpack will produce this chunks (1 main and 3 with styles):

chunk    {0} main.js (main) 267 kB [rendered]
    [0] ./src/main.js 827 bytes {0} [built]
    [1] ./~/jquery/dist/jquery.js 264 kB {0} [built]
    [2] ./src/select.js 440 bytes {0} [built]
    [3] ./src/myComponent.js 1.82 kB {0} [built]
chunk    {1} 1.1.js 10.2 kB {0} [rendered]
    [4] ./src/themeA.css 1.08 kB {1} [built]
    [5] ./~/css-loader?modules&localIdentName=[name]---[local]---[hash:base64:5]!./src/themeA.css 428 bytes {1} [built]
    [6] ./~/css-loader/lib/css-base.js 1.51 kB {1} {2} {3} [built]
    [7] ./~/style-loader/addStyles.js 7.21 kB {1} {2} {3} [built]
chunk    {2} 2.2.js 10.2 kB {0} [rendered]
    [6] ./~/css-loader/lib/css-base.js 1.51 kB {1} {2} {3} [built]
    [7] ./~/style-loader/addStyles.js 7.21 kB {1} {2} {3} [built]
    [8] ./src/themeB.css 1.08 kB {2} [built]
    [9] ./~/css-loader?modules&localIdentName=[name]---[local]---[hash:base64:5]!./src/themeB.css 429 bytes {2} [built]
chunk    {3} 3.3.js 10.2 kB {0} [rendered]
    [6] ./~/css-loader/lib/css-base.js 1.51 kB {1} {2} {3} [built]
    [7] ./~/style-loader/addStyles.js 7.21 kB {1} {2} {3} [built]
   [10] ./src/themeC.css 1.08 kB {3} [built]
   [11] ./~/css-loader?modules&localIdentName=[name]---[local]---[hash:base64:5]!./src/themeC.css 432 bytes {3} [built]

I will prove that 3 chunks with styles contain your theme styles. For example chunk 1 contains this code inside (I'm showing only important part of it):

/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {

    exports = module.exports = __webpack_require__(6)();
    // imports


    // module
    exports.push([module.id, ".themeA---wrapper---shnYu {\n  background-color: #eee;\n  color: #333;\n  padding: 20px;\n}\n\n.themeA---image---18Mgb {\n  float: left;\n  height: 100px;\n  margin: 20px;\n}\n", ""]);

    // exports
    exports.locals = {
        "wrapper": "themeA---wrapper---shnYu",
        "image": "themeA---image---18Mgb"
    };

How it looks in runtime

Here you can check new code it will even show ajax download chunks - you can try in console.




回答3:


If you want to do it in JavaScript, you could do a if/else or a switch statement.

var node = document.createElement("link").setAttribute("rel", "stylesheet") setAttribute("type", "text/css");

if (setting === 'A') {
    node.setAttribute("href", "layouts/themeA/myComponent.css");
} else if (setting === 'B') {
    node.setAttribute("href", "layouts/themeB/myComponent.css");
} else if (setting === 'C') {
    node.setAttribute("href", "layouts/themeC/myComponent.css");
}

document.getElementsByTagName('head').appendChild(node);

Or in jQuery :

var href;

if (setting === 'A') {
    href = "layouts/themeA/myComponent.css";
} else if (setting === 'B') {
    href = "layouts/themeB/myComponent.css";
} else if (setting === 'C') {
    href = "layouts/themeC/myComponent.css";
}

$('head').append('<link href="' + href +'" rel="stylesheet" type="text/css" />');


来源:https://stackoverflow.com/questions/38270362/css-modules-and-multiple-layouts-themes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!