?
首先拉取 styled-components 仓库,然后找到 /packages/styled-components 文件夹,核心代码都放在这个文件夹里;为了方便,后面会把 styled-components 简写为 sc。
往期 styled-components 文章:
《styled-components 深入浅出 (一) : 基础使用》
《styled-components 深入浅出 (二) : 高阶组件》
当我们对该项目有所了解的时候。
document.createElement
来创建 <style>
标签来实现样式化的,然后全局搜索 document.createElement
快速定位到。这种方式适合看具体单独的某个功能如何实现。对于 sc 这个项目我是采用两种方式结合使用。
一开始我就通过 document.createElement('style')
定位到 makeStyleTag
函数。代码所在文件:
代码非常简单易懂,这是去掉类型后的代码:
export const makeStyleTag = (target) => {
const head = document.head;
const parent = target || head;
const style = document.createElement('style');
// style 标签的插入位置
const prevStyle = findLastStyleTag(parent);
const nextSibling = prevStyle !== ?developer/article/2322549/undefined ? prevStyle.nextSibling : null;
// 自定义属性 data-set
style.setAttribute(SC_ATTR, SC_ATTR_ACTIVE);
style.setAttribute(SC_ATTR_VERSION, SC_VERSION);
const nonce = getNonce();
// 设置 style 标签的 nonce 属性:一种加密的随机数(一次使用的数字)
if (nonce) style.setAttribute('nonce', nonce);
// 在父节点里的最后一个子节点的位置插入新的 style 节点
parent.insertBefore(style, nextSibling);
return style;
};
我们可以看到,这个函数主要做了以下几件事:
document.head
<style>
标签,如果没有,则返回 null
<style>
标签,并设置了自定义属性<style>
标签的 nonce
属性<style>
标签插入到父节点的最后一个子节点的位置,然后返回 style 节点。通过第一篇文章,我们知道通过 styled.div
和 styled.div()
都能创建一个样式化的 <div>
标签,这是怎么回事?来看一个例子:
这其实是 ES6 的一个新语法:模板字符串,在这可以把它看做是一个函数,接受传参。
我们找到入口文件:packages/styled-components/src/index.ts,
import styled from './constructors/styled';
export * from './base';
export {
CSSKeyframes,
CSSObject,
CSSProp,
CSSProperties,
CSSPseudos,
DefaultTheme,
ExecutionContext,
ExecutionProps,
IStyledComponent,
IStyledComponentFactory,
IStyledStatics,
PolymorphicComponent,
PolymorphicComponentProps,
RuleSet,
Runtime,
StyledObject,
StyledOptions,
WebTarget,
} from './types';
export { styled as default, styled };
默认导出的 styled 函数是从 packages/styled-components/src/constructors/styled.tsx 导出的,找到它,去掉类型后的代码方便阅读。
import createStyledComponent from '../models/StyledComponent';
// HTML 标签列表
import domElements from '../utils/domElements';
import constructWithOptions from './constructWithOptions';
// 创建基础的 styled 方法
const baseStyled = (tag) =>
constructWithOptions(createStyledComponent, tag);
const styled = baseStyled;
// 实现通过 styled[domElement] 和 styled(domElement) 都能创建样式化组件
domElements.forEach(domElement => {
styled[domElement] = baseStyled(domElement);
});
export default styled;
我们看到通过 styled
函数是基础的 styled
方法:baseStyled
调用了 constructWithOptions
方法,找到 constructWithOptions
方法所在的文件 src/constructors/constructWithOptions.ts,去掉类型只留下关键的代码如下
import css from './css';
export default function constructWithOptions(componentConstructor, tag, options) {
const templateFunction = (initialStyles, ...interpolations) =>
componentConstructor(tag, options, css(initialStyles, ...interpolations));
// 返回样式化组件
return templateFunction;
}
constructWithOptions
函数的核心是 templateFunction
方法,它调用组件的构造方法 componentConstructor
返回一个样式化组件(携带样式的组件)。其实这个方法就是上面说到的 baseStyled 方法传入的 createStyledComponent
方法。该方法所在文件:src/models/StyledComponent.ts
为了方便理解,我画了个简单的函数调用关系图:
从这我们可以看出进来,当 sc 的使用者调用 styled()
这个api创建样式化组件时,本质上是通过 createStyledComponent
这个组件构造函数来实现的. 它返回一个 WrappedStyledComponent
组件,这个组件是通过 React.forwardRef
创建的,并且在返回之前会对 WrappedStyledComponent
组件添加一些属性。
我是 AndyHu,目前暂时是一枚前端搬砖工程师。
文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注呀?
未经许可禁止转载?
speak less,do more.