简介
Vue
和React
是目前前端最火的两个框架。不管是面试还是工作可以说是前端开发者们都必须掌握的。
今天我们通过对比的方式来学习Vue
和React
的样式这一部分。
本文首先讲述了Vue
和React
支持书写样式的方式,然后分析了Vue
和React
的模块化方案,最后通过对比总结了它们之间的相同点和不同点。
希望通过这种对比方式的学习能让我们学习的时候印象更深刻,希望能够帮助到大家。
静态style
静态style
就是内联样式。
Vue
Vue
支持内联样式,并且和普通css
语法一样。
<div style="color: red; font-size: 18px">内联静态style</div>
React
但是React
不支持这种写法。在React
中style
接受一个采用小驼峰命名属性的 JavaScript
对象,而不是 CSS
字符串。这与 DOM
中 style
的 JavaScript
属性是一致的,同时会更高效的,且能预防跨站脚本(XSS)的安全漏。
<div style={{ color: "red", fontSize: "18px" }}>内联静态style</div>
例子渲染效果如下
动态style
动态style
就是样式能根据变量值动态改变。
Vue
在Vue
中,内联动态样式支持对象和数组两种方式。
样式属性名支持两种方式,可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名。
<template>
<div class="style-wrapper">
<div :style="styleObj">内联动态style</div>
</div>
</template>
<script>
import { defineComponent, ref, reactive } from "vue";
export default defineComponent({
setup() {
let styleObj = reactive({ "font-size": "18px", fontWeight: "bold" });
return { styleObj };
},
});
</script>
:style
的数组语法可以将多个样式对象应用到同一个元素上:
<div :style="[stylesObj1, stylesObj2]"></div>
在 :style
中使用需要一个 vendor prefix (浏览器引擎前缀) 的 CSS property 时,Vue
将自动侦测并添加相应的前缀。Vue
是通过运行时检测来确定哪些样式的 property 是被当前浏览器支持的。如果浏览器不支持某个 property,Vue 会进行多次测试以找到支持它的前缀。
React
在React
中,内联动态样式只支持对象方式,并且浏览器前缀也不会自动加,需要自己补充对应的样式属性。
样式属性名只能用驼峰式 camelCase 来命名。
import { Component } from "react";
export class Style1 extends Component {
constructor() {
super();
this.state = {
styleObj: { fontSize: "18px", fontWeight: "bold" },
};
}
render() {
return (
<div>
<div style={this.state.styleObj}>内联动态style</div>
</div>
);
}
}
export default Style1;
React
会自动添加 ”px” 后缀到内联样式为数字的属性后。如需使用 ”px” 以外的单位,请将此值设为数字与所需单位组成的字符串。例如:
// Result style: '10px'
<div style={{ height: 10 }}>
Hello World!
</div>
// Result style: '10%'
<div style={{ height: '10%' }}>
Hello World!
</div>
自动添加 ”px”这个在Vue
中是不支持的。
例子渲染效果如下
静态class
静态class
就是内联class
,是不需要动态改变的。
Vue
在Vue
中,可以直接在元素或子组件上定义class。
<template>
<div class="style-wrapper">
<div class="class1">静态class</div>
<StyleChild class="class2"></StyleChild>
</div>
</template>
<script>
import { defineComponent } from "vue";
import StyleChild from "@/components/StyleChild";
export default defineComponent({
components: {
StyleChild,
},
});
</script>
<style>
.class1 {
color: blue;
}
.class2 {
color: darkblue;
}
</style>
例子渲染效果如下
React
在React
中,类名需要使用className
来定义,并且不支持直接在子元素上定义className
。
import "../style/style.css";
...
render() {
return (
<div>
<div className="class1">静态class</div>
<StyleChild className="class2"></StyleChild>
</div>
)
}
例子渲染效果如下
可以看到子组件上并没有我们添加的样式。
动态 class
动态class
就是class
能根据变量值动态改变。
Vue
在Vue
中,动态class
支持对象、数组、字符串三种方式。并且可以任意搭配三目运算符使用。
<template>
<div class="style-wrapper">
<div :class="classStr">动态class</div>
<div :class="hasClass1 ? 'class1' : ''">动态class</div>
<div :class="{ class1: hasClass1, class2: hasClass2 }">动态class</div>
<div :class="[classStr, { class3: hasClass3 }, hasClass4 ? 'class4' : '']">
动态class
</div>
</div>
</template>
<script>
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
let classStr = ref("str1 str2");
let hasClass1 = ref(true);
let hasClass2 = ref(false);
let hasClass3 = ref(true);
let hasClass4 = ref(true);
return { classStr, hasClass1, hasClass2, hasClass3, hasClass4 };
},
});
</script>
<style>
.class1 {
color: blue;
}
.class2 {
color: darkblue;
}
.class3 {
font-size: 18px;
}
.class4 {
font-weight: bold;
}
</style>
例子渲染效果如下
React
在React
中,动态class
只支持字符串的方式。
import { Component } from "react";
export class Style1 extends Component {
constructor() {
super();
this.state = {
classStr: "str1",
};
}
render() {
return (
<div>
<div className={this.state.classStr + " str2"}>动态class</div>
</div>
);
}
}
export default Style1;
渲染效果如下
如果要使用对象或数组语法需要借助第三方插件classnames
import classNames from "classnames";
import "../style/style.css";
export class Style1 extends Component {
render() {
return (
<div>
{/* 动态多个class需要classnames */}
<div className={classNames("c1 c2")}>classnames使用1 </div>
<div className={classNames({ c1: true, c2: false })}>
classnames使用2 渲染出 c1
</div>
<div className={classNames({ c1: true }, { c3: true })}>
classnames使用3 渲染出 c1 c3
</div>
</div>
);
}
}
更多用法可以查看classnames文档,这里笔者就不细说了。
模块化css
模块化css
有两个优点,一是能让样式代码复用,减少构建后代码体积。其次能避免样式污染,样式只在引入的文件生效,不会影响其他页面。
在React
并没有给我们提供与 Vue scoped
或 Vue module
类似的特性,需要通过其他方式来实现 CSS
模块化。
scoped
在Vue
中 我们只要在style
标签内加上scoped
属性,该style
里面的样式只有在当前组件才会生效。
<template>
<div class="div1">div1</div>
<div class="div2">div2</div>
</template>
<style scoped>
.div1 {
color: red;
}
.div2 {
color: green;
}
</style>
其原理就是会在每个元素上加上该组件的唯一hash
值,作为一个自定义属性,然后样式类会自动和该属性选择器结合。
由于每个组件的hash
值是唯一的,所以样式就只会在当前组件生效。
那我们能使用/deep/
来进行样式穿透的原理是什么呢?
我们把上面的例子作为一个子元素,再创建一个新的父元素,在父元素里面来修改子元素的样式。
请注意,父元素要想穿透,也必须设置成
scoped
<template>
<div class="style-wrapper">
<Style></Style>
</div>
</template>
<style scoped>
/deep/ .div1 {
color: yellow;
}
</style>
可以看到,其实就是把父元素的唯一hash
值放到了子组件上。(这也印证了上面说的,要想穿透,父元素必须设置成scoped
,因为开启了scoped
才会有hash
值)
然后父元素样式就是该hash
值和子类名形成组合选择器。它会将子类样式直接覆盖。
module
还可以使用module
,功能类似,但是使用方式稍有差别。
<template>
<header :class="$style.class1">module 样式</header>
</template>
<style module>
.class1 {
color: red;
}
</style>
Vue
会默认会创建一个$style
的计算属性,如果想修改计算属性名称,可以给module
传递一个值。
<template>
<header :class="customStyle.class1">module 样式</header>
</template>
<style module="customStyle">
.class1 {
color: red;
}
</style>
module
的原理很简单,其实就是生成一个唯一的class
。比如我们上面的例子,实际类名如下
在React
中最常用的模块化方案有 CSS Modules
和 styled-components
两种。
CSS Modules
与 styled-components
是两种截然不同的 CSS
模块化方案,它们最本质的区别是:前者是在外部管理 CSS
,后者是在组件中管理 CSS
。下面笔者详细介绍下。
CSS Modules
CSS Modules
允许我们像 import
一个 JS Module
一样去 import
一个 CSS Module
。每一个 CSS
文件都是一个独立的模块,每一个类名都是该模块所导出对象的一个属性。通过这种方式,便可在使用时明确指定所引用的 CSS
样式。并且,CSS Modules
在打包时会自动将 id
和 class
混淆成全局唯一的 hash
值,从而避免发生命名冲突问题。
配置
以 webpack
构建工具为例 ,我们需要 css-loader
中配置modules: true
来开启的 modules
特性:
// webpack.config.js -> module.rules
{
test: /.(c|sa|sc)ss$/i,
exclude: /node_modules/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
// 开启 CSS Modules
modules: true,
// 借助 CSS Modules,可以很方便地自动生成 BEM 风格的命名
localIdentName: '[path][name]__[local]--[hash:base64:5]',
},
},
'postcss-loader',
'sass-loader',
],
},
使用
当我们使用CSS Modules
后,我们可以像引入js
一样引入css
然后使用。
/* style.css */
:global(.card) {
padding: 20px;
}
.article {
background-color: #fff;
}
.title {
font-size: 16px;
}
// App.js
import React from 'react'
import styles from './style.css'
export default function App() {
return (
<article className={styles.article}>
<h2 className={styles.title}>Hello World</h2>
<div className="card">global style</div>
</article>
)
}
VSCode 扩展支持
在 VSCode
中写 CSS Modules
代码,默认是没有智能提示功能的。可以安装 CSS Modules
扩展,这样就会有智能提示啦。
CSS-in-JS
CSS-in-JS
就是允许我们在js
中写css
。CSS-in-JS
库目前已有几十种实现,最流行的当属 styled-components
。
使用
首先需要安装
npm i styled-components
它使用 ES6
提供的模版字符串功能来构造“样式组件”。
import styled from "styled-components";
// 这里面的样式跟css一样,不用小驼峰
// 定义一个<h3></h3> 样式如下
const Title = styled.h3`
text-align: left;
color: palevioletred;
`;
export class Style1 extends Component {
render() {
return (
<div>
<Title>styled-components 跟h3使用方式一样</Title>
</div>
);
}
}
更多用法可以参看style-components,这里笔者就不细说了。
VsCode 扩展支持
在 VSCode
中写 CSS-in-JS
代码,默认是没有智能提示功能的。可以安装 vscode-styled-components
扩展,这样就会有智能提示啦。
对比总结
相同点
- 都支持静态动态
style
和静态动态class
写法。 - 都有对应的模块化方案,
vue
有内置的scoped
和module
,react
有比较常用的css modul
和styled components
。
不同点
- 静态
style
,在Vue
中和书写普通html
一样。但是在React
中,需要以对象的形式书写,并且属性需要采用小驼峰命名。 - 动态
style
,在Vue
中支持对象和数组的写法,而且属性名支持驼峰和横线分隔,并且浏览器前缀会自动补充。但是在React
中,只支持对象写法,而且属性名只支持驼峰,并且不会自动补充浏览器前缀,但是它会自动加上px
单位,这个是Vue
不支持的。 - 静态
class
,在Vue
中和书写普通html
一样。但是在React
中,需要以className
来定义class
。 - 动态
class
,在Vue
中支持字符串、对象、数组的方式。但是在React
中只支持字符串的写法,功能较弱,但是可以借助classnames
插件来增强。 - 在
React
模块化主要通过CSS Modules
和styled-components
来实现。但是在Vue
中都已经封装好了,直接使用即可。并且如果想要在Vue
中使用CSS in Js
可以借助第三方插件vue-styled-components来实现。
系列文章
Vue和React对比学习之生命周期函数(Vue2、Vue3、老版React、新版React)
Vue和React对比学习之组件传值(Vue2 12种、Vue3 9种、React 7种)
Vue和React对比学习之路由(Vue-Router、React-Router)
Vue和React对比学习之状态管理 (Vuex和Redux)
Vue和React对比学习之条件判断、循环、计算属性、属性监听
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!