原文:Vue.js - Scoped Styles vs CSS Modules
作者:Michał Sajnóg 发表时间:Aug 28, 2018
译者:西楼听雨 发表时间: 2018/9/10
(转载请注明出处)

译者按:本文讲解的主要是 Scoped 样式和 CSS Module 的对比,对这两个概念已经熟悉的同学,同样也建议看一下,因为文中还提到了一些如 CSS Modules 的 “:export”概念,及 Scoped 样式存在一些缺陷,如果你对这些细节也已经熟知,那么请尽快离开这个页面,以免浪费您时间。

展开原文 It should not come as a surprise that CSS in modern web development is far from perfect. Nowadays, projects are usually quite complex and, given the global nature of styles, it’s extremely easy to end up with conflicting styles that override each other or that implicitly cascade down to elements we didn’t consider before.

The most commonly used solution that we use to reduce the main pain points is introducing the BEM (Block Element Modifier) methodology. However, it addresses only a small part of the bigger problem.

在现代化的 Web 开发中,CSS 还远未完美,这一点应该没有什么意外。现今的项目通常都相当复杂,而 css 样式天生又是全局性的,所以到最后总是极容易地就发生样式冲突——要么是样式相互覆盖,要么就是隐式地级联到了下面那些我们未考虑到的元素。

在减轻 CSS 存在的主要痛点方面,我们普遍采用的解决方案是引入 BEM (Block Element Modifier) 方法学。不过这只能解决我们这个大问题的很小一部分。

