前言
在上一章css in js 一次實踐中的末尾留了三個彩蛋:
1、styled.div & styled('div') 有什麼區別
2、模版字串中的樣式是怎麼解析的?為什麼可以巢狀?
3、為什麼會返回一個元件?
本章就從原始碼來簡單解釋一下。
第一個問題
先看包裡的package.json,找到main
欄位
// @flow
import type { Target } from '../types'
import domElements from '../utils/domElements'
export default (styledComponent: Function, constructWithOptions: Function) => {
const styled = (tag: Target) => constructWithOptions(styledComponent, tag)
// Shorthands for all valid HTML Elements
domElements.forEach(domElement => {
styled[domElement] = styled(domElement)
})
return styled
}
複製程式碼
可以看到,函式內定義了一個styled函式,接收一個Target型別的引數,這個Target型別定義是export type Target = string | ComponentType<*>
,而這個ComponentType就是從react包裡匯出的
type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
對應了React中兩種元件的定義方式(類定義和純函式定義)。接著看,註釋上說
對所有有效的html元素建立快捷鍵,這個docElements裡定一個所有的html元素,遍
歷這個docElements元素,將html元素新增為styled物件的屬性。至於這句
styled(domElement)
大家別忘了上面Target的型別定義裡前面還可以接收一個string
型別引數。至此,
第一個問題解決,styled.div 和 styled('div')沒有任何區別,styled.div只是style('div')的
快捷方式,但是 style.
形式的使用只支援html元素,對React元件不適用,
所以,在為元件定義樣式的時候還是使用styled(ReactComponent)。
第二個問題
ES6裡標籤模版的形式,函式即標籤,模版緊跟在標籤後面作為標籤的引數,也就是styled.div後面的模版字串裡的內容作為style.div函式的引數呼叫。 具體關於模版字串的內容請參考阮一峰的ES6模版字串
關於styled解決css巢狀問題,先貼一下css.js的程式碼:
// @flow
import interleave from '../utils/interleave'
import isPlainObject from '../utils/isPlainObject'
import { EMPTY_ARRAY } from '../utils/empties'
import flatten from '../utils/flatten'
import type { Interpolation, RuleSet, Styles } from '../types'
export default (
styles: Styles,
...interpolations: Array<Interpolation>
): RuleSet => {
if (typeof styles === 'function' || isPlainObject(styles)) {
// $FlowFixMe
return flatten(interleave(EMPTY_ARRAY, [styles, ...interpolations]))
}
// $FlowFixMe
return flatten(interleave(styles, interpolations))
}
複製程式碼
css這個函式是作為一個引數供style物件呼叫的,因為之間的關係太深,沒辦法貼出來,有興趣的小夥伴可以自己去看一下。
可以看到css.js返回了一個flatten函式,字面意思是拉伸、變平。這個函式的作用就是把巢狀的css變為一層,Array.flat()函式。
第三個問題
其實第三個問題大家應該都能想到,沒錯,就是HOC(高階元件),利用高階元件的Prop將style傳遞給children,類似於這種:
const StyledComponent = (options) => (Target) => {
return class extends React.Component{
static displayName = ...(styled.Target)
render(){
const {styles} = options;
const props = Object.assign({},this.props,{style:styles})
return <Target {...props}/>
}
}
}
複製程式碼
結尾
時間倉促,並沒有將這個包很詳細的講一下(其實我不會。。。),這個包裡還有很多很好玩的東西,比如觀察者模式、釋出訂閱模式、once函式、curry函式等等。寫這篇的主要目的就是想要看看一個包是怎麼實現的,它裡面用到的思想和技巧能夠對我們產生很大的啟發,所以我建議小夥伴們在使用一個第三方的時候儘可能的去看看人家是怎麼實現的?為什麼這麼實現?這能快速提高我們的思維方式和coding能力。
最後,如果我有寫的不對的地方,歡迎指正,謝謝。
祝生活愉快。