技术/前端/React
基本使用
不带括号的函数调用将被识别为标签模板字面量,其执行原理是:
所有静态字符串会被拆分出来合并成为数组, 作为第一个参数传入到目标函数,而内插(interpolation)表达式的值则会作为 rest 参数传入
入口
// @flow
import constructWithOptions from './constructWithOptions'
import StyledComponent from '../models/StyledComponent';
import domElements from '../utils/domElements';
import type { Target } from '../types';
// styled将StyledComponent与用户指定tag封装后返回
const styled = (tag: Target) => constructWithOptions(StyledComponent, tag);
// Shorthands for all valid HTML Elements
domElements.forEach(domElement => {
styled[domElement] = styled(domElement);
});
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
上定义了两个方法:withConfig
和attrs
,相当于增强的模板函数,可用来指定额外参数或者为组件指定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
是其内部定义的一个组件:
来看下这个组件是如何定义的:
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