Skip to content

Conversation

@YiSiWang
Copy link

@YiSiWang YiSiWang commented Aug 30, 2016

Problems in scoped CSS:

  1. Animation name conflict
  2. Only last css selector is scoped:
Child:
<template>
  <section>
    <div class="header">
      <p>I am red.</p>
    </div>
    <p>I am not.</p> <!-- this becomes red -->
  </section>
</template>
<style scoped>
  .header p { 
    color: red; 
  }
</style>

Parent:
<template>
  <section class="header"> <!-- Oops -->
    <child-component></child-component>
  </section>
</template>

CSS modules can solve these problems and is compatible with scoped.

Add CSS modules support

Basic

Simply use CSS modules with <style module="moduleName"> and class="moduleName.className"

<style module="style">
  .red { color: red; }
</style>
<template>
  <h2 class="style.red"></h2>
</template>

becomes:

<style module="style">
  ._8x_KsHmyrocTNd7akA_LL { color: red; }
</style>
<template>
  <h2 class="_8x_KsHmyrocTNd7akA_LL"></h2>
</template>

Class names are unique. So your style won't affect any other component.

Tips:

  1. With module, animation names are also converted. Feel free to write short class & animation names!
  2. With module + scoped, you can limit your style to your component more strictly.
  3. With module only, you can style <slot> in your component.

Binding classes

Static class names in binding expression are converted, too.

<template>
  <h3 class="{{ ['style.red'] }}"></h3>
  <h4 v-bind:class="['style.red']"></h4>
  <h5 :class="condition ? 'style.red' : ''"></h5>
  <h6 :class="['style.red', { 'style.red': isRed }, blue]"></h6>
</template>

becomes:

<template>
  <h3 class="{{ ['_8x_KsHmyrocTNd7akA_LL'] }}"></h3>
  <h4 v-bind:class="['_8x_KsHmyrocTNd7akA_LL']"></h4>
  <h5 :class="condition ? '_8x_KsHmyrocTNd7akA_LL' : ''"></h5>
  <h6 :class="['_8x_KsHmyrocTNd7akA_LL', { '_8x_KsHmyrocTNd7akA_LL': isRed }, blue]"></h6>
</template>

Note: Here, "static class name" means class name in single quotes.

Complex ones (eg. 'a' + 'b') are not supported (and not recommend in Vue).

Getting local class name in script

In some cases, you will like to control your class name in script.
You can do it like this:

<script>
  export default {
    loader: {
      // tell loader to inject `$styles` as a computed property.
      styles: '$styles'
    },
    computed: {
      className() {
        if (condition) {
          return this.$styles.moduleName.className
        }
        return 'something-else'
      }
    }
  }
</script>
<template>
  <h2 :class="className"></h2>
</template>

@iqingting
Copy link

👍 @yyx990803

@yyx990803
Copy link
Member

yyx990803 commented Aug 30, 2016

I think searching for static replacement in templates is inconsistent:

  • plain strings in template expressions will be magically converted
  • plain strings in JavaScript will not.

I think this can be quite confusing. Instead, I would:

  • always auto inject $styles if cssModule is used
  • just use the $styles variable for binding classes in all places.

@YiSiWang
Copy link
Author

  • $styles is not always needed. In most cases, we are just writing plain class name, or simple js expression in binding classes.
  • Always inject $styles will enlarge file size with useless codes.
  • Class name replacement is done in compile time, which provides compile time error rather than runtime.

@yyx990803
Copy link
Member

  • The API needs to be consistent and clear. Currently it feels hacky because it adds a lot of mental complexity to the usage:
    • the user now needs to be aware that the templates will go through a separate compilation process that rewrites static strings in template expressions, which is different from Vue's runtime behavior.
    • needs to be aware of edge cases like concatenated class names not supported in template expressions. This can actually be quite common e.g. :class="'button-' + type"
    • different usage when doing it inside JavaScript.
  • Not sure what errors can be caught when replacing class names...

To me it's much more important to have a simple mindset when using scoped CSS.

@YiSiWang
Copy link
Author

YiSiWang commented Aug 30, 2016

That's right. I will remove class replacement in js expressions templates.

@YiSiWang
Copy link
Author

YiSiWang commented Aug 31, 2016

