为什么要使用TypeScript?详细对比分析
TypeScript的核心价值 TypeScript是JavaScript的超集,添加了静态类型检查。它在编译时发现错误,而不是在运行时,从而提高代码质量和开发效率。 1️⃣ 类型安全:避免运行时错误
让我用最简单的方式来解释这三个单位:
CSS
/* 方法1:用rem - 所有按钮大小统一 */
html { font-size: 16px; } /* 总开关设为16px */
.button {
width: 10rem; /* = 160px (16×10) */
height: 3rem; /* = 48px (16×3) */
font-size: 1rem; /* = 16px */
}
/* 方法2:用em - 按钮大小跟随自己的字体 */
.button {
font-size: 18px; /* 自己的字体18px */
width: 8em; /* = 144px (18×8) */
height: 2.5em; /* = 45px (18×2.5) */
padding: 0.5em; /* = 9px (18×0.5) */
}
什么时候用哪个?
CSS
/* 用vh做全屏效果 */
.hero-section {
height: 100vh; /* 占满整个屏幕高度 */
}
.header {
height: 10vh; /* 占屏幕高度的10% */
}
.content {
height: 80vh; /* 占屏幕高度的80% */
}
.footer {
height: 10vh; /* 占屏幕高度的10% */
}
为什么用vh?
HTML
<div class="card-rem">用rem的卡片</div>
<div class="card-em">用em的卡片</div>
<div class="card-vh">用vh的卡片</div>
CSS
html { font-size: 16px; }
/* rem卡片 - 大小固定,只跟html有关 */
.card-rem {
width: 20rem; /* 永远是320px */
height: 15rem; /* 永远是240px */
font-size: 1.2rem; /* 永远是19.2px */
}
/* em卡片 - 大小跟自己的字体有关 */
.card-em {
font-size: 20px; /* 设置自己的字体 */
width: 16em; /* = 320px (20×16) */
height: 12em; /* = 240px (20×12) */
padding: 1em; /* = 20px (20×1) */
}
/* vh卡片 - 大小跟屏幕高度有关 */
.card-vh {
width: 50vw; /* 屏幕宽度的50% */
height: 30vh; /* 屏幕高度的30% */
}
CSS
/* ✅ 整体布局 - 希望统一缩放 */
.container { max-width: 80rem; }
.sidebar { width: 20rem; }
/* ✅ 组件尺寸 - 希望保持比例 */
.avatar { width: 4rem; height: 4rem; }
.icon { width: 2rem; height: 2rem; }
/* ✅ 字体层级 - 希望统一管理 */
h1 { font-size: 3rem; }
h2 { font-size: 2.5rem; }
p { font-size: 1rem; }
CSS
/* ✅ 内边距 - 希望跟文字大小成比例 */
.button {
font-size: 18px;
padding: 0.5em 1em; /* 跟按钮文字大小成比例 */
}
/* ✅ 图标 - 希望跟文字一样大 */
.text-with-icon {
font-size: 20px;
}
.text-with-icon .icon {
width: 1em; /* 跟文字一样大 */
height: 1em;
}
CSS
/* ✅ 全屏效果 */
.hero { height: 100vh; }
/* ✅ 移动端布局 */
.mobile-header { height: 10vh; }
.mobile-content { height: 80vh; }
.mobile-footer { height: 10vh; }
/* ✅ 响应式容器 */
.modal {
max-width: 90vw; /* 不超过屏幕宽度90% */
max-height: 90vh; /* 不超过屏幕高度90% */
}
rem 做布局,用 vh 做全屏em
Margin 合并(也叫 Margin 折叠)是指相邻元素的垂直 margin 会合并成一个 margin,取两者中的较大值,而不是相加。
HTML
<div class="sibling1">第一个元素</div>
<div class="sibling2">第二个元素</div>
CSS
.sibling1 {
margin-bottom: 30px;
background: lightblue;
padding: 10px;
}
.sibling2 {
margin-top: 20px;
background: lightcoral;
padding: 10px;
}
/*
期望间距: 30px + 20px = 50px
实际间距: max(30px, 20px) = 30px ← 发生了合并!
*/
HTML
<div class="parent">
<div class="child">子元素</div>
</div>
CSS
.parent {
margin-top: 40px;
background: lightgreen;
}
.child {
margin-top: 60px;
background: lightyellow;
padding: 10px;
}
/*
期望: 父元素距离上方40px,子元素再距离父元素60px
实际: 父元素距离上方60px,子元素紧贴父元素 ← 合并了!
*/
HTML
<div class="before">前面的元素</div>
<div class="empty"></div>
<div class="after">后面的元素</div>
CSS
.before {
margin-bottom: 25px;
background: lightblue;
padding: 10px;
}
.empty {
margin-top: 15px;
margin-bottom: 35px;
/* 没有内容、padding、border、height */
}
.after {
margin-top: 20px;
background: lightcoral;
padding: 10px;
}
/*
空元素的上下margin会合并: max(15px, 35px) = 35px
然后与相邻元素继续合并: max(25px, 35px, 20px) = 35px
*/
CSS
/* 方法1: overflow */
.bfc-overflow {
overflow: hidden; /* 或 auto、scroll */
}
/* 方法2: display */
.bfc-display {
display: flow-root; /* 专门用于创建BFC */
}
/* 方法3: position */
.bfc-position {
position: absolute; /* 或 fixed */
}
/* 方法4: float */
.bfc-float {
float: left; /* 或 right */
}
/* 方法5: flex/grid容器 */
.bfc-flex {
display: flex;
flex-direction: column;
}
HTML
<div class="container">
<div class="item">元素1</div>
<div class="item">元素2</div>
</div>
CSS
/* 解决父子margin合并 */
.container {
overflow: hidden; /* 创建BFC */
background: #f0f0f0;
}
.item {
margin: 20px;
padding: 10px;
background: lightblue;
}
/* 现在margin不会与父元素合并了 */
CSS
/* 问题代码 */
.problematic {
margin-top: 30px;
margin-bottom: 30px;
}
/* 解决方案 */
.solution-padding {
padding-top: 30px;
padding-bottom: 30px;
/* padding 不会发生合并 */
}
CSS
/* 阻止父子margin合并 */
.parent-with-border {
border-top: 1px solid transparent; /* 透明边框 */
/* 或者 */
padding-top: 1px;
/* 或者 */
overflow: hidden;
}
.parent-with-border .child {
margin-top: 30px; /* 现在不会与父元素合并 */
}
CSS
.flex-container {
display: flex;
flex-direction: column;
gap: 30px; /* 使用gap替代margin */
}
.flex-item {
padding: 20px;
background: lightblue;
/* 不需要设置margin */
}
CSS
.grid-container {
display: grid;
grid-template-rows: repeat(auto-fit, auto);
gap: 30px; /* 统一间距 */
}
.grid-item {
padding: 20px;
background: lightcoral;
}
CSS
:root {
--spacing-unit: 20px;
--spacing-small: calc(var(--spacing-unit) * 0.5);
--spacing-medium: var(--spacing-unit);
--spacing-large: calc(var(--spacing-unit) * 1.5);
}
.spaced-element {
margin-bottom: var(--spacing-medium);
/* 统一管理,避免不同值的合并问题 */
}
/* 特殊情况下强制不合并 */
.force-spacing {
margin-bottom: calc(var(--spacing-medium) + 1px);
/* 微小差异阻止合并 */
}
HTML
<div class="card-list">
<div class="card">卡片1</div>
<div class="card">卡片2</div>
<div class="card">卡片3</div>
</div>
CSS
/* 有问题的写法 */
.card {
margin: 20px 0;
padding: 15px;
background: white;
border: 1px solid #ddd;
/* 相邻卡片间距只有20px,而不是期望的40px */
}
CSS
/* 方案1: 使用flexbox */
.card-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.card {
padding: 15px;
background: white;
border: 1px solid #ddd;
/* 不需要margin */
}
/* 方案2: 只设置一个方向的margin */
.card-list-v2 .card {
margin-bottom: 20px;
padding: 15px;
background: white;
border: 1px solid #ddd;
}
.card-list-v2 .card:last-child {
margin-bottom: 0;
}
/* 方案3: 使用相邻选择器 */
.card-list-v3 .card + .card {
margin-top: 20px;
}
HTML
<article class="article">
<h1>标题</h1>
<p>第一段内容</p>
<p>第二段内容</p>
<blockquote>引用内容</blockquote>
</article>
CSS
/* 有问题的写法 */
h1 { margin: 30px 0; }
p { margin: 15px 0; }
blockquote { margin: 25px 0; }
/* margin会发生合并,间距不均匀 */
CSS
/* 方案1: 统一间距系统 */
.article > * {
margin-top: 0;
margin-bottom: 1.5rem;
}
.article > *:last-child {
margin-bottom: 0;
}
/* 方案2: 使用相邻选择器 */
.article h1 + p { margin-top: 1rem; }
.article p + p { margin-top: 1rem; }
.article p + blockquote { margin-top: 1.5rem; }
/* 方案3: CSS Grid */
.article {
display: grid;
gap: 1.5rem;
}
HTML
<div class="modal-overlay">
<div class="modal">
<div class="modal-header">标题</div>
<div class="modal-body">内容</div>
</div>
</div>
CSS
/* 有问题的写法 */
.modal {
margin: auto; /* 水平居中 */
margin-top: 50px; /* 想要距离顶部50px */
}
.modal-header {
margin-bottom: 20px;
}
.modal-body {
margin-top: 20px; /* 可能与header的margin合并 */
}
CSS
/* 方案1: Flexbox居中 */
.modal-overlay {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 50px 20px;
}
.modal {
background: white;
border-radius: 8px;
overflow: hidden; /* 创建BFC,防止内部margin合并 */
}
.modal-header {
padding: 20px;
background: #f5f5f5;
}
.modal-body {
padding: 20px;
}
/* 方案2: Grid居中 */
.modal-overlay-grid {
display: grid;
place-items: center;
min-height: 100vh;
padding: 50px 20px;
}
CSS
/* 显示margin区域 */
.debug-margins * {
outline: 1px solid red;
background-clip: content-box;
}
/* 显示所有盒模型 */
.debug-all * {
box-shadow:
0 0 0 1px red, /* border */
0 0 0 2px yellow, /* padding */
0 0 0 3px blue; /* margin的近似显示 */
}
/* 检测BFC */
.debug-bfc {
background: rgba(255, 0, 0, 0.1);
}
.debug-bfc::before {
content: 'BFC';
position: absolute;
top: 0;
left: 0;
font-size: 12px;
background: red;
color: white;
padding: 2px 4px;
}
JavaScript
// 检测元素是否创建了BFC
function hasBFC(element) {
const style = getComputedStyle(element);
return (
style.overflow !== 'visible' ||
style.display === 'flow-root' ||
style.position === 'absolute' ||
style.position === 'fixed' ||
style.float !== 'none' ||
style.display === 'flex' ||
style.display === 'grid' ||
style.display === 'inline-block' ||
style.display === 'table-cell'
);
}
// 计算实际margin
function getActualMargin(element) {
const rect = element.getBoundingClientRect();
const style = getComputedStyle(element);
return {
top: parseFloat(style.marginTop),
right: parseFloat(style.marginRight),
bottom: parseFloat(style.marginBottom),
left: parseFloat(style.marginLeft)
};
}
// 使用示例
const element = document.querySelector('.my-element');
console.log('是否创建BFC:', hasBFC(element));
console.log('实际margin:', getActualMargin(element));
CSS
/* 不推荐:频繁改变margin */
.animated-bad {
transition: margin 0.3s;
}
.animated-bad:hover {
margin-top: 20px; /* 会触发重排 */
}
/* 推荐:使用transform */
.animated-good {
transition: transform 0.3s;
}
.animated-good:hover {
transform: translateY(20px); /* 只触发重绘 */
}
CSS
/* 使用CSS自定义属性统一管理 */
:root {
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-xl: 2rem;
}
/* 统一的间距类 */
.mt-sm { margin-top: var(--spacing-sm); }
.mt-md { margin-top: var(--spacing-md); }
.mt-lg { margin-top: var(--spacing-lg); }
.mb-sm { margin-bottom: var(--spacing-sm); }
.mb-md { margin-bottom: var(--spacing-md); }
.mb-lg { margin-bottom: var(--spacing-lg); }
gap 属性margin-bottom 或 margin-top
+ 选择器精确控制间距记住:理解margin合并的规则,选择合适的解决方案,让布局更加可控和可预测!