- 原文地址:The magic behind ? styled-components
- 原文作者:mxstbr.blog
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:WangLeto
- 校對者:kezhenxu94,ziyin feng
如果你不曾瞭解 styled-components
,下面是 styled component 中定義 React 元件的形式:
const Button = styled.button`
background-color: papayawhip;
border-radius: 3px;
color: palevioletred;
`
複製程式碼
你可以用 Button
變數來渲染元件,就如同其他任何 React 元件一樣。
<Button>Hi Dad!</Button>
複製程式碼
所以原理是什麼?你覺得是哪種 webpack、babel 之類的神奇轉譯器能做到這樣?
標籤模板字串(Tagged Template Literals)
實際上,styled.button``
這種古怪的宣告,是 JavaScript 語法的一部分!這是一種叫做“標籤模板字串”的特性,在 ES6 中引入。
本質上來說,呼叫函式 styled.button()
和使用 styled.button``
幾乎是一回事!但是當你傳入引數時就會看到不同之處了。
我們先建立一個簡單的函式用於探索:
const logArgs = (...args) => console.log(...args)
複製程式碼
這個函式會輸出呼叫時傳入的引數,別的什麼都不做。
你可以在(任何現代瀏覽器)控制檯中,貼上上面的函式,然後執行接下來的程式碼,來跟隨我的分析。
一個簡單的使用例子:
logArgs('a', 'b')
// -> a b
複製程式碼
->
在本文中表示輸出內容
現在,試著用標籤模板字串來呼叫它:
logArgs``
// -> [""]
複製程式碼
只列印出來一個陣列,裡面有且僅有一個空字串。有趣!當傳入一個簡單的字串進去又會發生什麼呢?
logArgs`I like pizza`
// -> ["I like pizza"]
複製程式碼
好吧,所以這個陣列的第一個元素正是傳入的字串,不管裡面是什麼內容。那為什麼還要搞個陣列出來呢?
插值
模板字串可以進行插值,類似於:`I like ${favoriteFood}`
。讓我們將一個模板字串作為引數,使用小括號呼叫 logArgs
:
const favoriteFood = 'pizza'
logArgs(`I like ${favoriteFood}.`)
// -> I like pizza.
複製程式碼
如你所見,JavaScript 繼續執行,將插入字串的值放入字串,然後傳遞給了函式。那麼我們直接使用模板字串來呼叫 logArgs
呢?
const favoriteFood = 'pizza'
logArgs`I like ${favoriteFood}.`
// -> ["I like ", "."] "pizza"
複製程式碼
開始有趣起來了:可以看到,我們不再僅僅是得到了一個內容為 "I like pizza"
的字串(像我們使用小括號呼叫的時候)。
傳入引數的第一位仍然是陣列,不過現在有了 2 個元素:位於插值左側的 I like
,作為陣列第一個元素;位於插值的右側的 .
,是陣列第二個元素。插值內容 favoriteFoor
成為了第二個傳入引數。
可以看到,差別在於當我們使用標籤模板字串呼叫 logArgs
時,模板字串被分解了,首先是原始文字組成的陣列,然後是插值。
如果我們插入不止一個變數呢,你能猜到嗎?
const favoriteFood = 'pizza'
const favoriteDrink = 'obi'
logArgs`I like ${favoriteFood} and ${favoriteDrink}.`
// -> ["I like ", " and ", "."] "pizza" "obi"
複製程式碼
每個插入的變數,都成為了呼叫函式傳入的下個引數。你儘可以插入新的變數,會一直向後繼續!
與通常呼叫函式的方法比較一下:
const favoriteFood = 'pizza'
const favoriteDrink = 'obi'
logArgs(`I like ${favoriteFood} and ${favoriteDrink}.`)
// -> I like pizza and obi.
複製程式碼
我們僅僅得到了一個長字串,所有東西都揉在一起了。
為什麼這很有用?
哎呦不錯哦,這樣我們就能用用重音符(`
)呼叫函式了,而且傳參也別具一格,哇哦 —— 不過這又有什麼了不起的?
好吧,事實證明可以用它進行一些很酷的探索。我們將 styled-components
作為案例,分析一下。
對於 React 元件,你希望使用 props 值調整他們的樣式。比如我們通過傳入一個 primary
的 prop 值,讓 <Button />
元件變大一些,像這樣:<Button primary />
。
當你使用 styled-components
傳入一個插值函式,我們其實就向元件傳入了一個 props
,使用它就可以進行元件樣式調整。
const Button = styled.button`
font-size: ${props => props.primary ? '2em' : '1em'};
`
複製程式碼
現在如果 Button
是個基本按鈕(primary),就有 2em 大小的字型,否則為 1em。
// font-size: 2em;
<Button primary />
// font-size: 1em;
<Button />
複製程式碼
回頭看一眼 logArgs
函式。我們嘗試使用插值函式呼叫它,就像上面 styled.button
一樣,只不過我們沒有使用插值模板字串。我們傳入什麼呢?
logArgs(`Test ${() => console.log('test')}`)
// -> Test () => console.log('test')
複製程式碼
函式被 toString
轉化了,logArgs
獲取到一個字串,看上去就是:"Test () => console.log('test')"
。(注意現在只是一個字串,不是真的函式)
比較一下直接使用插值模板字串呼叫:
logArgs`Test ${() => console.log('test')}`
// -> ["Test", ""] () => console.log('test')
複製程式碼
我知道上面的文字現在還是不明顯,但是我們拿到的第二個傳入引數確實是個函式了!(不僅是函式宣告時的字串)在你的控制檯多試幾次,仔細觀察,來更好地感受它。
這表示我們現在能夠拿到函式了,也能直接執行它!為了深入測試,讓我們來建立一個稍有不同的函式,它可以執行所有傳入引數中的函式:
const execFuncArgs = (...args) => args.forEach(arg => {
if (typeof arg === 'function') {
arg()
}
})
複製程式碼
當呼叫這個函式時,它會忽略所有不是函式的引數,但是如果傳入引數是函式,它就會執行這個函式:
execFuncArgs('a', 'b')
// -> undefined
execFuncArgs(() => { console.log('this is a function') })
// -> "this is a function"
execFuncArgs('a', () => { console.log('another one') })
// -> "another one"
複製程式碼
讓我們試著用小括號包裹著模板字串來再呼叫一次:
execFuncArgs(`Hi, ${() => { console.log('Executed!') }}`)
// -> undefined
複製程式碼
什麼都沒發生,因為 execFuncArgs
沒有被傳入函式。它不過得到了一個字串:"Hi, () => { console.log('I got executed!') }"
。
現在看一下,當我們使用標籤模板字串呼叫函式會發生什麼:
execFuncArgs`Hi, ${() => { console.log('Executed!') }}`
// -> "Executed!"
複製程式碼
與之前相比,execFuncArgs
獲得的第二個引數是一個真正的函式,並且執行了它。
styled-components
底層就是這麼做的!在渲染時,我們向所有插值函式中傳入 props,以便使用者可以基於 props 修改樣式。
標籤模板字串使得 styled-components
API 得以實現,沒有這個特性 styled-compnents
就不可能出現。期待大家能以不同的方式利用標籤模板字串!
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。