提问 发文

如何在react组件中创建动态的伪元素和伪类

赵炎飞

| 2023-06-22 23:25 349 0 0

在使用easyv组件开发工具开发react组件的时候,有时我们需要用伪元素和伪类来达到一些效果,比如:hover,::after等,但是这些css代码只能写在css文件中,不能在js代码中直接编写,没办法动态修改样式。下面我会介绍几种比较原始的方法,让开发者在不使用第三方库的情况下来动态修改伪元素和伪类的样式。

一、利用css的变量函数 var()或attr()来实现。

先看以下代码:

index.jsx:

import css from './styles/index.module.css';

export default function (props) {
const { left, top, width, height, id, configuration } = props;
const { hoverColor, afterContent } = configuration;

const styles={
position:"absolute",
left, top, width, height
}

return (
<div className="__easyv-component" style={styles} id={id}>
<div className={css.test}>按我</div>
</div>
)
}

main.json:

{
"base": {
"name": "测试",
"module_name": "test",
"version": "1.0.0",
"show": 1
},
"width": 200,
"height": 70,
"configuration": [
{
"name":"hoverColor",
"displayName":"悬浮色",
"type":"color",
"value":"red"
},
{
"name":"afterContent",
"displayName":"伪元素文本",
"type":"input",
"value":"text"
}
]
}

index.module.css:

.test{
width:100px;
height:60px;
background:blue;
}

        这里我们创建了一个简单的函数式组件,hoverColor对应悬浮时的组件背景色,afterContent对应组件的伪元素 ::after 的内容,可以看到,我们的css文件也非常简单,只是给test类的元素赋予了一定的宽高和背景色。

        那么接下来,如果我们要给test添加一个hover样式,正常来讲,我们会在css文件中这样写:

.test:hover{
background:red;
}
        这样可以给test添加一个固定的hover背景色,但是没办法动态修改,如果想动态修改,可以将该部分css代码修改如下:
.test:hover{
background:var(--color);
}
        这里我们引用了一个css变量--color,表示test的hover背景色是由--color决定的,这样当我们修改--color的值时,背景色也就会跟着动态改变了。那么,我们要怎么修改--color的值呢?很简单,在index.jsx中可以直接修改:
import css from './styles/index.module.css';

export default function (props) {
const { left, top, width, height, id, configuration } = props;
const { hoverColor, afterContent } = configuration;

const styles={
position:"absolute",
left, top, width, height
}
//btnStyle不一定非得给className=test的元素,给className=__easyv-component的元素也有效
const btnStyle={
"--color":hoverColor //此处根据配置项中的hoverColor来给--color赋值,达到动态修改--color的效果
}

return (
<div className="__easyv-component" style={styles} id={id}>
<div className={css.test} style={btnStyle}>按我</div>
</div>
)
}
        此这般,我们就达到了再jsx代码中动态控制hover样式的目的。对于伪元素after也是如此,但是有个需要注意的地方是,伪元素的content属性无法赋予var变量(content:var(xxx)这种写法是不生效的,此处存疑,因为在开发者工具中这样确实是不生效的,但是我用静态的html页面试了一下,好像是可以生效的),所以这边我们需要借用attr()函数,来给content赋值。
index.module.css:
.test::before{
content:attr(data-txt);
position:absolute;
right:0;
width:60px;
height:60px;
background: var(--color);
}

index.jsx:

import css from './styles/index.module.css';

export default function (props) {
const { left, top, width, height, id, configuration } = props;
const { hoverColor, afterContent } = configuration;

const styles={
position:"absolute",
left, top, width, height
}
//btnStyle不一定非得给className=test的元素,给className=__easyv-component的元素也有效
const btnStyle={
"--color":hoverColor //此处根据配置项中的hoverColor来给--color赋值,达到动态修改--color的效果
}

return (
<div className="__easyv-component" style={styles} id={id}>
{/* 这里给test添加一个自定义的属性data-txt,它的值由afterContent修改,
css里会获取到data-txt属性的值并赋予content */}
<div className={css.test} data-txt={afterContent} style={btnStyle}>按我</div>
</div>
)
}

