尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

三列布局与 TypeScript 工具类型 Pick / Omit / Partial 详解

三列布局与 TypeScript 工具类型 Pick / Omit / Partial 详解 文章目录一、CSS 三列布局1.1 什么是三列布局1.2 前置知识格式化上下文BFC / FFC / GFC1.3 方案一Flex 弹性布局最常用1.4 方案二Grid 网格布局最简洁1.5 方案三Float 浮动布局最古老需理解 BFC1.6 三种方案对比二、TypeScript 工具类型2.1 为什么需要工具类型2.2 基础keyof 与 typeof2.3 Pick挑选字段2.4 Omit剔除字段2.5 重点Omit Pick Exclude2.6 Partial全字段可选2.7 Record 与 ReturnType2.8 Exclude 与 Omit 的区别三、全文总结四、核心知识点复盘五、常见问题 / 避坑指南这是一篇面向「基础偏薄弱但想系统搞懂」的前端同学的文章。前半部分用三种方式Flex / Grid / Float讲透 PC 端最常见的三列布局以及它背后的核心概念——格式化上下文BFC / FFC / GFC后半部分讲TypeScript 内置工具类型Pick、Omit、Partial、Record、ReturnType重点拆解Omit Pick Exclude这条底层等价关系。每个知识点都按「是什么 → 为什么 → 怎么用 → 易错点」的顺序展开代码完整可运行。一、CSS 三列布局1.1 什么是三列布局在 PC 端网页里有一种非常经典的页面骨架左右两列固定宽度中间一列自适应撑满剩余空间。典型场景就是下面这种结构┌────────────┬──────────────────────────┬────────────┐ │ 左侧导航 │ 中间主内容 │ 右侧广告 │ │ (固定 200px)│ (自适应剩余宽度) │ (固定 200px)│ └────────────┴──────────────────────────┴────────────┘它的 HTML 骨架长这样divclasslayoutasideclasssidebar left左侧导航/asidemainclasscontent中间主内容自适应/mainasideclasssidebar right右侧广告/aside/div这里有一个很关键的细节为什么要把main主内容放在中间、优先渲染因为两侧通常是导航、广告这类「次要信息」而主内容才是用户最想第一时间看到的东西。让主内容先渲染页面感知速度更快SEO 也更友好。这个「内容优先」的诉求决定了我们后面选择布局方案时的取舍。要真正理解三列布局得先搞懂一个底层概念——格式化上下文。1.2 前置知识格式化上下文BFC / FFC / GFC这部分是 CSS 布局的地基也是很多同学「知其然不知其所以然」的地方我们把它讲透。文档流Normal Flow是浏览器默认的排版规则块级元素div、p、h1…从上到下垂直排列行内元素span、a、em…从左到右水平排列。html作为根元素一启动就开启了第一个格式化上下文BFC所有元素默认都排在这个「最外层的大容器」里。BFCBlock Formatting Context块级格式化上下文可以理解为一个「独立的排版作用域」在这个作用域里元素按照自己的规则排列外层的 BFC 不会影响到内部新的格式化上下文内部也不会反过来干扰外部。问题来了默认的 BFC 只能让块级元素从上往下排没法做「左右分列」这种局部复杂布局。而inline行内元素又天生不适合当容器盒子。所以要实现三列布局就必须「开启一个新的格式化上下文」让某个块级元素拥有独立的、更强大的排版能力。开启新的格式化上下文的常见方式写法开启的上下文说明display: flexFFCFlex 格式化上下文一维布局主轴方向排列display: gridGFCGrid 格式化上下文二维布局行列同时控制display: table/inline-block新的 BFC表格 / 行内块排版float: left / right新的 BFC元素脱离文档流浮动position: absolute / fixed新的 BFC脱离文档流定位overflow: hidden / auto / scroll新的 BFC触发「块级格式化上下文」记住一句话只要让一个块级元素开启新的格式化上下文它内部就能独立排版不再受外层默认文档流的束缚。三列布局正是靠这个实现的。下面用三种方式分别实现感受它们各自的特点。1.3 方案一Flex 弹性布局最常用Flex 是一维布局容器设置display: flex后子项会沿着「主轴」排列。中间列用flex: 1吃掉所有剩余空间即可。!DOCTYPEhtmlhtmllangzh-CNheadmetacharsetUTF-8/title三列布局 - Flex/titlestyle*{margin:0;padding:0;box-sizing:border-box;}.layout{display:flex;/* 开启弹性格式化上下文 FFC */height:100vh;/* 占满视口高度方便观察 */}/* 左右两列固定宽度且不允许被压缩 */.sidebar{width:200px;flex-shrink:0;/* 关键默认 flex 子项可能被压缩这里禁止压缩 */}.left{background:#f0c0c0;}.right{background:#c0c0f0;}/* 中间主内容自适应剩余宽度 */.content{flex:1;/* 等价于 flex: 1 1 0%撑满剩余空间 */background:#fff;}/style/headbodydivclasslayoutasideclasssidebar left左侧导航/asidemainclasscontent中间主内容自适应宽度/mainasideclasssidebar right右侧广告/aside/div/body/html代码思路解析.layout上display: flex把三个子项排成一行两侧.sidebar固定width: 200px并加flex-shrink: 0——因为 flex 子项默认允许被「压缩」不加这个当内容多时两侧会被挤窄固定宽度就失效了中间.content用flex: 1表示「占据所有剩余空间」宽度自动适配。易错点很多同学只写width: 200px却忘了flex-shrink: 0结果窗口变窄时左右两列被压变形。这是 flex 布局里最隐蔽的坑。1.4 方案二Grid 网格布局最简洁Grid 是二维布局可以直接用grid-template-columns一次声明三列的宽度。!DOCTYPEhtmlhtmllangzh-CNheadmetacharsetUTF-8/title三列布局 - Grid/titlestyle*{margin:0;padding:0;box-sizing:border-box;}.layout{display:grid;/* 三列左 200px | 中间 1fr 自适应 | 右 200px */grid-template-columns:200px 1fr 200px;height:100vh;}.left{background:#f0c0c0;}.right{background:#c0c0f0;}.content{background:#fff;}/style/headbodydivclasslayoutasideclasssidebar left左侧导航/asidemainclasscontent中间主内容自适应/mainasideclasssidebar right右侧广告/aside/div/body/html代码思路解析display: grid开启网格格式化上下文 GFCgrid-template-columns: 200px 1fr 200px里200px是固定列宽1fr是「按剩余空间等分 1 份」于是中间列自动撑满。frfraction份数是 Grid 独有的单位非常好用。相比之下Grid 写三列布局代码最少、语义最清晰是现代项目里值得优先考虑的方案。它天然不会有 flex 那种「被压缩」的问题。1.5 方案三Float 浮动布局最古老需理解 BFCFloat 是「上古」方案但它能帮我们真正吃透 BFC所以值得讲。核心思路左右两列分别float: left/float: right中间列触发 BFC 来避开浮动。!DOCTYPEhtmlhtmllangzh-CNheadmetacharsetUTF-8/title三列布局 - Float/titlestyle*{margin:0;padding:0;box-sizing:border-box;}.layout{height:100vh;}.sidebar{width:200px;height:100vh;}.left{float:left;background:#f0c0c0;}.right{float:right;background:#c0c0f0;}/* 中间列触发 BFC避免被浮动元素覆盖 */.content{overflow:hidden;/* 关键开启新的 BFC */background:#fff;}/style/headbody!-- 注意float 方案必须让「浮动元素」写在前面 --divclasslayoutasideclasssidebar left左侧导航/asideasideclasssidebar right右侧广告/asidemainclasscontent中间主内容自适应/main/div/body/html代码思路解析左列float: left、右列float: right两者脱离文档流分别贴在容器左右两侧中间列如果不做处理会被浮动元素「盖住」所以给它overflow: hidden——这会让它开启一个新的 BFC从而不再与浮动元素重叠自动占满剩余空间。两个必须记住的坑DOM 顺序变了float 要求「浮动元素」必须写在前面否则浮不上去。这跟「主内容优先渲染」的诉求是冲突的所以 float 方案其实不利于语义化这也是它逐渐被淘汰的原因之一overflow: hidden的作用是触发 BFC不是单纯「裁剪溢出」。很多同学以为它只是隐藏滚动条其实它的真正作用是建立独立的排版上下文。1.6 三种方案对比方案代码量语义化是否需处理「压缩」问题兼容性适用场景Flex中好可保持 main 居中需flex-shrink: 0极好最常用推荐Grid少最好无好现代项目首选Float中差浮动弹需前置需overflow: hidden触发 BFC极好理解 BFC / 老代码维护小结能写 Grid 优先 Grid其次 FlexFloat 主要用于理解原理和阅读历史代码。二、TypeScript 工具类型2.1 为什么需要工具类型在大型项目里「类型」会被大量复用。假如我们有一个用户对象接口interfaceUser{id:number;name:string;age:number;email:string;}实际开发中经常遇到这类需求列表页只展示id和name不想暴露全部字段接口返回给前端时要剔除掉敏感字段比如email更新用户信息时所有字段都可选传哪个改哪个定义「状态码 → 错误提示」这种字典。如果每次都手写一个新接口字段一多就会又啰嗦又容易漏改。TypeScript 内置了一批工具类型Utility Types专门用来「基于已有类型加工出新类型」。下面逐个讲清楚。2.2 基础keyof 与 typeof理解工具类型前先掌握两个基础操作符。keyof取出一个类型「所有键名」组成的联合类型。typeUserKeyskeyofUser;// 等价于type UserKeys id | name | age | emailtypeof类型上下文里取出一个「值」的类型。functionfn(){return{x:1,y:2};}typeFnResultReturnTypetypeoffn;// { x: number; y: number }注意这里的typeof是「类型层面」的用法和 JS 里的运行时typeof是两码事别混淆。2.3 Pick挑选字段PickT, K从类型T里挑选出键集合K中的字段组成新类型。// 从 User 中只挑出 id 和 nametypeUserPreviewPickUser,id|name;constu:UserPreview{id:1,name:zmt,};// 如果写成 { id: 1, name: zmt, age: 18 } 会报错age 不存在于 UserPreview底层实现可略读理解即可typePickT,KextendskeyofT{[PinK]:T[P];};[P in K]是「映射类型」遍历K里的每个键用T[P]取出对应的值类型。K extends keyof T约束K必须是T的键防止写错。2.4 Omit剔除字段OmitT, K与 Pick 相反从类型T里剔除掉键集合K保留其余字段。// 从 User 中剔除 email得到不含敏感字段的安全类型typeUserSafeOmitUser,email;constsafeUser:UserSafe{id:1,name:zmt,age:18,};// email 已被剔除写 email 会报错典型场景接口返回给前端的 DTO数据传输对象通常要Omit掉密码、邮箱等敏感字段。2.5 重点Omit Pick Exclude这是本章最核心、也最容易被问到的一条等价关系OmitT, K 等价于 PickT, Excludekeyof T, K这条式子怎么理解分三步拆解keyof T拿到T所有键的联合类型即id | name | age | emailExcludekeyof T, K从这些键里删掉要剔除的K比如email剩下要保留的键id | name | agePickT, 剩下的键再用 Pick 把剩下的键从T里挑出来。「剔除 先算出要保留的键再挑选」——Omit 本质上是 Pick 的一个「组合拳」它自己没有独立的新能力只是把 Exclude 和 Pick 串了起来。TypeScript 内部正是这么实现的// TS 官方 Omit 的等价实现typeMyOmitT,KextendskeyofanyPickT,ExcludekeyofT,K;用代码验证一下结果完全一致typeUserKeyskeyofUser;// id | name | age | emailtypeKeepKeysExcludeUserKeys,email;// id | name | agetypeMyOmitUserPickUser,KeepKeys;// 等价于 OmitUser, email再单独看Exclude的底层实现加深理解typeExcludeT,UTextendsU?never:T;它利用「条件类型的分配律」当T是联合类型时会逐个判断T的每个成员是否属于U属于就变成never相当于丢弃不属于就保留。比如Excludeid|name|age|email, email前三个都不等于email所以保留email自己等于email所以变成never被丢弃。2.6 Partial全字段可选PartialT把类型T的所有字段都变成可选。typePartialUserPartialUser;// 现在所有字段都可选可以只传部分字段constpatchUser:PartialUser{name:张三,age:18,};// 甚至可以传一个空对象完全合法constemptyObj:PartialUser{};典型场景更新接口patch经常只传「要修改的那几个字段」用Partial表达最合适。底层实现typePartialT{[PinkeyofT]?:T[P];// 注意多了一个 ?表示可选};对比 Pick 的实现差别只在T[P]后面多了一个?。理解这个「映射类型 可选修饰符」的组合Pick/Omit/Partial 就全通了。2.7 Record 与 ReturnTypeRecordK, V构造一个「键类型为 K、值类型为 V」的对象类型常用于字典 / 映射表。// 键是 string值是 numbertypeDictRecordstring,number;constobj:Dict{a:1,b:2};// 经典场景HTTP 状态码 → 错误提示typeErrorMsgMapRecordnumber,string;consterrorMsgMap:ErrorMsgMap{400:请求参数错误,401:未登录请重新登录,403:权限不足拒绝访问,404:资源找不到,500:服务器内部错误请稍后重试,};// 配合函数使用取不到时给兜底值functiongetErrMsg(code:number):string{returnerrorMsgMap[code]??未知错误;}补充一个小知识HTTP 状态码的分段含义——1XX 执行中、2XX 成功、3XX 重定向、4XX 客户端错误、5XX 服务端错误。上面ErrorMsgMap就是用Record把这套映射固化成了类型安全的字典。ReturnTypeT取出一个函数的「返回值类型」。functionfn(){return{x:1,y:2};}typeFunReturnReturnTypetypeoffn;// { x: number; y: number }它依赖infer推断底层实现长这样了解即可typeReturnTypeTextends(...args:any)anyTextends(...args:any)inferR?R:any;2.8 Exclude 与 Omit 的区别很多同学分不清这两个一句话点破Exclude操作的是「联合类型」从一串键或字面量里删除某些成员Omit操作的是「对象 / 接口类型」从一个对象的字段里删除某些键。所以它们的「输入类型」不同、产出也不同Excludea|b|c, c得到a|bOmitObj, c得到一个新的对象类型。而Omit内部正是「借用了 Exclude 删除键再用 Pick 重建对象」这就是 2.5 节那条等价关系的由来。三、全文总结这篇文章围绕两条主线展开三列布局核心是「左右固定、中间自适应」。实现它的底层原理是格式化上下文——html根元素开启第一个 BFC块级元素默认只能从上到下排列要左右分列就必须通过display: flexFFC、display: gridGFC、float/overflow: hidden等方式开启新的格式化上下文。三种方案里Grid 最简洁、Flex 最常用、Float 最古老但最能帮助理解 BFC。TS 工具类型核心是「基于已有类型加工新类型」。Pick挑字段、Omit删字段、Partial全转可选、Record造字典、ReturnType取返回值类型。其中最重要的等价关系是OmitT,K PickT, Excludekeyof T, K——它说明 Omit 不是「原生能力」而是 Exclude删键 Pick挑键的组合拳理解了它整套工具类型就串起来了。四、核心知识点复盘知识点一句话结论文档流块级元素从上到下、行内元素从左到右BFC / FFC / GFC独立的排版作用域html根元素开启第一个 BFC开启新格式化上下文flex/grid/float/position/overflow: hidden等三列布局左右定宽 中间自适应flex: 1/1fr/overflow: hiddenflex-shrink: 0防止 flex 子项被压缩、固定宽度失效keyof T取出 T 所有键组成的联合类型PickT, K挑选 K 里的字段组成新类型OmitT, K剔除 K 里的字段ExcludeT, U从联合类型 T 中删除属于 U 的成员等价关系OmitT,K PickT, Excludekeyof T, KPartialT所有字段转可选用于 patch 更新RecordK, V构造键 K 值 V 的字典类型ReturnTypetypeof fn取函数返回值类型五、常见问题 / 避坑指南Q1三列布局里中间列没撑满 / 左右列被压缩了怎么办A最常见原因是 flex 方案里漏写了flex-shrink: 0。flex 子项默认flex-shrink: 1允许收缩当空间不足时左右两列会被挤窄。给固定宽度的.sidebar加上flex-shrink: 0即可。Grid 方案则天然没有这个问题。Q2overflow: hidden在三列布局里到底起了什么作用A它不是「隐藏滚动条」那么简单而是触发 BFC。float 布局中浮动元素会脱离文档流、盖住后面的普通元素给中间列加overflow: hidden会让它开启新的 BFC从而不再与浮动元素重叠自动占满剩余宽度。这是理解 float 布局的关键。Q3为什么 float 方案里main要写在两个 aside 后面Afloat 要求「浮动元素必须出现在文档流中更靠前的位置」才能正常浮起来。这跟「主内容优先渲染」的诉求相冲突也是 float 被 flex / grid 取代的重要原因之一。Q4keyof和typeof老分不清怎么办A一句话区分——keyof取「键」typeof取「类型」。keyof User得到键名联合类型typeof 变量在类型位置得到该变量的类型。ReturnTypetypeof fn就是「先拿到 fn 的类型再取它的返回值类型」。Q5Exclude和Omit到底什么时候用哪个A操作对象是「联合类型」用Exclude删几个字面量成员操作对象是「对象 / 接口」用Omit删几个字段。记不住也没关系记住Omit Pick Exclude这条关系一切都能倒推出来。Q6Partial之后字段都可选会不会丢失类型安全A不会「丢失」反而是「放宽」。PartialUser要求你写出来的每个字段如果有类型仍然正确只是允许你不写某些字段。更新接口patch 语义用它是安全的读取/展示场景则应该用完整的User或Pick/Omit出来的类型别滥用。Q7这些工具类型需要自己写吗A不需要。它们都是 TypeScript 内置的直接使用即可。但理解它们的底层实现尤其是映射类型[P in keyof T]和条件类型T extends U ? X : Y能让你举一反三以后遇到Required、Readonly、Record等同类工具类型时一眼就能看懂本质。
返回列表