Add CSS modules support

Simply use CSS Modules with <style module="moduleName">.

moduleName will be injected as a computed property.

Example:

<style module="style">
  .red { color: red; }
  /*
    becomes
    ._8x_KsHmyrocTNd7akA_LL { color: red; }
  */
</style>

<script>
  export default {
    ready() {
      console.log(this.style.red)
      // => _8x_KsHmyrocTNd7akA_LL
    }
  }
</script>

<template>
  <h2 v-bind:class="style.red"></h2>
</template>

@YiSiWang
Copy link
Author

YiSiWang commented Sep 1, 2016

Use scoped,lang and module combined:

<style scoped lang="stylus" module="style">
.red
  color: red
</style>

@YiSiWang
Copy link
Author

YiSiWang commented Sep 2, 2016

@yyx990803

@JounQin
Copy link

JounQin commented Sep 6, 2016

Do we need this actually?

If you need CSS Module support, just extract style file specifically and use css-loader to handler the styles then do this:

import classes from '/path/to/style'

export default {
    data() {
        classes
    }
}

Then, we could use classes.clazzz in the template!

So, on my opinion, this pr is unnecessary.

@YiSiWang
Copy link
Author

YiSiWang commented Sep 6, 2016

@JounQin

  1. scoped
  2. Single File Component
  3. There are problems using only scoped css

@JounQin
Copy link

JounQin commented Sep 6, 2016

@YiSiWang I think there is no need to use scoped with CSS Module nor pursue single file component so mightily which makes the component a bit more confusing. 😄

@speedornothing
Copy link

一个项目由大量组件组成,大量组件需要考虑内部样式的命名,这样 @JounQin 的方式会有些不便之处:

  • 对每个需要使用 CSS Modules 的组件,都需要编写一些重复代码,比如引入一个外部样式,然后把样式设置到组件的 data 属性中
  • 必须是 external css, 这和 .vue 单文件的方式不兼容,使用不方便

更倾向于 尤大 的建议( 使用 $styles ),另外对 @YiSiWang 的方法有个疑问,如果已有的某个 computed 属性与 style#module#name 相同会发生什么?

( 英语不好,打汉字不会被鄙视吧.. )

@YiSiWang
Copy link
Author

YiSiWang commented Sep 6, 2016

@speedornothing
注入的计算属性的名字是由<style module="xxx">决定的,也就是,由组件的维护者决定的,维护者当然也知道自己组件有哪些计算属性,因此命名冲突不是问题。

@yyx990803
Copy link
Member

yyx990803 commented Sep 8, 2016

@YiSiWang why do we need to explicitly declare the injected variable name? Using a reserved key like $styles seems simpler, and also it becomes more obvious when you are using the module class names.

How about something like <style module> with $styles as default?

@YiSiWang
Copy link
Author

YiSiWang commented Sep 8, 2016

@yyx990803 There might be multiple <style module> tags when use external css.

Though I don't think multiple moduled <style> tags are useful, it's worse to limit only one moduled tag or mix all local classes into $style.

@yyx990803
Copy link
Member

@YiSiWang I think we can default to $styles when the module attribute has no value. You can still use self-defined names if you need multiple style tags.

@YiSiWang
Copy link
Author

YiSiWang commented Sep 8, 2016

Great idea! I will work on it.

But, when use multiple no-module-value <style>s (let's call it default module), should it throw an error or mix their class names? I prefer the former.

@yyx990803
Copy link
Member

@YiSiWang yeah, throwing an error in that case sounds fine.

README.md Outdated
@@ -1,3 +1,36 @@
# Add CSS modules support
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this down to a sub-section instead of at the top?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, of course. It's just for reviewers to checkout the new API quickly.
Seems you are satisfied with current API, I will move them to the doc.

@YiSiWang
Copy link
Author

@yyx990803 Docs updated.

@yyx990803 yyx990803 merged commit 3fad1cf into vuejs:master Sep 14, 2016
@yyx990803
Copy link
Member

Thanks for the great work! Btw, would you be interested in porting this to v9.x? (next branch)

@YiSiWang
Copy link
Author

@yyx990803 Yes, I'm willing to do it.

Thanks for merging and HAPPY MOON FESTIVAL!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants