前言
很多小伙伴在第一次尝试封装组件时会和我一样碰到许多问题,比如人家的组件会有 color 属性,我们在使用组件时传入组件文档中说明的属性值如 primary ,那么这个组件的字体颜色会变为 primary 对应的颜色,这是如何做到的?还有别人封装的组件类名都有自己独特的前缀,这是如何处理的呢,难道是 css 类名全部加上前缀吗,这也太麻烦了!
如果你正在困惑这些问题,你可以看看这篇文章。
我会参照antd的divider组件来讲述如何基于react封装一个组件,以及解答上述的一些问题,请耐心看完!
antd 的源码使用了 typescript 语法,因此不了解语法的同学要及时了解哦!
import * as react from 'react';import classnames from 'classnames';import { configconsumer, configconsumerprops } from '../config-provider';export interface dividerprops { prefixcls?: string; type?: 'horizontal' | 'vertical'; orientation?: 'left' | 'right' | 'center'; classname?: string; children?: react.reactnode; dashed?: boolean; style?: react.cssproperties; plain?: boolean;}const divider: react.fc<dividerprops> = props => ( <configconsumer> {({ getprefixcls, direction }: configconsumerprops) => { const { prefixcls: customizeprefixcls, type = 'horizontal', orientation = 'center', classname, children, dashed, plain, ...restprops } = props; const prefixcls = getprefixcls('divider', customizeprefixcls); const orientationprefix = orientation.length > 0 ? `-${orientation}` : orientation; const haschildren = !!children; const classstring = classnames( prefixcls, `${prefixcls}-${type}`, { [`${prefixcls}-with-text`]: haschildren, [`${prefixcls}-with-text${orientationprefix}`]: haschildren, [`${prefixcls}-dashed`]: !!dashed, [`${prefixcls}-plain`]: !!plain, [`${prefixcls}-rtl`]: direction === 'rtl', }, classname, 车同轨 ); return ( <div classname={classstring} {...restprops} role="parator"> {children && <span classname={`${prefixcls}-inner-texeverytime 太阳的后裔t`}>{children}</span>} </div> ); }} </configconsumer>);export default divider;
在源码中,最先看到的是以下内容,这些属性也就是divider组件所暴露的属性,我们可以<divider type='vertical' />
这样来传入 type 属性,那么 divider 分割线样式就会渲染为垂直分割线,是不是很熟悉!
export interface dividerprops { // interface 是 typescript 的语法 prefixcls?: string; type?: 'horizontal' | 'vertical'; // 限定 type 只能传入两个值中的一个 orientation?: 'left' | 'right' | 'center'; classname?: string; children?: react.reactnode; dashed?: boolean; style?: react.cssproperties; plain?: boolean;}
在上面的属性中,我们还发现 classname 和 style是比较常见的属性,这代表我们可以<divider type='vertical' classname='myclassname' style={{width: '1em'}} />
这样使用这些属性。
我们知道,antd 的组件类名会有他们独特的前缀ant-
,这是如何处理的呢?继续看源码韩国大学。
<configconsumer> {({ getprefixcls, direction }: configconsumerprops) => { const { prefixcls: customizeprefixcls, type = 'horizontal', orientation = 'center', classname, children, dashed, plain, ...restprops } = props; const prefixcls = getprefixcls('divider', customizeprefixcls);
从源码中,我们发现 prefixcls ,这里是通过 getprefixcls 方法生成,再看看 getprefixcls 方法的源码,如下。
export interface configconsumerprops { ... getprefixcls: (suffixcls?: string, customizeprefixcls?: string) => string; ...}const defaultgetprefixcls = (suffixcls?: string, customizeprefixcls?: string) => { if (customizeprefixcls) return customizeprefixcls; return suffixcls ? `ant-${suffixcls}` : 'ant';};
不难发现此时会生成的类名前缀为ant-divider
。
我们封装的组件肯定是有预设的样式,又因为样式要通过类名来定义,而我们传入的属性值则会决定组件上要添加哪个类名,这又是如何实现的呢?下面看源码。
import classnames from 'classnames';const classstring = classnames( prefixcls, `${prefixcls}-${type}`, { [`${prefixcls}-with-text`]: haschildren, [`${prefixcls}-with-text${orientationprefix}`]: haschildren, [`${prefixcls}-dashed`]: !!dashed, [`${prefixcls}-plain`]: !!plain, [`${prefixcls}-rtl`]: direction === 'rtl', }, classname,);return ( <div classname={classstring} {...restprops} role="parator"> {children && <span classname={`${prefixcls}-inner-text`}>{children}</span>} </div>);
我们发现,它通过 classnames 方法)定义了一个所有类名的常量,然后传给了 div 中的 classname 属性。
其实生成的类名也就是ant-divider-horizontal
这个样子,那么css中以此类名定义的样式也就自然会生效了。而 classname 和 style 属性则是通过{...restprops}
来传入。
最后我们再看看它的css样式代码是怎么写的!
antd 组件的样式使用 less 书写,不了解 less 语法的同学一定要了解一下。
@import '../../style/themes/index';@import '../../style/mixins/index';@divider-prefix-cls: ~'@{ant-prefix}-divider'; // 可以看到这里对应的也就是之前说到的类名前缀.@{divider-prefix-cls} { .ret-component(); border-top: @border-width-ba solid @divider-color; &-vertical { // 这里的完整类名其实就是 ant-divider-vertical, 也就是 divider 组件的 type 属性值为 v元宵节活动方案ertical 时对应的样式 position: relative; top: -0.06em; display: inline-block; height: 0.9em; margin: 0 8px; vertical-align: middle; border-top: 0; border-left: @border-width-ba solid @divider-color; } &-horizontal { display: flex; clear: both; width: 100%; min-width: 100%; margin: 24px 0; } &-horizontal&-with-text { display: flex; margin: 16px 0; color: @heading-color; font-weight: 500; font-size: @font-size-lg; white-space: nowrap; text-align: center; border-top: 0; border-top-color: @divider-color; &::before, &::after { position: relative; top: 50%; width: 50%; border-top: @border-width-ba solid transparent; // chrome not accept `inherit` in `border-top` border-top-color: inherit; border-bottom: 0; transform: translatey(50%); content: ''; } } &-horizontal&-with-text-left { &::before { top: 50%; width: @divider-orientation-margin; } &::after { top: 50%; width: 100% - @divider-orientation-margin; } } &-horizontal&-with-text-right { &::before { top: 50%; width: 100% - @divider-orientation-margin; } &::after { top: 50%; width: @divider-orientation-margin; } } &-inner-text { display: inline-block; padding: 0 @divider-text-padding; } &-dashed { background: none; border-color: @divider-color; border-style: dashed; border-width: @border-width-ba 0 0; } &-horizontal&-with-text&-dashed { border-top: 0; &::before, &::after { border-style: dashed none none; } } &-vertical&-dashed { border-width: 0 0 0 @border-width-ba; } &-plain&-with-text { color: @text-color; font-weight: normal; font-size: @font-size-ba; }}@import './rtl';
这样一来,我相信同学们也大概了解如何去封装一个组件以及关键点了,在源码中还有很多地方值得我们学习,比如这里的 configconsumer 的定义与使用,感兴趣的同学欢迎一起交流。
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注www.887551.com的更多内容!
本文发布于:2023-04-04 18:46:43,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/3fd1c1f3c5b40761593b8f66077a02b4.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:你知道怎么基于 React 封装一个组件吗.doc
本文 PDF 下载地址:你知道怎么基于 React 封装一个组件吗.pdf
留言与评论(共有 0 条评论) |