起源看到 earlyBirdCamp 用到了 tailwindcss,而後順勢瞭解一番css
若是打開 tailwindcss 的 github倉庫,大概會一臉蒙,怎麼 css 框架一堆 js ,並且 css 文件也是奇怪的atrule?能夠說,tailwindcss對 postcss 的使用真的上了一個層次,以前我也沒想到能夠這麼玩!經過 js 配置,postcss-js 和 postcss-nested 去解析咱們的配置,生成 node,而後最後再用 postcss 去生成 css,甚至還可使用 puregecss 去裁剪生成的 css ,或者用比較熟悉的話術說,就是能夠 tree shaking 咱們的 css,聽上去很高級的樣子。這裏不會細緻,大概挑幾個比較有特點的講一下。node
首先看下面三個比較經典的 atrule ,atrule
是 postcss 裏面 ast 的一個節點,表明 css中的@節點,其中後面跟隨的是這個節點的 params ,具體能夠去 astexplorer 以及結合 postcss 的api文檔去理解git
@tailwind base;
@tailwind components;
@tailwind utilities;
複製代碼
在 tailwindcss 裏面,這樣經典的節點,會起兩個做用,拿@tailwind base;
這個來講,postcss插件遇到這個 atrule 的時候,識別 params 是 base ,會把 pluginBase 中的節點內容生成 css ,同時會修復 source ,這樣 sourcemap 就不會出錯了,具體能夠看下面,能夠看到,如今 atRule 前面插入內容,移除這個 atRulegithub
css.walkAtRules('tailwind', atRule => {
if (atRule.params === 'preflight') {
// prettier-ignore
throw atRule.error("`@tailwind preflight` is not a valid at-rule in Tailwind v1.0, use `@tailwind base` instead.", { word: 'preflight' })
}
if (atRule.params === 'base') {
atRule.before(updateSource(pluginBase, atRule.source))
atRule.remove()
}
if (atRule.params === 'components') {
atRule.before(postcss.comment({ text: 'tailwind start components' }))
atRule.before(updateSource(pluginComponents, atRule.source))
atRule.after(postcss.comment({ text: 'tailwind end components' }))
atRule.remove()
}
if (atRule.params === 'utilities') {
atRule.before(postcss.comment({ text: 'tailwind start utilities' }))
atRule.before(updateSource(pluginUtilities, atRule.source))
atRule.after(postcss.comment({ text: 'tailwind end utilities' }))
atRule.remove()
}
if (atRule.params === 'screens') {
includesScreensExplicitly = true
atRule.before(postcss.comment({ text: 'tailwind start screens' }))
atRule.after(postcss.comment({ text: 'tailwind end screens' }))
}
})
if (!includesScreensExplicitly) {
css.append([
postcss.comment({ text: 'tailwind start screens' }),
postcss.atRule({ name: 'tailwind', params: 'screens' }),
postcss.comment({ text: 'tailwind end screens' }),
])
}
複製代碼
至於修復 sourcemap 指向,能夠看下面這段函數,讓新生成的全部節點都指向了原來 atRule 的source,完美~api
function updateSource(nodes, source) {
return _.tap(Array.isArray(nodes) ? postcss.root({ nodes }) : nodes, tree => {
tree.walk(node => (node.source = source))
})
}
複製代碼
Instead of writing a raw media query that duplicates that value like this:bash
@media (min-width: 640px) {
/* ... */
}
複製代碼
you can use the @screen directive and reference the breakpoint by name:app
@screen sm {
/* ... */
}
複製代碼
上面這個英文,是從文檔裏面拷貝出來的, 就是常見的媒體查詢,咱們能夠寫死到 css,可是若是過段時間又要改呢?這裏抽離出來到js配置裏面,咱們經過讀取配置文件,默認是 tailwind.config.js,裏面的 theme.screens 對應 atRule 中的 params 的值,生成咱們具體的 css,簡直不要太方便啊框架
import _ from 'lodash'
import buildMediaQuery from '../util/buildMediaQuery'
export default function({ theme }) {
return function(css) {
css.walkAtRules('screen', atRule => {
const screen = atRule.params
if (!_.has(theme.screens, screen)) {
throw atRule.error(`No \`${screen}\` screen found.`)
}
atRule.name = 'media'
atRule.params = buildMediaQuery(theme.screens[screen])
})
}
}
複製代碼
tailwindcss 插件的寫法,具體怎麼寫,這裏不具體展開,這裏講講原理。插件其實主要的做用就是把咱們寫的 js css 配置生成一個初步的 css,而後再根據配置文件,進行一個二次處理,最後生成實際的 css,拿一個內置的 zIndex 的插件當例子,主要是這個插件也寫了測試用例,結合文檔,介紹起來簡直不要太方便函數
首先咱們看看這個插件是怎麼寫的post
import _ from 'lodash'
import prefixNegativeModifiers from '../util/prefixNegativeModifiers'
export default function() {
return function({ addUtilities, e, theme, variants }) {
const utilities = _.fromPairs(
_.map(theme('zIndex'), (value, modifier) => {
return [
`.${e(prefixNegativeModifiers('z', modifier))}`,
{
'z-index': value,
},
]
})
)
addUtilities(utilities, variants('zIndex'))
}
}
複製代碼
其實咱們要關心的就是 addUtilities(utilities, variants('zIndex'))
這塊到底幹嗎了,簡單地說,就是 zIndex 塞到 pluginUtilities 裏面,也就是說,最後是對應 @tailwind utilities;
,能夠看看測試用例中生成的是什麼,以下,就是這個樣子,其中 utilities 還好理解,variants 是什麼?variants 的使用能夠看文檔的時候,說白了,就是會有 @variants responsive
把咱們utilities中生成的代碼包住,這樣就初步生成咱們的 css,而後 substituteResponsiveAtRules 這個會二次處理這個 atRule,生成最終的 css
expect(addedUtilities).toEqual([
{
utilities: {
'.-z-20': { 'z-index': '-20' },
'.-z-10': { 'z-index': '-10' },
'.z-10': { 'z-index': '10' },
'.z-20': { 'z-index': '20' },
},
variants: ['responsive'],
},
])
複製代碼
tailwindcss
還有不少玩法,具體能夠去文檔挖掘,這裏只是簡單說下它實現的原理,它給我帶來的震撼是,沒想到 postcss 或者說 css 還能夠這麼玩,腦洞太大了, ps: 爲了瞭解這貨,基本把主要依賴庫的文檔瞄了一遍,:逃)