二、动态生成<style>标签

        在html页面中,<style>标签内可以书写css代码,这也意味着如果我们可以动态创建style标签,也就能动态创建伪元素和伪类。只要做好<style>标签的管理,避免样式覆盖或内存泄漏等问题,就可以用这个方法来解决任何js动态控制css样式的问题。我们可以通过以下几个步骤来动态生成style标签:

1.使用document.createElement("style")来创建style标签,这里需要先验证一下,是否已创建过style标签,如果已创建过了,就不必二次创建了。

2.将创建好的style标签插入到<head>标签中,使其生效,同时将style标签绑定到react的ref上,这样后续可以通过ref来修改style标签的内容,也能通过ref来判断是否已创建过style标签。

3.通过修改style标签的innerHTML的值来动态生成样式,这里我们可以利用模板字符串,书写css样式和插入js变量会更方便。

4.在组件卸载时,需要将生成的style标签移出dom,避免内存泄漏。

        下面是示例代码,运行后和方法一的效果是一样的。

import React,{useEffect, useRef} from "react";

export default function(props) {
const { left, top, width, height, id, configuration } = props;
const { hoverColor, afterContent } = configuration;

const styles={
position:"absolute",
left, top, width, height
}

const styleRef = useRef(); //这个ref用于绑定style标签的dom实例
//这个useEffect用于监听配置项,当配置项变化时,需要动态修改style标签的innerHTML
useEffect(()=>{
//先对styleRef进行判断,如果styleRef未绑定style标签,则需要生成一个新的style标签并绑定
if(!styleRef.current){
let styleDom = document.createElement("style");
document.head.appendChild(styleDom);
styleRef.current = styleDom;
}
//这一步动态生成css样式,部分不需要动态生成的样式其实可以写到css文件里,比如下面的.test
//这里我偷懒了,直接写一块儿了。
styleRef.current.innerHTML=`
.test{
width:100px;
height:60px;
background:blue;
}
.test:hover{
background:${hoverColor};
}
.test::before{
content:"${afterContent}";
position:absolute;
right:0;
width:60px;
height:60px;
background: ${hoverColor};
}
`;
},[hoverColor, afterContent]);
//这个useEffect用于将style标签从dom中移除。避免内存泄漏
useEffect(()=>{
return ()=>{
if(styleRef.current){
styleRef.current.remove();
}
}
},[]);

return (
<div className="__easyv-component" style={styles} id={id}>
<div className="test">按我</div>
</div>
)
}

三、利用useInsertionEffect来动态插入<style>标签

        方法二其实是有弊端的,我们都知道useEffect是异步执行的,这意味着如果线程被阻塞了,style标签将无法被及时插入,样式的修改会很慢。所以我们可能会更期望样式的变化在dom变化之前完成,而react官方给出的解决方案就是useInsertionEffect这个hook,它的用法和useEffect类似,但执行时机是在dom变化之前(比useLayoutEffect的时机更加靠前,useLayoutEffect是在dom变化之后,组件渲染之前),所以用它来插入style标签可以避免阻塞问题。不过这个属于react18新特性,有兴趣的同学可以自行了解。

总结:

        以上三种方法个人还是比较推荐第一种,没有太多风险和性能浪费,其次是第三种,不过需要用到react18,如果是旧版本的react的话,还是用第二种好了,不过不是特别推荐,毕竟频繁的修改style标签内容,也是一种性能的浪费。

收藏 0
分享
分享方式
微信

评论

游客

全部 0条评论

19

文章

1.91K

人气

6

粉丝

1

关注

官方媒体

轻松设计高效搭建,减少3倍设计改稿与开发运维工作量

开始免费试用 预约演示

扫一扫关注公众号 扫一扫联系客服

©Copyrights 2016-2022 杭州易知微科技有限公司 浙ICP备2021017017号-3 浙公网安备33011002011932号

互联网信息服务业务 合字B2-20220090

400-8505-905 复制
免费试用
微信社区
易知微-数据可视化
微信扫一扫入群