Vue和React对比学习之css以及相应的模块化方案

简介: 模块化方案

简介

VueReact是目前前端最火的两个框架。不管是面试还是工作可以说是前端开发者们都必须掌握的。

今天我们通过对比的方式来学习VueReact的样式这一部分。

本文首先讲述了VueReact支持书写样式的方式,然后分析了VueReact的模块化方案,最后通过对比总结了它们之间的相同点和不同点。

希望通过这种对比方式的学习能让我们学习的时候印象更深刻,希望能够帮助到大家。

静态style

静态style就是内联样式。

Vue

Vue支持内联样式,并且和普通css语法一样。

<div style="color: red; font-size: 18px">内联静态style</div>

React

但是React不支持这种写法。在Reactstyle 接受一个采用小驼峰命名属性的 JavaScript 对象,而不是 CSS 字符串。这与 DOM 中 style 的 JavaScript 属性是一致的,同时会更高效的,且能预防跨站脚本(XSS)的安全漏。

<div style={{ color: "red", fontSize: "18px" }}>内联静态style</div>

例子渲染效果如下

image.png

动态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中是不支持的。

例子渲染效果如下

image.png

静态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>

例子渲染效果如下

image.png

React

React中,类名需要使用className来定义,并且不支持直接在子元素上定义className

import "../style/style.css";

...

render() {
  return (
    <div>
      <div className="class1">静态class</div>
      <StyleChild className="class2"></StyleChild>
    </div>
  )
}

例子渲染效果如下

image.png

可以看到子组件上并没有我们添加的样式。

动态 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>

例子渲染效果如下

image.png

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;

渲染效果如下

image.png

如果要使用对象或数组语法需要借助第三方插件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值,作为一个自定义属性,然后样式类会自动和该属性选择器结合。

image.png

image.png

image.png

由于每个组件的hash值是唯一的,所以样式就只会在当前组件生效。

那我们能使用/deep/来进行样式穿透的原理是什么呢?

我们把上面的例子作为一个子元素,再创建一个新的父元素,在父元素里面来修改子元素的样式。

请注意,父元素要想穿透,也必须设置成 scoped
<template>
  <div class="style-wrapper">
    <Style></Style>
  </div>
</template>

<style scoped>
/deep/ .div1 {
  color: yellow;
}
</style>

可以看到,其实就是把父元素的唯一hash值放到了子组件上。(这也印证了上面说的,要想穿透,父元素必须设置成scoped,因为开启了scoped才会有hash值)

image.png

然后父元素样式就是该hash值和子类名形成组合选择器。它会将子类样式直接覆盖。

image.png

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。比如我们上面的例子,实际类名如下

image.png

React中最常用的模块化方案有 CSS Modulesstyled-components 两种。

CSS Modulesstyled-components 是两种截然不同的 CSS 模块化方案,它们最本质的区别是:前者是在外部管理 CSS,后者是在组件中管理 CSS。下面笔者详细介绍下。

CSS Modules

CSS Modules 允许我们像 import 一个 JS Module 一样去 import 一个 CSS Module。每一个 CSS 文件都是一个独立的模块,每一个类名都是该模块所导出对象的一个属性。通过这种方式,便可在使用时明确指定所引用的 CSS 样式。并且,CSS Modules 在打包时会自动将 idclass 混淆成全局唯一的 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中写cssCSS-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 扩展,这样就会有智能提示啦。

对比总结

相同点

  1. 都支持静态动态style和静态动态class写法。
  2. 都有对应的模块化方案,vue有内置的scopedmodulereact有比较常用的css modulstyled components

不同点

  1. 静态style,在Vue中和书写普通html一样。但是在React中,需要以对象的形式书写,并且属性需要采用小驼峰命名。
  2. 动态style,在Vue中支持对象和数组的写法,而且属性名支持驼峰和横线分隔,并且浏览器前缀会自动补充。但是在React中,只支持对象写法,而且属性名只支持驼峰,并且不会自动补充浏览器前缀,但是它会自动加上px单位,这个是Vue不支持的。
  3. 静态class,在Vue中和书写普通html一样。但是在React中,需要以className来定义class
  4. 动态class,在Vue中支持字符串、对象、数组的方式。但是在React中只支持字符串的写法,功能较弱,但是可以借助classnames插件来增强。
  5. React模块化主要通过CSS Modulesstyled-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对比学习之Style样式

Vue和React对比学习之Ref和Slot

Vue和React对比学习之Hooks

Vue和React对比学习之路由(Vue-Router、React-Router)

Vue和React对比学习之状态管理 (Vuex和Redux)

Vue和React对比学习之条件判断、循环、计算属性、属性监听

后记

感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!

相关文章
|
2月前
|
前端开发 JavaScript
React学习之——条件渲染
【10月更文挑战第16天】React 中没有像Vue中v-if这种指令。React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符 if 或者条件运算符去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。
|
3月前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
89 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
3月前
|
前端开发
学习react基础(2)_props、state和style的使用
本文介绍了React中组件间数据传递的方式,包括props和state的使用,以及如何在React组件中使用style样式。
41 0
|
1月前
|
前端开发
create-react-app 如何使用 less/sass 和 react-css-modules?
本文详细介绍了在 create-react-app 项目中如何使用 less/sass 和 react-css-modules 来管理和应用样式。首先,通过安装相应依赖并配置 webpack 支持 less/sass;接着,介绍如何使用这些预处理器编写样式;最后,讲解了如何配置和使用 react-css-modules 实现样式的模块化管理,以及如何结合使用 less/sass 和 react-css-modules 提高开发效率和代码质量。
32 3
|
1月前
|
前端开发 JavaScript 安全
学习如何为 React 组件编写测试:
学习如何为 React 组件编写测试:
40 2
|
2月前
|
前端开发 JavaScript
React Tailwind CSS
10月更文挑战第12天
20 1
|
2月前
|
资源调度 前端开发 JavaScript
React进阶学习
React进阶学习
16 1
|
2月前
|
设计模式 JavaScript 开发工具
Vue开发中使用好钩子方法(hook method)可以使你的代码更加模块化和可维护
Vue开发中使用好钩子方法(hook method)可以使你的代码更加模块化和可维护
22 0
|
2月前
|
前端开发 容器
React 使用 CSS 样式
10月更文挑战第12天
40 0
|
2月前
|
JSON 前端开发 JavaScript
React 进阶阶段学习计划
React 进阶阶段学习计划