技术/前端/React

基本使用

carbon (2).png

不带括号的函数调用将被识别为标签模板字面量,其执行原理是:

所有静态字符串会被拆分出来合并成为数组, 作为第一个参数传入到目标函数,而内插(interpolation)表达式的值则会作为 rest 参数传入

入口

/constructors/styled.js

  1. // @flow
  2. import constructWithOptions from './constructWithOptions'
  3. import StyledComponent from '../models/StyledComponent';
  4. import domElements from '../utils/domElements';
  5. import type { Target } from '../types';
  6. // styled将StyledComponent与用户指定tag封装后返回
  7. const styled = (tag: Target) => constructWithOptions(StyledComponent, tag);
  8. // Shorthands for all valid HTML Elements
  9. domElements.forEach(domElement => {
  10. styled[domElement] = styled(domElement);
  11. });
  12. export default styled;

上面的代码很简单,大致流程就是首先引入模板函数和预定义StyledComponent组件,用来生成对应的styled组件。其中domElements就是原生的DOM元素标签,可参考文件 /utils/domElements.js ,遍历各个DOM原生元素,然后通过styled函数来生成对应的styled组件。
下面分析一下具体实现:

  • constructWithOptions
  • StyledComponent

constructWithOptions

源码文件/constructors/constructWithOptions.js

export default function constructWithOptions(
 componentConstructor: Function,
 tag: Target,
 options: Object = EMPTY_OBJECT
) {
const templateFunction = (...args) => componentConstructor(tag, options, css(...args));
return templateFunction;
}

另外,模板函数templateFunction上定义了两个方法:withConfigattrs,相当于增强的模板函数,可用来指定额外参数或者为组件指定attrs。其中componentConstructor即是StyledComponent
下面来看一下,tag和参数进入到StyledComponent后是如何处理。

StyledComponent

源码文件/models/StyledComponent.js
Line-234

export default function createStyledComponent(target: Target, options: Object, rules: RuleSet) {}

由此可见,constructWithOptions中的StyledComponent其实就是一个工厂函数,用来创建styled组件。
而创建组件的关键代码则为:

let WrappedStyledComponent;
const forwardRef = (props, ref) => (
   <ParentComponent {...props} forwardedComponent={WrappedStyledComponent} forwardedRef={ref} />
 );
forwardRef.displayName = displayName;
WrappedStyledComponent = React.forwardRef(forwardRef);

其中ParentComponent是其内部定义的一个组件:

styled-component-parentcomponent@2x.png

来看下这个组件是如何定义的:

class StyledComponent extends Component<*> {
    constructor() {
   super();
   this.renderOuter = this.renderOuter.bind(this);
   this.renderInner = this.renderInner.bind(this);
    }
    render() {
   return <StyleSheetConsumer>{this.renderOuter}</StyleSheetConsumer>;
 }
 renderOuter(styleSheet?: StyleSheet = StyleSheet.master) {
   this.styleSheet = styleSheet;
   // No need to subscribe a static component to theme changes, it won't change anything
   if (this.props.forwardedComponent.componentStyle.isStatic) return this.renderInner();
   return <ThemeConsumer>{this.renderInner}</ThemeConsumer>;
 }
}

该组件主要处理组件渲染上下文,可以实现定制化主题如theme/models/StyledComponent.js Line-106