important 修饰符
可以通过在开头添加 ! 字符
<p class="!font-medium font-bold">
This will be medium even though bold comes later in the CSS.
</p>
! 始终位于任何变体之后,但在任何前缀之前:
<div class="sm:hover:!tw-font-bold">
content
动态 class
如果您使用字符串插值或将部分类名连接在一起,Tailwind 将无法找到它们,因此不会生成相应的 CSS
始终使用完整的类名
<div class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>
总是将 props 映射到静态类名
function Button({ color, children }) {
const colorVariants = {
blue: 'bg-blue-600 hover:bg-blue-500',
red: 'bg-red-600 hover:bg-red-500',
}
return (
<button className={`${colorVariants[color]} ...`}>
{children}
</button>
)
}
使用第三方库
如果您已经创建了自己的组件,这些组件使用 Tailwind 进行样式化,并将它们导入到多个项目中,请确保配置 Tailwind 以扫描这些组件的类名。
module.exports = {
content: [
'./components/**/*.{html,js}',
'./pages/**/*.{html,js}',
'./node_modules/@my-company/tailwind-components/**/*.js',
],
// ...
}
如果你在 monorepo 中使用 tailwind,你可能需要使用 require.resolve 来确保Tailwind 可以看到你的内容文件
const path = require('path');
module.exports = {
content: [
'./components/**/*.{html,js}',
'./pages/**/*.{html,js}',
path.join(path.dirname(require.resolve('@my-company/tailwind-components')), '**/*.js'),
],
// ...
}
安全 class
由于 Tailwind 使用一种非常简单的方法来检测内容中的 class,因此您可能会发现生成了一些您实际上并不需要的 class。
例如,这个 HTML 仍然会生成 container class,即使这个类实际上没有被使用
<div class="text-lg leading-8 text-gray-600">
Every custom pool we design starts as a used shipping container, and is
retrofitted with state of the art technology and finishes to turn it into
a beautiful and functional way to entertain your guests all summer long.
</div>
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{html,js}',
'./components/**/*.{html,js}',
],
blocklist: [
'container',
'collapse',
],
// ...
}
theme
扩展默认主题
扩展了 fontFamily 属性,添加了 font-display class,它可以更改元素上使用的字体:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
fontFamily: {
display: 'Oswald, ui-serif', // Adds a new `font-display` class
}
}
}
}
<h1 class="font-display">
This uses the Oswald font
</h1>
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
screens: {
'3xl': '1600px', // Adds a new `3xl:` screen variant
}
}
}
}
<blockquote class="text-base md:text-md 3xl:text-lg">
Oh I gotta get on that internet, I'm late on everything!
</blockquote>
覆盖默认主题
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
// Replaces all of the default `opacity` values
opacity: {
'0': '0',
'20': '0.2',
'40': '0.4',
'60': '0.6',
'80': '0.8',
'100': '1',
}
}
}
引用其他值
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
spacing: {
// ...
},
backgroundSize: ({ theme }) => ({
auto: 'auto',
cover: 'cover',
contain: 'contain',
...theme('spacing')
})
}
}
注意,这种闭包只能用于顶级主题键,而不能用于每个节中的键。
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
fill: {
gray: ({ theme }) => theme('colors.gray')
}
}
}
引用默认主题
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
extend: {
fontFamily: {
sans: [
'Lato',
...defaultTheme.fontFamily.sans,
]
}
}
}
}
禁用整个核心插件
如果你不想为某个核心插件生成任何 class,最好在你的 corePlugins 配置中将该插件设置为 false,而不是在你的 theme 配置中为该键提供一个空对象。
/** @type {import('tailwindcss').Config} */
module.exports = {
corePlugins: {
opacity: false,
}
}
屏幕
覆盖默认值
- 要完全替换默认断点,请直接在
theme键下添加您的自定义screens配置
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
screens: {
'sm': '576px',
// => @media (min-width: 576px) { ... }
'md': '960px',
// => @media (min-width: 960px) { ... }
'lg': '1440px',
// => @media (min-width: 1440px) { ... }
},
}
}
- 要覆盖单个屏幕大小(如
lg),请在theme.extend键下添加自定义的screens值:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
screens: {
'lg': '992px',
// => @media (min-width: 992px) { ... }
},
},
},
}
添加较大的断点
添加额外的较大断点的最简单方法是使用 extend 键
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
screens: {
'3xl': '1600px',
},
},
},
plugins: [],
}
添加更小的断点
如果你想添加一个额外的小断点,你不能使用 extend ,因为小断点将被添加到断点列表的末尾,断点需要从最小到最大排序,以便按照预期与最小宽度断点系统一起工作。
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
screens: {
'xs': '475px',
...defaultTheme.screens,
},
},
plugins: [],
}
使用自定义屏幕名称
您可以随意命名您的自定义屏幕,并且不限于遵循 Tailwind 默认使用的 sm / md / lg / xl / 2xl 约定。
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
screens: {
'tablet': '640px',
// => @media (min-width: 640px) { ... }
'laptop': '1024px',
// => @media (min-width: 1024px) { ... }
'desktop': '1280px',
// => @media (min-width: 1280px) { ... }
},
}
}
<div class="grid grid-cols-1 tablet:grid-cols-2 laptop:grid-cols-3 desktop:grid-cols-4">
<!-- ... -->
</div>
高级配置
最大宽度断点
确保按降序列出最大宽度断点,以便它们按预期相互覆盖。
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
screens: {
'2xl': {'max': '1535px'},
// => @media (max-width: 1535px) { ... }
'xl': {'max': '1279px'},
// => @media (max-width: 1279px) { ... }
'lg': {'max': '1023px'},
// => @media (max-width: 1023px) { ... }
'md': {'max': '767px'},
// => @media (max-width: 767px) { ... }
'sm': {'max': '639px'},
// => @media (max-width: 639px) { ... }
}
}
}
固定范围断点
与常规的最小宽度或最大宽度断点不同,以这种方式定义的断点仅在视口大小明确位于定义的范围内时才生效。
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
screens: {
'sm': {'min': '640px', 'max': '767px'},
// => @media (min-width: 640px and max-width: 767px) { ... }
'md': {'min': '768px', 'max': '1023px'},
// => @media (min-width: 768px and max-width: 1023px) { ... }
'lg': {'min': '1024px', 'max': '1279px'},
// => @media (min-width: 1024px and max-width: 1279px) { ... }
'xl': {'min': '1280px', 'max': '1535px'},
// => @media (min-width: 1280px and max-width: 1535px) { ... }
'2xl': {'min': '1536px'},
// => @media (min-width: 1536px) { ... }
},
}
}
<div class="md:text-center">
This text will be centered on medium screens, but revert back
to the default (left-aligned) at all other screen sizes.
</div>
多范围断点
假设您有一个侧边栏,并希望断点基于内容区域宽度而不是整个视口。您可以通过在侧边栏变得可见并缩小内容区域时将其中一个断点回退到较小的断点来模拟此操作
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
screens: {
'sm': '500px',
'md': [
// Sidebar appears at 768px, so revert to `sm:` styles between 768px
// and 868px, after which the main content area is wide enough again to
// apply the `md:` styles.
{'min': '668px', 'max': '767px'},
{'min': '868px'}
],
'lg': '1100px',
'xl': '1400px',
}
}
}
自定义媒体查询
使用 raw 键定义的媒体查询将按原样输出,而 min 和 max 键将被忽略。
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
screens: {
'tall': { 'raw': '(min-height: 800px)' },
// => @media (min-height: 800px) { ... }
}
}
}
}
颜色
添加其他颜色
如果您想将全新颜色添加到默认调色板,请将其添加到配置文件的 theme.extend.colors 部分
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
colors: {
brown: {
50: '#fdf8f6',
100: '#f2e8e5',
200: '#eaddd7',
300: '#e0cec7',
400: '#d2bab0',
500: '#bfa094',
600: '#a18072',
700: '#977669',
800: '#846358',
900: '#43302b',
},
}
},
},
}
如果您的设计需要,您还可以使用 theme.extend.colors 向现有颜色添加其他阴影
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
colors: {
blue: {
950: '#17275c',
},
}
},
},
}
使用 CSS 变量
将 CSS 变量定义为没有颜色空间函数的通道
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
/* For rgb(255 115 179 / <alpha-value>) */
--color-primary: 255 115 179; /* For hsl(198deg 93% 60% / <alpha-value>) */
--color-primary: 198deg 93% 60%; /* For rgba(255, 115, 179, <alpha-value>) */
--color-primary: 255, 115, 179;
}
}
然后在配置文件中定义颜色,确保包含您正在使用的颜色空间,以及 Tailwind 在使用不透明度修改器时用于注入 alpha 值的特殊 <alpha-value> 占位符:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
colors: {
// Using modern `rgb`
primary: 'rgb(var(--color-primary) / <alpha-value>)',
// Using modern `hsl`
primary: 'hsl(var(--color-primary) / <alpha-value>)',
// Using legacy `rgba`
primary: 'rgba(var(--color-primary), <alpha-value>)',
}
}
}
间距
默认间距比例
16 的间距是 8 的两倍。一个间距单位等于 0.25rem ,在普通浏览器中默认转换为 4px 。
插件
要开始使用第一个插件,请从 tailwindcss/plugin 导入Tailwind的 plugin 函数。然后在 plugins 数组中,调用导入的 plugin 函数,并将匿名函数作为第一个参数。
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addUtilities, addComponents, e, config }) {
// Add your custom styles here
}),
]
}
官方插件
插件可以通过 npm 安装到你的项目中,然后将它们添加到你的 tailwind.config.js 文件中:
/** @type {import('tailwindcss').Config} */
module.exports = {
// ...
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('@tailwindcss/aspect-ratio'),
require('@tailwindcss/container-queries'),
]
}
@tailwindcss/typography 插件添加了一组 prose 类,可用于快速向来自markdown或CMS数据库等来源的内容块添加合理的排版样式。
<article class="prose lg:prose-xl">
<h1>Garlic bread with cheese: What the science tells us</h1>
<p>
For years parents have espoused the health benefits of eating garlic bread with cheese to their
children, with the food earning such an iconic status in our culture that kids will often dress
up as warm, cheesy loaf for Halloween.
</p>
<p>
But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases
springing up around the country.
</p>
<!-- ... -->
</article>
@tailwindcss/forms 插件添加了一个自定义的表单重置层,可以更容易地使用实用程序类对表单元素进行样式化。
<!-- You can actually customize padding on a select element: -->
<select class="px-4 py-3 rounded-full">
<!-- ... -->
</select>
<!-- Or change a checkbox color using text color utilities: -->
<input type="checkbox" class="rounded text-pink-500" />
@tailwindcss/aspect-ratio 插件是原生 aspect-ratio 支持的替代方案,可在旧浏览器中工作,并添加了 aspect-w-* 和 aspect-h-* 类,可以组合以提供元素固定的长宽比。
<div class="aspect-w-16 aspect-h-9">
<iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
@tailwindcss/container-queries 插件添加了新的 @{size} 变体,如 @sm 和 @md ,允许您基于标记为 @container 的父元素的尺寸而不是视口来设置元素的样式。
<div class="@container">
<div class="@lg:text-sky-400">
<!-- ... -->
</div>
</div>
添加工具 class
addUtilities 和 matchUtilities 函数允许您在Tailwind的 utilities 层中注册新样式。
静态工具 class
使用 addUtilities 函数注册不支持用户提供值的简单静态实用程序
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addUtilities }) {
addUtilities({
'.content-auto': {
'content-visibility': 'auto',
},
'.content-hidden': {
'content-visibility': 'hidden',
},
'.content-visible': {
'content-visibility': 'visible',
},
})
})
]
}
动态工具 class
使用 matchUtilities 函数注册映射到用户 theme 配置中定义的值的实用程序
const plugin = require('tailwindcss/plugin')
module.exports = {
theme: {
tabSize: {
1: '1',
2: '2',
4: '4',
8: '8',
}
},
plugins: [
plugin(function({ matchUtilities, theme }) {
matchUtilities(
{
tab: (value) => ({
tabSize: value
}),
},
{ values: theme('tabSize') }
)
})
]
}
以这种方式定义的实用程序还支持任意值,这意味着您可以使用方括号表示法使用主题中不存在的值
<div class="tab-[13]">
<!-- ... -->
</div>
前缀和 important
默认情况下,插件实用程序会自动遵守用户的 prefix 和 important 首选项
这意味着,在这种 Tailwind 配置下:
/** @type {import('tailwindcss').Config} */
module.exports = {
prefix: 'tw-',
important: true,
// ...
}
上面的示例插件将生成以下 CSS
.tw-content-auto {
content-visibility: auto !important;
}
.tw-content-hidden {
content-visibility: hidden !important;
}
.tw-content-visible {
content-visibility: visible !important;
}
与修饰符一起使用
任何使用 addUtilities 添加的自定义实用程序都可以自动与修改器一起使用:
<div class="content-auto lg:content-visible">
<!-- ... -->
</div>
提供默认值
实用程序插件可以通过将配置对象作为第二个参数包含到 plugin 函数中来提供默认值:
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function({ matchUtilities, theme }) {
matchUtilities(
{
tab: (value) => ({
tabSize: value
}),
},
{ values: theme('tabSize') }
)
}, {
theme: {
tabSize: {
1: '1',
2: '2',
4: '4',
8: '8',
}
}
})
添加组件 class
addComponents 函数允许您在 Tailwind 的 components 层中注册新样式。
其他框架中看到的预构建组件,可能需要覆盖。
要从插件中添加新的组件样式,请调用 addComponents ,使用 CSS-in-JS语法传入样式
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addComponents }) {
addComponents({
'.btn': {
padding: '.5rem 1rem',
borderRadius: '.25rem',
fontWeight: '600',
},
'.btn-blue': {
backgroundColor: '#3490dc',
color: '#fff',
'&:hover': {
backgroundColor: '#2779bd'
},
},
'.btn-red': {
backgroundColor: '#e3342f',
color: '#fff',
'&:hover': {
backgroundColor: '#cc1f1a'
},
},
})
})
]
}
前缀和 important
默认情况下,组件类自动遵守用户的 prefix 首选项,但它们不受用户的 important 首选项的影响。
/** @type {import('tailwindcss').Config} */
module.exports = {
prefix: 'tw-',
important: true,
// ...
}
.tw-btn {
padding: .5rem 1rem;
border-radius: .25rem;
font-weight: 600;
}
.tw-btn-blue {
background-color: #3490dc;
color: #fff;
}
.tw-btn-blue:hover {
background-color: #2779bd;
}
.tw-btn-red {
background-color: #e3342f;
color: #fff;
}
.tw-btn-red:hover {
background-color: #cc1f1a;
}
可以手动添加 !important
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addComponents }) {
addComponents({
'.btn': {
padding: '.5rem 1rem !important',
borderRadius: '.25rem !important',
fontWeight: '600 !important',
},
// ...
})
})
]
}
默认情况下,选择器中的所有 class 都将被添加前缀,所以如果你添加一个更复杂的样式,比如:
const plugin = require('tailwindcss/plugin')
module.exports = {
prefix: 'tw-',
plugins: [
plugin(function({ addComponents }) {
const components = {
// ...
'.navbar-inverse a.nav-link': {
color: '#fff',
}
}
addComponents(components)
})
]
}
.tw-navbar-inverse a.tw-nav-link {
color: #fff;
}
与修饰符一起使用
使用 addComponents 添加的任何组件类都可以自动与修饰符一起使用
<div class="btn md:btn-lg">
<!-- ... -->
</div>
添加基础样式
addBase 函数允许您在Tailwind的 base 层中注册新样式。使用它来添加基本排版样式,固执己见的全局重置或 @font-face 规则。
要从插件中添加新的基本样式,请调用 addBase ,使用 CSS-in-JS 语法传入样式:
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addBase, theme }) {
addBase({
'h1': { fontSize: theme('fontSize.2xl') },
'h2': { fontSize: theme('fontSize.xl') },
'h3': { fontSize: theme('fontSize.lg') },
})
})
]
}
prefix 或 important 配置不会生效
添加变体
addVariant 和 matchVariant 函数允许您注册自己的自定义修饰符,这些修饰符可以像内置变量(如 hover 、 focus 或 supports )一样使用。
静态变量
对于简单的自定义变量,使用 addVariant 函数,传入自定义变量的名称,以及表示选择器应如何修改的格式字符串。
const plugin = require('tailwindcss/plugin')
module.exports = {
// ...
plugins: [
plugin(function({ addVariant }) {
addVariant('optional', '&:optional')
addVariant('hocus', ['&:hover', '&:focus'])
addVariant('inverted-colors', '@media (inverted-colors: inverted)')
})
]
}
<form class="flex inverted-colors:outline ...">
<input class="optional:border-gray-300 ..." />
<button class="bg-blue-500 hocus:bg-blue-600">...</button>
</form>
动态变量
使用 matchVariant 函数注册新的参数化变量,如内置的 supports-* 、 data-* 和 aria-* 变量
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ matchVariant }) {
matchVariant(
'nth',
(value) => {
return `&:nth-child(${value})`;
},
{
values: {
1: '1',
2: '2',
3: '3',
}
}
);
})
]
}
使用 matchVariant 定义的变体还支持使用方括号表示法的任意值:
<div class="nth-[3n+1]:bg-blue-500 ...">
<!-- ... -->
</div>
如果需要,可以使用 sort 选项来控制生成的CSS的源代码顺序,以避免来自同一变体的其他值的优先级问题:
matchVariant("min", (value) => `@media (min-width: ${value})`, {
sort(a, z) {
return parseInt(a.value) - parseInt(z.value);
},
});
父状态和兄弟状态
您的自定义修改器不会自动与 Tailwind 的父状态修改器和同级状态修改器一起使用
为了支持您自己的自定义修饰符的 group-* 和 peer-* 版本,请使用特殊的 :merge 指令将它们注册为单独的变体,以确保 .group 和 .peer 类只在最终选择器中出现一次。
const plugin = require('tailwindcss/plugin')
module.exports = {
// ...
plugins: [
plugin(function({ addVariant }) {
addVariant('optional', '&:optional')
addVariant('group-optional', ':merge(.group):optional &')
addVariant('peer-optional', ':merge(.peer):optional ~ &')
})
]
}
扩展配置
插件可以通过提供一个对象作为 plugin 函数的第二个参数,将它们自己的配置值集合合并到用户的 tailwind.config.js 配置中:
const plugin = require('tailwindcss/plugin')
module.exports = plugin(function({ matchUtilities, theme }) {
matchUtilities(
{
tab: (value) => ({
tabSize: value
}),
},
{ values: theme('tabSize') }
)
}, {
theme: {
tabSize: {
1: '1',
2: '2',
4: '4',
8: '8',
}
}
})
暴露选项
有时候,一个插件以一种不属于 theme 的方式进行配置是有意义的,比如你可能希望用户能够自定义插件使用的类名。
对于这种情况,您可以使用 plugin.withOptions 来定义一个可以通过配置对象调用的插件。这个API类似于常规的 plugin API,除了每个参数都应该是一个函数,它接收用户的 options 并返回通常使用常规API传入的值:
const plugin = require('tailwindcss/plugin')
module.exports = plugin.withOptions(function (options = {}) {
return function({ addComponents }) {
const className = options.className ?? 'markdown'
addComponents({
[`.${className}`]: {
// ...
}
})
}
}, function (options) {
return {
theme: {
markdown: {
// ...
}
},
}
})
当用户在他们的 plugins 配置中注册它时,用户会调用你的插件并传递沿着他们的选项
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
// ...
},
plugins: [
require('./plugins/markdown.js')({
className: 'wysiwyg'
})
],
}
如果不需要传入任何自定义选项,用户也可以注册以这种方式创建的插件,而不调用它们:
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
// ...
},
plugins: [
require('./plugins/markdown.js')
],
}
CSS-in-JS语法
Tailwind 的插件系统期望将 CSS 规则编写为 JavaScript 对象,使用与您可能从 CSS-in-JS 库(如 Emotion)中识别出的相同的语法,由 postcss-js 底层提供支持。
.card {
background-color: #fff;
border-radius: .25rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
将其转换为CSS-in-JS对象看起来像这样:
addComponents({
'.card': {
'background-color': '#fff',
'border-radius': '.25rem',
'box-shadow': '0 2px 4px rgba(0,0,0,0.2)',
}
})
Presets
默认情况下,您在自己的 tailwind.config.js 文件中添加的任何配置都将智能地与默认配置合并,您自己的配置将充当一组覆盖和扩展。
presets 选项允许您指定一个不同的配置作为您的基础,从而可以轻松地打包一组您想要在项目中重用的自定义。
创建 presets
预设只是常规的Tailwind配置对象,其形状与您在 tailwind.config.js 文件中添加的配置完全相同。
// Example preset
module.exports = {
theme: {
colors: {
blue: {
light: '#85d7ff',
DEFAULT: '#1fb6ff',
dark: '#009eeb',
},
pink: {
light: '#ff7ce5',
DEFAULT: '#ff49db',
dark: '#ff16d1',
},
gray: {
darkest: '#1f2d3d',
dark: '#3c4858',
DEFAULT: '#c0ccda',
light: '#e0e6ed',
lightest: '#f9fafc',
}
},
fontFamily: {
sans: ['Graphik', 'sans-serif'],
},
extend: {
flexGrow: {
2: '2',
3: '3',
},
zIndex: {
60: '60',
70: '70',
80: '80',
90: '90',
100: '100',
},
}
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
}
/** @type {import('tailwindcss').Config} */
module.exports = {
presets: [
require('./my-preset.js')
],
// Customizations specific to this project would go here
theme: {
extend: {
minHeight: {
48: '12rem',
}
}
},
}
如果要创建完全替换默认配置的预设,请在预设本身中包含一个空的 presets 键:
// Example preset
module.exports = {
presets: [],
theme: {
// ...
},
plugins: [
// ...
],
}
深入了解合并规则
presets 覆盖 tailwind.config.js 中的以下选项
contentdarkModeprefiximportantvariantOrderseparatorsafelist
theme 进行浅合并,顶层选项会被合并,extend 例外会收集所有配置。
多个 preset 合并,后面覆盖前面
plugins 进行合并,不能禁用已添加的 preset 插件
corePlugins 对象进行合并,数组替换