展开原文 Fortunately for us, the community already developed solutions that can help us deal with the problem more thoroughly. You might’ve already heard about [**CSS Modules**](https://github.com/css-modules/css-modules), **Styled Components**, **Glamorous** or[ **JSS**](http://cssinjs.org/) - these are just a few of the most popular tools that we can add to our projects today. If you're interested in the topic, you can check [this post](https://hackernoon.com/all-you-need-to-know-about-css-in-js-984a72d48ebc) - [Indrek Lasn](https://twitter.com/lasnindrek) explains the whole CSS-in-JS idea there very thoroughly.

Every new Vue.js application created by vue-cli comes with two great built-in solutions: Scoped CSS and CSS Modules. Both of them have some pros and cons, so let’s take a closer look and see which solution might be a better fit for your case.

我们非常幸运,社区已经开发出了一些解决方案,他们可以帮我们处理这些问题。说不定你已经听说过了 CSS Modules、Styled Components、Glamorous、JSS——这些只是众多流行的工具中的少数几个。如果你对这个话题感兴趣,你可以查看这篇帖文——作者 Indrek Lasn 对 CSS-in-JS 的思想做了非常详尽的讲解。

每个通过 vue-cli 创建的 Vue.js 应用都内置了两个很好的解决方案:Scoped CSS 和 CSS Modules (模块式 CSS)。两种方案各有优缺点,所以下面我们就仔细看下哪种方案在你的案例中更适用。

Scoped 样式

In order to get scoped styles working, we just have to add a scoped attribute to the **<style>** tag:

我们只需要在 <style> 标签上添加一个 scoped 属性即可启用 scoped 样式:

<template><button class=”button” />
</template><style scoped>.button {color: red;}
</style>
复制代码

It will apply our styles only to elements in the same component by using PostCSS and transforming the above example to the following:

这样就会使得我们的样式只被应用到这个组件中的元素上。这是借助 PostCSS 实现的,它会将上面的代码转换成下面这样:

<style>
.button[data-v-f61kqi1] {color: red;
}
</style><button class=”button” data-v-f61kqi1></button>
复制代码

As you can see, it requires no effort at all to have nicely scoped styles, and it also handles scoping tags’ styles in the same way.

就像你看到的这样,整个过程不需要做什么就可以达到很好的 scoped 样式效果。

Now, if you need to - let’s say - change the width of a component in a specific view, you can apply an extra class to it and style it as you normally would with all the benefits of scoped styles:

现在假设你需要调整一个视图中的某个组件的宽度,那么你可以像你平时那样做的一样:在这个组件上添加一个额外的 class 来设置其样式。

<template><BasePanel class=”pricing-panel”>content</BasePanel>
</template><style scoped>.pricing-panel {width: 300px;margin-bottom: 30px;}
</style>
复制代码

经转换后:

<style>.base-panel[data-v-d17eko1] {...}.pricing-panel[data-v-b52c41] {width: 300px;margin-bottom: 30px;}
</style><div class=”base-panel pricing-panel” data-v-d17eko1 data-v-b52c41>content
</div>
复制代码

展开原文 Once again - with no extra effort you’ve got full control over the layout.

However, be aware that this feature was introduced with one drawback - if your child component's root element has a class that also exists in the parent component, the parent component's styles will leak to the child. You can check out this CodeSandbox to get a better understanding of the problem.

Although it is not recommended and should be avoided - there are cases where we need to style something deeply inside our child component. For the sake of simplicity, let’s assume that our parent component should be responsible for the styling header of the BasePanel component. In scoped styles, the >>> combinator (also known as /deep/) comes in at this point.

这次还是一样,不需要做什么你就获得了对布局的彻底控制。

不过请注意:这个特性存在一个缺陷,即如果你子组件的根元素上有一个类已经在这个父组件中定义过了,那么这个父组件的样式就会泄露到子组件中。如果想更好地理解这个问题,可以查看这个 CodeSandbox 例子。

还有一些情况是我们需要对我们的子组件的深层结构设置样式——虽然这种做法并不受推荐且应该避免。为了简便起见,我们假设我们的父组件现在要对 BasePanel 的标题设置样式,在 scoped 样式中,这种情况可以使用 >>> 连接符(或者 /deep/ )实现。

<style scoped>.pricing-panel >>> .title {font-size: 24px;}
</style>
复制代码

经转换后:

.pricing-panel[data-v-b52c41] .title {font-size: 24px;
}
复制代码

Plain and simple, huh? But be aware that we just lost the encapsulation. Any .title class that will be used inside this component (even implicitly by a grandchild) will be affected by these styles.

非常简单,是吧?可是别忘记,我们却因此失去了组件的封装效果。这个组件内的所有的 .title 类的样式都会被这些样式所浸染——即便是孙节点。

模块式 CSS

CSS Modules gained their popularity due to the React community that quickly adopted this technology. Vue.js takes it to another level by combining its power with simplicity of use and out-of-the-box support by using vue-cli.

模块式 CSS 的流行源于 React 社区,它获得了社区的迅速的采用。Vue.js 更甚之,其强大、简便的特性在加上通过 vue-cli 对其开箱即用的支持,将其发展到另一个高度。

Now let’s look at how we can use it:

现在让我们来看下怎么使用它:

<style module>.button {color: red}
</style>
复制代码

What makes it so special and different from scoped styles is that all the created classes are accessible via the $style object inside the component. So in order to apply this class we have to use class binding:

这次我们使用的不是 scoped 属性,而是 module。这等于告诉 vue-template-compiler 和 vue-cli 的 webpack 配置要对这一部分采用哪些相应的 loader,进而生成像下面这样的 CSS:

.ComponentName__button__2Kxy {color: red;
}
复制代码

What makes it so special and different from scoped styles is that all the created classes are accessible via the $style object inside the component. So in order to apply this class we have to use class binding:

它的特殊之处以及和 scoped 样式不一样的地方就在于所有创建的类可以通过这个组件的 $style 对象获取。因此,要将这个类进行应用,我们需要像下面这样进行 class 绑定:

<template><button :class="$style.button" />
</template><style module>.button {color: red}
</style>
复制代码

这段代码将生成下面的 HTML 及相关的样式:

<style>.ComponentName__button__2Kxy {color: red;}
</style><button class=”ComponentName__button__2Kxy”></button>
复制代码

展开原文

The first benefit is that by looking at this element in our HTML we immediately know which component it belongs to. Secondly, everything becomes very explicit and we have full control - no magic whatsoever. However, we have to be careful while styling HTML tags, as they land in the final CSS as-is, as opposed to scoped styles, where even plain tags are scoped by the unique data attribute.

Similar to the second example from scoped styles, let’s see how we can style the component in a certain context:

它的第一点好处就是,当我们在 HMTL 中查看这个元素时我们可以立刻知道它所属的是哪个组件;第二点好处是,一切都变成显式的了,我们拥有了彻底的控制权——不会再有什么奇怪的现象了。和 scoped 样式那种把普通的标签也加上那些 data 属性的做法不一样,这些普通标签在转换后还是最初的样子。

比较 scoped 样式中的第二个例子,我们来看下我们可以怎么对那个组件设置样式:

<template><BasePanel :class="$style['pricing-panel']">content</BasePanel>
</template><style module>.pricing-panel {width: 300px;margin-bottom: 30px;}
</style>
复制代码

其转换后:

<style>.BasePanel__d17eko1 {/* some styles */}.ComponentName__pricing-panel__a81Kj {width: 300px;margin-bottom: 30px;}
</style><div class="BasePanel__d17eko1 ComponentName__pricing-panel__a81Kj">content
</div>
复制代码

It simply gets the job done, without any surprises! Moreover, because all classes are available through the $style object we can now pass them however deep we want using props, making it super easy to use a class in any place of the child component:

毫无意外,跟我们期望的结果一样。此外,因为所有的 CSS 类可以通过 $style 对象获取到,所以我们可以通过 props 将这些类传递到任何我们希望的深度中,这样,在子组件中的任意位置使用这些类就会变得极其容易:

<template><BasePaneltitle="Lorem ipsum":titleClass="$style.title">Content</BasePanel>
</template>
复制代码

CSS Modules have great interoperability with JS, and they do not limit you to classes. Using :export keyword, we can also export additional things to our $style object.

模块式 CSS 与 JS 有着很好的互操作性 (interoperability),这一点不只局限于 CSS 类。我们还可以使用 :export 关键字将其他的东西导出到 $style 对象上。

Imagine you have a chart to develop - you can keep your colour variables in CSS, and additionally export them for use in your component:

例如,想象一下你有一个图表需要开发 —— 你可以在 CSS 中定义你的色彩变量的同时将其导出,以供你的组件使用:

<template><div>{{ $style.primaryColor }}</div> <!-- #B4DC47 -->
</template><style module lang="scss">$primary-color: #B4DC47;:export {primaryColor: $primary-color}
</style>
复制代码

I only scratched the surface here - the CSS Modules concept is much broader and I encourage you to check out the full specification to know more.

对于模块式 CSS的概念,我这里还只是讲到了它的皮毛,它实际要宽泛的多,建议你查看下它完整的规范以了解更多。

总结

展开原文

Both solutions are very simple, easy to use and, to an extent, solve the same issue. Which one should you choose then?

Scoped styles require literally no extra knowledge to use and feel comfortable with. Their limitations also make them simple to use, and they're capable of supporting small to mid-sized applications.

However, when it comes to more complex scenarios and bigger apps, we probably want to be more explicit and have more control over what’s going on in our CSS. Even though using the $style object multiple times in a template might not look so sexy, it’s a small price to pay for the safety and flexibility it allows. We also get easy access to our variables (like colours or breakpoints) in JS, without having to keep separate files in sync.

Which one do you use? And why? Feel free to share any additional scenarios you encountered along the way!

其实两种方案都非常简单、易用,在某种程度上解决的是同样的问题。 那么你该选择哪种呢?

scoped 样式的使用不需要额外的知识,给人舒适的感觉。它所存在的局限,也正是它的使用简单的原因。它可以用于支持小型到中型的应用。

在更大的应用或更复杂的场景中,这个时候,对于 CSS 的运用,我们就会希望它更加显式,拥有更多的控制权。虽然在模板中大量使用 $style 看起来并不那么“性感”,但却更加安全和灵活,为此我们只需付出微小的代价。还有一个好处就是我们可以用 JS 获取到我们定义的一些变量(如色彩值、样式断点),这样我们就无需手动保持其在多个文件中同步。

[译] Vue: scoped 样式与 CSS Module 对比相关推荐

  1. scoped 不生效_解决vue scoped scss 无效的问题

    今天遇到scoped内部的scss设置无效,解决办法如下: /deep/ .position-el-steps { /deep/ .el-step.is-vertical { .el-step__de ...

  2. vue scoped 原理解析

    Vue scoped,原理,涉及到 vue-loader 的处理策略: 一.首先呢,是 VueLoaderPlugin 策略: VueLoaderPlugin 先获取了 webpack 原来的 rul ...

  3. class ts 扩展方法_如使用Typescript撸Vue(Vue2 + TS +TSX+CSS module)

    Vue对TS的支持一致不太好,连Vue作者尤大也自嘲真香压错了宝.期待Vue3.0会用TS重构且会有较大改进.不过目前有一些第三方的库可以曲线优化对TS的支持.主要就介绍下过下面两个库来写Vue. 总 ...

  4. Vue | 使用Vue脚手架 【脚手架的基本使用+ref属性+props属性+mixin混入+插件scoped样式+TodoList+浏览器本地存储+组件的自定义事件+全局事件总线+过度与动画】

    文章目录 脚手架的基本使用 初始化脚手架 分析脚手架结构 render函数 修改默认配置 ref属性 props属性 mixin混入 插件 scoped样式 Todo-list案例 组件化编码流程(通 ...

  5. [Vue]Scoped Css与Css Modules的区别

    均为解决CSS全局作用域问题(样式冲突(污染))的一个解决方案. 1.Scoped CSS 当 <style> 标签有 scoped 属性时,相当于在元素中添加了一个唯一属性用来区分. & ...

  6. vue组件样式scoped

    1.vue组件中的样式如果没加scrped,样式代表的是全局样式(避免组件之间样式的冲突).加了属性代表是模块化的. 其他组件引用button组件 上面分析了单个组件渲染后的结果,那么组件互相调用之后 ...

  7. vue scoped html样式无效

    1.问题场景 page1,page2都使用flexible移动端自适应的时候,有一个页面page2需要手动设置rem基准值, //手动设置基准 html{ font-size: 120px !impo ...

  8. vue的scoped 样式隔离,样式穿透,及细节

    先说环境吧:vue 3.2.45,也试过vue 3.2.16 vue的scoped样式隔离 原理: 在当前组件的.vue文件中,如果style标签加了scoped属性,那么在组件渲染为DOM时,会对每 ...

  9. jquery改变css样式和vue改变样式的区别

    jQuery做的就是操作dom节点,从而去改变css样式:而vue不用操作DOM,只关心数据,是从数据绑定的角度去改变样式的 先来看看jQuery,我用的是vue框架,所以先安装jQuery npm ...

最新文章

  1. 土地利用结构信息熵_科研成果快报第163期:基于市政用水时序数据的城市混合土地利用感知与分析...
  2. 字符设备驱动基础篇3——字符设备驱动工作原理
  3. 运维监控再添新品,F5联合智维数据推出应用质量主动拨测解决方案
  4. python-标示符和关键字
  5. MATLAB中矩阵与数组的区别,点运算符的运用
  6. C++ strlen和size的等价性
  7. CSS IE6躲猫猫Bug
  8. 手机锁屏密码忘记了怎么办,清除锁屏的办法
  9. rust如何加好友steam_《Rust》加入女性建模 角色性别将由STEAM账号决定
  10. 一种简单的图像显著性计算模型
  11. 最大功率点跟踪MPPT
  12. 报刊资料整理:云安全(cloud security)
  13. 前端实现搜索联想时防抖功能:
  14. js:网页中的高和宽(document)
  15. 从零开始学习3D可视化之数据对接(2)
  16. maven lastUpdated 文件清理脚本
  17. 2020年终总结!新的起航,新的征程
  18. 在ARM芯片中使用打印函数总结
  19. B数和B+树、红黑树数据结构的特点
  20. 以太坊(一)——概述

热门文章

  1. Linux编程(7)_gdb
  2. Webpack4 学习笔记 - 02:loader 打包静态资源(图片)
  3. RuoYiConfig中加入自定义属性值获取不到解决办法?
  4. 利用Linux搭建SVN服务器,详细过程
  5. 第2章[2.7] Ext JS数据模型与数据封装
  6. EMC-- DFC --Sessions and Session Managers
  7. php判断字段重复,删除完全重复和部分关键字段重复的记录
  8. Python中判断字符串中是否包含另一个字符串
  9. unity 关闭自己脚本_太可了!这些领域可以把Unity玩的那么好,带你开启新世界...
  10. Oracle dbms_job管理