前言
祝大家在新的一年:大吉大利,身体健康,迎大运,发大财!
吐槽随便侃侃,无感请直接跳过到正文。
注册了快 7 年的账号,终于发了第一篇文 你敢信 ~~ 哈哈,今天开张了,撒花。至于契机嘛… 司倒,失业之
T_T。
最近刷看到类似的帖子也挺多的,ORZ 放心,真不是来输出负面情绪的 ~ 主菜马上就到。
y 情后各行各业都不容易啊,待了六年的公司也熬不住去年末倒下了(还有 3 个月工资拖欠着,希望仲裁给力吧)。个人也长时间呆在舒适区,大环境每况愈下,经历了这波社会毒打后,时常感到焦虑。经过了几天的 emo 期(并没有,疯狂打球笑嘻嘻 … 但心里苦)哥们已经调整好了,等你回来 生活终归是要继续的。
给自己放放假,调整心态,查缺补漏,为了找到新工作,自己制定了如下小目标:
- ✅ 1. 重启技术博客 blog.fridolph.top,坚持输出
- ✅ 2. 做几个实战项目(于是这篇文就诞生了)
- ⏰ 3. 整理前端知识大纲,形成思维导图
- ⏰ 4. 逛掘金,刷面经 + 整理(有些想法,于是开了新坑,再过几天就 OK)
- 💊 5. 找到工作转正前
封印Steam
– “你算什么男人算什么男人,眼睁睁看帕鲁走却不闻不问” T_T
我原本是想重构一下我之前写的在线简历,采用 Vue3 + TS + Pinia 来实现。几下敲完代码,我自己都觉得没啥东西。简历上个人作品,开源那两栏空空如也 = = 之前老刷到tailwind
的文章,遂决定学习换个心态尝试学习,加到项目中,现切现做,这瓜绝对保熟
。
之前刷到 前端发展趋势的预测 之类文,很赞同一些观点。学历,简历只是敲门砖,在这个越来越卷的环境,竞争力不足,以后还是要多关注学习下 Node、Next.js,伪全栈万金油是个可选方向。
通过这篇文章你可以
- 📖 学会在项目中灵活使用 TailWindCSS
- 📮 了解 TailWindCSS 的核心概念
- 📝 获得我整理的一份
Tss 速记指南
- ⬆️ 随我一起学习 CSS “新” 特性
- 🚀 本项目 Vite + Vue3 实践及相关优化
- 🌹 Fork + Star 即得一份在线简历模板,现改现用
- github: github.com/Fridolph/my…
- 在线浏览:resume.fridolph.top/
注:本项目使用 pnpm 作为包管理工具,请升级 Node 版本到 16.22.2 以上
下面是我学习 TailWindCSS 的过程,本文不会先上来讲大段概念,而是通过实际项目入手,获得正反馈后再反思、理解,并思考总结写出该篇文章。
为什么想学 TailWindCSS
之前也看到过类似的文章推送,我大致的印象就是这不就是 BootStrap 嘛。毕竟,人的精力是有限的,现有的 BEM 定义 class 一把梭就挺快的,为什么还要用这种过于原子化
的 CSS mixin,import 肯定又是 几百 k。 不知道屏幕前的你有没这些疑问。
每一个能够流行起来的高 Star 项目内涵都不会简单,反正练手,正好了解一番,毕竟实践过后,才有发言权
不是嘛
而巧合的是,我有一个现成的案例。以前为了找工作,我也做过在线简历项目。做完新版简历后,不论是代码的提炼还是规范性方面都有了很大的提升。这也算是对过去几年的努力有个交代吧,各个方面有了一个明显的对比。
本文中我简称 TailWindCSS 为 Tss。可能会有误导,因此在其他地方请不要使用这个名词
。
在比较的同时,我会插入一些 TailWindCSS 文档中的概念,这样更容易让我们理解和体会到 Tss 的特性。
最重要的是切入点,本文通过实际案例让你快速了解 Tss,对感兴趣的部分可以对照文档自行学习,可谓事半功倍。废话不多说,show you the code
!
新旧项目对比
下面开始差生示范,只对比 HTML 结构和 CSS,JS 代码就暂省略了
App.vue
老项目的 app.vue style 没有加 scoped,我们编写的 css,比如 .page-resume p { margin: 10px 0; }
这类代码,一不小心就会应用到其他模块中,而很难发现。命名是门学问,诸如 BEM 之类的规范有很多,这也是业界主流吧,但随着项目内容增多,样式会越来越多臃肿。
我正好遇到过项目中三人同时写一个路由下的三个子页面,都有查询和表格,另个页面是以图文为主,很多链接,提示说明。 有个同事下了如下代码:
<style>
.xxx-page a {
color: var(--text-link) !important;
}
.xxx-page .title {
font-size: 12px !important;
}
</style>
<style scoped>
/* 页面相关样式 */
</style>
template 都是复制粘贴,page 名还没改,一旦切到这页面再切到其他页面就会覆盖某些样式 …… 产生极其强大的魔法
虽然标签里的 class 会变得很臃肿。但单看 DOM 结构,这是良好符合 WAI-ARIA 规范的。有以下好处:
-
不用纠结命名,从此告别
xxx-wrapper
、xxx-title
、xxx-list
、xxx-item
-
避免潜在的样式冲突。因为框架都给你定义好了
你可能会吐槽,这 html 一大堆很难维护,css 是好了可 html 又病了
关于这点,后续章节 @apply 或者 tailwind.config.js 可能解决,但注定要在灵活性
上做一定取舍
Hobby > movie 过渡
如果上面的不能说服你,那这个例子可以补充一些论据。
Hobby 模块里都是一些简单的动画实现,其中 movie 在旧模块中是这么实现的:
这只是其中一个组件,我们给了不同命名来加以区分。最开始写这块的时候没想太多,也没拆分组件,一个文件几百行 css 代码出现了(屎山就是这么炼成的)
项目中 = = 动辄几百行的还算好,经常 template、js 代码加起来几百行,若是再来上千行的 css,加上项目赶工期,就更头大。项目完给时间大家自觉优化还好,但现实情况,经常继续改 bug,同时又要做新功能 … 看着那座”山”,不由得一声叹息
<!-- 神奇,我真的没写css代码,也不用去想 wrapper item box 之类的命名了 -->
<div class="flex flex-wrap justify-content">
<div class="group overflow-hidden relative ring-1 ring-slate-200 rounded flex items-center justify-center size-12 mr-3 mb-3 origin-center align-center">
<Iconfont class="size-12 absolute left-0 top-0 translate-x-[10px] translate-y-1 transition-transform ease-linear duration-500 group-hover:-translate-y-12" />
<Iconfont class="size-12 absolute left-0 top-0 translate-x-[10px] translate-y-1 transition-transform ease-linear duration-500 group-hover:-translate-y-12" />
</div>
</div>
上手 Tss 后,最大的一个变化和惊喜就是,只要你知道你想实现什么,都可以直接用 class 实现最终效果。
可能会吐槽,class已经一坨完全不能看,放心,这只是开始,后面有解决方案
没有写一行样式,完美实现了相同的效果,而且复用性强。当你把这串 template 丢给同事,只要有 tailwind 不需要引入 css 文件,不用担心 scoped 和样式覆盖就能得到一样的效果。
并不是说 BEM 不好,我们甚至可以两者结合,如果 Tss 提供的类名不足以实现你想要的效果,可把大致布局文字颜色等先写好,再另起一个 class 继续写 CSS 即可。
对 Tss 动画感兴趣的小伙伴可参考文档,玩出更多花样 www.tailwindcss.cn/docs/transi…
Hobby > shake 动画
上面的例子可实现过渡效果了,那动画了? 在 class 里写@keyframe 嘛?
这里就延伸出 Tss 可配置文件,tailwind.config.js
。不仅仅是 动画,其他类似断点,和一些自定义类名,mixin 等,我们都可根据实际情况自行添加。
原则上,能通过 base core 完成的功能就尽量别放到配置中,因为打包会生成额外的代码,除非你确定这部分会多处使用和利于抽象。
/** @type {import('tailwindcss').Config} */
export default {
theme: {
extend: {
animation: {
singerShake: 'singerShake linear infinite',
},
keyframes: {
singerShake: {
'0%, 100%': { transform: 'translateX(0)' },
'10%, 30%, 50%, 70%, 90%': { transform: `translateX(-2px) scale(1.05)` },
'20%, 40%, 60%, 80%': { transform: `translateY(2px) scale(1)` },
},
},
},
},
}
在代码中直接 animate-[singerShake_3s_ease-in-out_infinite]
即可使用。至于细节和命名等看文档就好,后面的 Tss 实用指南会提到。
<div class="transition-all duration-300 ease-linear origin-center hover:bg-slate-500 hover:rounded-full hover:text-white">
<Iconfont
class="size-12 absolute left-0 top-1 rotate-0 text-center transition-all origin-center group-hover:animate-[singerShake_3s_ease-in-out_infinite]"
:size="28"
name="changge"
/>
</div>
CSS 更可控
为了最快实现一个公共列表的样式,写出这么一串样式。如果要优化,在前期,可以通过定义 class,把公共样式抽离出来。
本简历的标题 – 内容(行) – 左 label 右 text,结构也大致如此。上述样式可实现该结构,但随着项目内容变多,需求变得繁杂:
- label 应自适应,可以给一个最小宽度,但在移动端如果太长能换行
- 行结构只有内容,没有 label。或 行结构只展示标题(粗体)没有 text,支持 icon
- 作为子组件时,应该统一对齐 padding
这不,html 越来越多,很多样式感觉能公共,但又不好复用。(实现开发经常遇到这类情况)与其花时间做优化,不如直接新撸来得更快
而用 Tss 这些布局-排版-文字 相关样式类名可直接写到 class 中,复用性大大增强。
<div class="magic-wrapper gird-cols-3 bg-white gap-3 font-bold text-sm leading-5"></div>
<style scoped>
.magic-wrapper {
/* ... */
}
</style>
这样的好处是我们通过 TW 抽离了很多公共样式,而真正额外的样式则是 scoped 里的,不会污染其他作用域,这部分样式代码会比一开始少很多。若其他人接手,看到这部分样式能更快知道改动效果。
到这里相信很多朋友会发问:
-
这和 Bootstrap 有什么区别?
-
css 是好维护了,但 html 结构混乱得不堪入目,10 行简单结构,给你能搞出 20 行?
别急,优化的事一步步来,下个章节会结合项目实践,慢慢解释
从布局开始,更高效的响应式支持
如果一个结构细节多,需要针对更多断点情况,媒体查询的代码很更让人头疼了
Tss 实现起来非常简单快速,手敲示例代码:
wrapper 这类命名不需要,这里只为了大家能看清楚结构和上述 css 进行对比
非常建议手敲代码,能更快上手。这里实现下图片的 CSS,放心,绝对没有添加剂!
<div class="page-resume lg:py-[5vh] lg:px-[5%]">
<div class="group side-wrapper md:h-screen lg:h-[90vh] p-5 pr-[21px] hover:pr-[15px]">
<div
class="list-wrapper md:h-[calc(100%-262px)] overflow-hidden group-hover:oerflow-y-auto"
>
<div class="hero-box pr-[6px]"></div>
</div>
</div>
<div
class="main-wrapper lg:h-[90vh] overflow-hidden pr-[26px] hover:overflow-y-auto hover:pr-5"
></div>
</div>
vscode安装插件后上手更佳,智能提示放心写
你可以试着安装 VScode 插件 Tailwind CSS IntelliSense
看到这里如果你对 md
、 lg
这些名称感到似曾相识,那么文档是最终的归途
www.tailwindcss.cn/docs/respon…
TailWindCSS 核心概念
简历很简单,但这并不就是 Tss 最佳实践,还很多东西等待着我们挖掘
有了这样一个开局,如果你已经对 Tss 产生了兴趣,那么接下来的官网概念相信会容易接受得多
- 效用第一 Utility-First Fundamentals
- 处理状态 Handling Hover, Focus, and Other States
- 响应式设计 Responsive Design
- 深色模式 Dark Mode
- 重用样式 Reusing Styles
- 添加自定义样式 Adding Custom Styles
- 功能和指令 Functions & Directives
效用第一
引用下文档内容:
- 您不会浪费精力发明类名。不再需要添加像 sidebar-inner-wrapper 这样的愚蠢的类名只是为了能够设计某些东西,也不再为实际上只是一个 Flex 容器的东西的完美抽象名称而烦恼。
- 你的 CSS 停止增长。使用传统方法,每次添加新功能时,您的 CSS 文件都会变得更大。有了实用程序,一切都可以重用,因此您很少需要编写新的 CSS。
- 做出改变感觉更安全。 CSS 是全局性的,当你做出改变时你永远不知道你会破坏什么。 HTML 中的类是本地的,因此您可以更改它们,而不必担心其他内容会被破坏。
处理状态
Tss 包含几乎所有的修饰器,包括:
- 伪类,例如
:hover
、:focus
、:first-child
和:required
- 伪元素,例如
::before
、::after
、::placeholder
和::selection
- 媒体和功能查询,例如响应式断点、深色模式和偏好减少模式
- 属性选择器,例如
[dir="rtl"]
和[open]
我们平常写样式的思路是: 1. 定义 class; 2. 添加状态; 3. 添加对应样式代码
<div class="container"></div>
<style>
.container {
background-color: black;
}
.container:hover {
background-color: white;
}
</style>
而Tss 则是编译成
.hover\:bg-white:hover {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
在 Tailwind 中,您不是将 hover 状态的样式添加到现有类,而是向仅在 hover 时执行某些操作的元素添加另一个类。
www.tailwindcss.cn/docs/hover-…
更多可参考文档,需要时再看就好,学一点用一点会一点。
响应式设计
上一快状态里也有响应式标记,这里就不多提,记住一点就好
- 移动优先
默认情况下,Tss 使用移动优先断点系统,类似于您在 Bootstrap 等其他框架中可能使用的系统。
这意味着无前缀的实用程序(如 uppercase)对所有屏幕尺寸都有效,而带前缀的实用程序(如 md:uppercase)仅在指定的断点及以上位置生效。
深色模式
暂略过,核心就是通过改变状态标记,这里挖个坑吧,后续会为简历添加深色模式。敬请期待
(新增内容)快速实现主题切换
不用后续,克服困难,现在开码。
提前定义好变量,便于维护
说干就干。为了更好地使用主题模式,我建议提前定义好主题相关的变量,并为这些变量添加一个 dark 的前缀(根据实际情况自行调整)。
其实官方文档就是最好的示例。当我们要实现深色模式或者多主题时,需要考虑需要改变哪些内容?我简单总结如下:
- 页面的大背景色(如果有的话)
- 布局模块(如:header、main、aside、section、footer 等)的背景色
- 模块的常态样式,如:边框(内边框、外边框还是 background-image 等)圆角等
- 文字的颜色
- 阴影效果
- 其他等等…
当我们考虑到以上内容之后,再来思考状态改变时的颜色,例如 hover、focus、active 等。这只是一个思路,具体设计主题系统还需要 UI 设计同学的参与,还有许多细节值得我们深入研究。这里只是一个简单的参考。
/* src/styles/var.css */
:root {
/* theme-light */
--page-bg: #c5dae2;
--bg-hover: #f5f5f5;
--text: #607d8b;
--text-hover: #00bdea;
--main-bg: white;
--aside-bg: #f5f5f5;
--scroll-piece: rgba(218, 230, 238, 0.9);
--scroll-thumb: rgba(188, 198, 223, 0.8);
--border: #e5e7eb;
--focus-border: #bae6fd;
--card-bg: #f0f9ff;
/* dark-mode */
--dark-page-bg: #1f2635;
--dark-bg-hover: #151e31;
--dark-text: rgb(145, 158, 175);
--dark-text-hover: rgb(185, 196, 212);
--dark-main-bg: #151e29;
--dark-aside-bg: #131822;
--dark-scroll-piece: rgba(18, 40, 54, 0.9);
--dark-scroll-thumb: rgba(82, 86, 95, 0.8);
--dark-border: rgb(13, 39, 53);
--dark-focus-border: rgb(21, 53, 70);
--dark-card-bg: #0a2c42;
}
我把使用到的色彩变量,都加了对应的 dark 副本,在用到这些的地方提醒自己写一个 dark:xxx
即可
你可以点开简历,查看下效果。下面以最外层wrapper为例子:
<div class="w-full min-h-screen md:flex md:justify-center md:align-middle p-0 bg-[var(--page-bg)] md:py-[5vh] md:px-[5vw] dark:bg-[var(--dark-page-bg)] dark-transition">
<section class="relative sm:w-full md:w-full max-w-[1600px] flex flex-col md:flex-row justify-center align-middle text-color shadow-inner sm:shadow-2xl dark:shadow-neutral-700">
<!-- 内容省略 -->
</section>
</div>
当主题为 dark
时,容器的背景色会变成 var(--dark-page-bg)
;section 默认是内阴影,dark主题后,会应用设定的 box-shadow shadow-neutral-700
tailwind 主题配置
根据文档,我们需要先配置 tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: 'class',
// ...
}
// 名称可自行改动,具体参考文档,这里不多赘述
配置好后,只要我们在 html 标签上添加 class="dark"
并为要改变的元素上按下面的方式增加对应类名即可
<!-- Dark mode enabled -->
<html class="dark">
<body>
<!-- Will be black -->
<div class="bg-white dark:bg-black">
<!-- ... -->
</div>
</body>
</html>
注意:我们的页面是 create(App).mount(‘#app’) 为了实现上述对 html class 的添加,我们还需要进行额外的 DOM 操作
我添加了一个 PageTool / ThemeChange
(小项目其实用不上状态管理,这里只为展示Pinia
用法请勿吐槽 )
import { defineStore } from 'pinia'
export const useCommonStore = defineStore('common', {
state: () => ({
theme: 'light',
}),
getters: {
isDark: (state) => state.theme === 'dark',
},
actions: {
switchTheme() {
if (!this.isDark) {
this.theme = 'dark'
document.documentElement.classList.add('dark')
} else {
this.theme = 'light'
document.documentElement.classList.remove('dark')
}
},
},
})
现在我们绑定好方法就可以快速切换主题了
<div class="switch-wrap" @click="handleSwitch">
<Iconfont v-show="isDark" name="night-mode-fill" />
<Iconfont v-show="!isDark" name="light-bulb" />
</div>
<script setup lang="ts">
import { toRefs } from 'vue'
import { useCommonStore } from '../../../store/common'
import Iconfont from '../../Iconfont/Iconfont.vue'
const store = useCommonStore()
const { isDark } = toRefs(store)
const handleSwitch = () => {
store.switchTheme()
}
</script>
综上所述,这个简历项目,在 App.vue 的结构清晰明了,只需要在 wrapper、main、aside 等地方填入相应的 dark:xxxx 即可,比如 <main class="... dark:bg-[var(--dark-main-bg)] ">
。
优化 – 添加平滑过渡
这部分代码可能会变得很冗长,慢慢地,tailwind 的这一缺点就凸显出来了。不过这并非大问题,通过使用 @layer
,我们可以将这些公共样式抽离出来,这是我在项目开发过程中一点点进行优化的成果。
感兴趣可点击切换,试下效果。会了黑白主题,再添加其他自定义主题不在话下。
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.dark-transition {
@apply transition-colors duration-1000 ease-in-out;
}
}
这样再切换,我们的主题就拥有良好的过渡效果了。
重用样式
上文提到实现 Hobby 模块的 movie 效果时,对 item 写了一堆样式。
<div class="flex flex-wrap justify-content">
<div class="group overflow-hidden relative ring-1 ring-slate-200 rounded flex items-center justify-center size-12 mr-3 mb-3 origin-center align-center">
<!-- ... -->
</div>
<div class="group overflow-hidden relative ring-1 ring-slate-200 rounded flex items-center justify-center size-12 mr-3 mb-3 origin-center align-center">
<!-- ... -->
</div>
<div class="group overflow-hidden relative ring-1 ring-slate-200 rounded flex items-center justify-center size-12 mr-3 mb-3 origin-center align-center">
<!-- ... -->
</div>
<!-- 很多重复结构 -->
</div>
在项目中有 8 个 item,需要将相同的东西复制八遍吗?起初可能是这样,尽管看起来有些辣眼睛
,但至少功能已经实现了。无意中翻看文档时,我发现了以下的信息:
很多时候,类似这样的重复甚至不是真正的问题,因为它们通常都可以集中在一个地方,甚至实际上根本不存在,因为您正在迭代一组项目并且只编写一次标记。如果您只需要在单个文件中重用需要重用的样式,那么多光标编辑和循环是管理任何重复内容的最简单方法。 —— 官方文档
好的,给我我笑了 … 于是继续编码。 额,额 …… 当然不止是这样,我们有几种方式来优化
*: 选择器
<div class="parent-comp flex flex-wrap justify-start *:overflow-hidden *:relative *:ring-1 *:ring-slate-200 *:rounded *:flex *:items-center *:justify-center *:size-12 *:mr-3 *:mb-3">
<div class="child-comp"></div>
<div class="child-comp"></div>
<div class="child-comp"></div>
<div class="child-comp"></div>
<div class="child-comp"></div>
</div>
使用@apply 提取类
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.hobby-item {
@apply overflow-hidden relative ring-1 ring-slate-200 rounded flex items-center justify-center size-12 mr-3 mb-3 origin-center align-cente;
}
}
笔者也只是初窥门径,还有更多用法学习,尽在官方文档(再次强调)
添加自定义样式 Adding Custom Styles
如果您想更改调色板、间距比例、排版比例或断点等内容,请将自定义添加到 tailwind.config.js 文件的主题部分:
/** @type {import('tailwindcss').Config} */
export default {
content: ['./index.html', './src/**/*.{ts,tsx,vue}'],
theme: {
container: {
padding: '1rem',
},
screens: {
sm: '640px',
md: '1024px',
lg: '1376px',
xl: '1920px',
},
},
}
项目中如何优化 Tss 代码
我新建了一个 styles/tailwind.css
专门存放 Tss 相关代码
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.hobby-fromBottom {
@apply translate-y-full transition-transform ease-in-out duration-500 group-hover:translate-y-0;
}
.hobby-fromTop {
@apply -translate-y-full transition-transform ease-in-out duration-500 group-hover:translate-y-0;
}
.hobby-fromLeft {
@apply -translate-x-full transition-transform ease-in-out duration-500 group-hover:translate-x-0;
}
.hobby-fromRight {
@apply translate-x-full transition-transform ease-in-out duration-500 group-hover:translate-x-0;
}
.hobby-fadeIn {
@apply transition-opacity ease-in-out duration-500 opacity-0 group-hover:opacity-100;
}
}
结构简洁多了,把布局颜色等样式代码单独写 css,过渡相关样式写到了单独的文件中便于维护。
需要注意的是:hobby-fromBottom 这种用法在打包后会有编译开销,实际我们只用到一次的话没必要这样使用。这里只是方便展示
<div
class="absolute w-full h-full p-2 z-10 left-0 right-0 top-0 bottom-0 rounded bg-sky-50"
:class="{
'hobby-fromBottom': transitionType === 'fromBottom',
'hobby-fromTop': transitionType === 'fromTop',
'hobby-fromLeft': transitionType === 'fromLeft',
'hobby-fromRight': transitionType === 'fromRight',
'hobby-fadeIn': transitionType === 'fadeIn',
}"
>
<slot></slot>
</div>
至此已经可以回答之前的疑问了,和 Bootstrap 像吗,是的。但不仅仅是这些,Tss 不仅可定制样式,而且更加灵活,而库体积却更小。
功能和指令 Functions & Directives
Directives
指令是自定义 Tss 特定的规则,您可以在 CSS 中使用,为项目提供特殊功能。
/**
* 这会注入 Tailwind 的基本样式以及插件
*/
@tailwind base;
/**
* 这会注入 Tailwind 的组件类和任何组件类通过插件注册
*/
@tailwind components;
/**
* 这会注入 Tailwind 的实用程序类和注册的任何实用程序类通过插件
*/
@tailwind utilities;
/**
* 使用此指令来控制 Tailwind 注入悬停、焦点、响应式、深色模式以及其他变体
* 如果省略,Tailwind 会将这些类附加到默认情况下您的样式表
*/
@tailwind variants;
Tss 速记指南
一个新的东西需要学习成本。看到这的你,我相信已接受 Tss,并想投入使用了。这里结合笔者的经验,整理了一些速成技巧
编写 Tss 建议遵循着以下思路和顺序来:
- 布局
- 间隔 margin、padding
- 背景
- 边框
- 尺寸 width、height
- 排版(文字相关)
- 过渡、动画
- 其他交互
- cursor
- hover
这里以组件 TitleDesc.vue 为例。简历项目中在 ProjectWrap 用到来展示标题 + 内容,结构如下:
<template>
<div class="flex flex-between flex-nowrap border-b-2 my-3 py-2 gap-x-5 text-lg">
<div class="text-justify">
<Iconfont v-if="showIcon" class="align-middle"></Iconfont>
<span class="ml-1 font-bold align-middle text-[#333]">{{ title }}</span>
</div>
<div>
<slot name="extra"></slot>
</div>
</div>
<div class="bg-gray pl-3 text-base text-[#777]">{{ description }}</div>
</template>
思路有了一切都很简单,Tss 很多 Class 和原生缩写一样,很容易记,看一遍基本就会了
这里整理一些常用 和 容易错误的:
布局相关
display
Class | Properties |
---|---|
block | display: block; |
hidden | display: none; |
inline-block | display: inline-block; |
inline | display: inline; |
flex | display: flex; |
grid | display: grid; |
container 在当前断点下都处于 width: 100%
gap 是真的好用,前提是在 flex 或 grid 下生效
overflow
Class | Properties |
---|---|
overflow-auto | overflow: auto; |
overflow-hidden | overflow: hidden; |
overflow-clip | overflow: clip; |
overflow-visible | overflow: visible; |
overflow-scroll | overflow: scroll; |
position
Class | Properties |
---|---|
static | position: static; |
fixed | position: fixed; |
absolute | position: absolute; |
relative | position: relative; |
sticky | position: sticky; |
size 尺寸这里单独说一下
Tss 里的基本单位 1rem = 16px 这点非常重要,请记住。对于尺寸,1 TSS 标准单位大小 = 4px (0.25rem)
后面跟的 left-1、size-10 看到这种默默乘 4 就可以换算出 left-1(left:4px)、w-8(width: 32px)、mb-4(margin-bottom: 16px)
Class | Properties |
---|---|
m-0 | margin: 0; |
w-px | width: 1px; |
mx-auto | margin-left: auto;margin-right:auto; |
py-auto | padding-top: auto;padding-bottom:auto |
h-full | height: 100%; |
left-1/2 | left: 50%; |
排版
- 0 就是 0px
- px 一般都是 1px
- full 表示 100%
- x 横坐标,如
mx
就是 margin-left、margin-right - y 纵坐标,同上
py
就是 padding-top、padding-bottom - 加入 1/3 这样的,表示百分比,具体看文档
- 若想精确的,可以自定义
-[x]
,如w-[66px]
、h-[calc(100vh-50px)
背景
Tss 的写这类复合样式,都是要拆开写的,虽麻烦,但本质上也算是解耦嘛
后续 flex、border、transition 这类属性也类似,我们只要知道大致 value 即可,这么一想也挺好,对 CSS 越熟悉就越容易上手 Tss。
background 本身是 background-color、background-image、background-position、background-repeat、background-size 多个属性的缩写。
Class | Properties | Desc |
---|---|---|
bg-none | background-image: none; | – |
bg-white | background-color: white; | 颜色 |
bg-center | background-position: center; | 位置 |
bg-cover | background-size: cover; | 尺寸 |
bg-contain | background-size: contain; | 尺寸 |
bg-no-repeat | background-repeat: no-repeat; | 重复 |
用这类属性,需要根据 bg-[效果]
带来的实际效果提炼关键点。这么想就容易上手了
Transform
常用的都在这里,知道这几个即可:
- translate 同 left right ,直接写
- scale 同 opacity,需注意 Tss 里的像百分比的值都乘了 100
- rotate 官方只制定了几个常用的,这类自己写就好,如
rotate-[46deg]
- skew 同上
- origin
origin-center
为 transform-origin: center;
transform-gpu
就是面试中常考的的开启 gpu 加速
但没看到对 translate-z 的支持,
perspective
只能手动添加了
若有错误麻烦指正下,谢谢
来记点不一样的
上面说了关键点在于应用 CSS 带来的实际效果。再来看下面这些,你就会容易理解得多:
Class | Properties | Desc |
---|---|---|
bg-white | background-color: white; | 颜色 |
text-base | font-size: 1rem; line-height: 1.5rem; | 字体和行高 |
truncate | overflow、text-ellipsis、white-space | 三剑客文字省略你懂的 |
italic | font-style: italic; | 斜体 |
font-bold | font-weight: 700; | 加粗 |
leading-5 | line-height: 20px; | 行高 |
text-center | text-align: center; | 文字居中 |
align-middle | vertical-align: middle; | 内联垂直对齐 |
rounded | border-radius: 4px; | – |
divide-x > + | border-right-width: 0px; border-left-width: 1px; | 不用单独写边框了 |
divide-y-2 > + | border-top-width: 2px; border-bottom-width: 0px; | 不用单独写边框了 |
outline-1 | outline-width: 1px; | 外边框,这几种根据实际需要选择即可 |
ring-1 | 太多省略 | 本质为 box-shadow 模拟的边框 |
opacity-0 | opacity: 0; | 透明 |
opacity-100 | opacity: 1; | 不透明 |
visible | visibility: visible; | 典,虽隐藏了但保留 DOM 位置,影响回流 |
invisible | visibility: hidden; | 本质为 box-shadow 模拟的边框 |
cursor-pointer | cursor: pointer; | 聚焦 |
cursor-not-allowed | cursor: not-allowed; | 禁止 |
pointer-events-none | pointer-events: none; | 阻止浏览器事件 |
select-none | user-select: none; | 禁止选中 |
注:用 text 千万要注意
line-height
,虽然很想吐槽 明明是 font-size ,但整个 text 很异类.
如果只想使用 font-size 的话,直接text-[14px]
这样就好了
上述这些浏览一遍就可快速上手,遇到不会的再查文档就是。
学到的 CSS 新特性
1. display: flow-root;
flow-root
该元素生成一个块级元素盒,其会建立一个新的块级格式化上下文,定义格式化上下文的根元素。
IE8 启动 …… 额,不是 还在用 clear 清楚浮动嘛? float 毕竟是针对内联图文的,
如果想让之后的元素不受到浮动影响,可以直接新建一个元素,并使用该属性
2. grid 布局快速上手
四列布局是吧? 来了,瀑布流?不在话下
<div class="grid grid-cols-4 gap-4">
<div>01</div>
<!-- ... -->
<div>09</div>
</div>
更多参考:www.tailwindcss.cn/docs/grid-t…
3. scroll-behavior: smooth;
论回到顶部的四种写法:
- jQuery.animate({scrollTop:0})
- window.scrollTo(0, 0)
- document.body.scrollTop = 0
如果想丝滑滚动还要写一堆 js 代码,防抖啊,requestAnimationFrame
又给整出来了,现在不要 998,只需一句 css scroll-behavior: smooth
,结合 <a>
锚点即可实现,浏览器更懂浏览器。
此时一位路过的朋友提出兼容 IE8(大哥现在 2024 React 都 18 了,vue 都 3 了,全民 AI 时代您还搁这 IE。抗击旧世界残党我辈义不容辞)掘金内外又瞬间充满了快活的空气
4. caret-color
用于控制文本输入光标颜色 caret-color: orange;
<input class="caret-orange-200 ..." />
5. background-clip
设置元素的背景(背景图片或颜色)是否延伸到边框、内边距盒子、内容盒子下面,在这里,头像下的文字就是 background-clip: text
和 linear-gradient
实现的。
更多可看文档 developer.mozilla.org/zh-CN/docs/…。
6. will-change
用于优化即将发生变化的元素的动画。官方建议慎用,在明确 transition perproties 的情况下可以提升性能
7. postcss
我发现两者结合已能满足大部分功能和场景,什么 sass(安装就像买彩票) scss、stylus 我都不需要了
8. 更多选择器和修饰符
再帖一遍文档,真的很好用
www.tailwindcss.cn/docs/hover-…
一个好的优化原则就是,能用 css 的尽量用 css 实现。 合理选择器和好处在于 它的权重和 class 都是相同的,知道这点,就可以让我们可以少写很多没必要的高权重选择器
9. 了解到 WAI-ARIA
感兴趣可看我之前的一篇文,如果需要,可以向标签添加 data 和 aria 属性,结合 role,提供更好的用户可访问性。
blog.fridolph.top/2024/01/26/…
还有更多等待发掘
HTML、CSS,简单和立即反馈,通过这次学习感觉找回了初学前端时的快乐,这算是一个意外之喜吧。
在写博客时,也发现一些可尝试的新东西,预计就随便写写 2000 字不能再多,没想到码了这么多字
这篇文章真是从 2000 字 -> 想着扩展点吧写到 5000 字 -> 好人做到底完善到 1W 字 ~ 骄傲,原来我不是文废,语文老师可以放心了!
总结
考虑到这一章节,这实质上也是对过往经验的一种总结。在我早期的项目开发中,往往采用一种即兴的方式编写 css 和模板,导致组件粒度不够细,复用性不高,同时也存在一定程度的未提取冗余样式的问题。随着项目复杂度的增加,势必出现一些无法解决的样式问题,不得不通过全局样式甚至使用“important”来进行覆盖。
随着对 Tss 的学习,加上最近也是在复习,想到就会尝试添加新的特性,不知不觉 2 天搞定的项目断断续续码了十多天,但这一过程非常享受,我重新找回了对编码的好奇心和热情。
关于技术选型
-
本文不是说服你去用Tss,而是多一个了解,多一个待选方案
-
项目中添加 TSS 有一定的学习成本
刷到些评论,感觉更多的困难是能否让同事接受 TSS 的核心概念。刷文档做 demo 实际 1 天即可上手。还有另一好处是让平时不太关注 CSS 的小伙伴,(不考虑兼容性情况下)能用到一些新特性。
- 对于灵活性的取舍
我理解熟悉 TSS 带来的高效开发和其带来的 html 臃肿问题,@apply
(tailwind.config.js) 如何使用决定了我们需要牺牲怎样的灵活性以换来更高的可维护性
- 代码体积小,灵活度高,推荐一试
从下面可看到打包后 css 代码部分很小(只编译打包了我们用到的部分)这也是与其他写好 .xs .md .m-1 .m-2 .p-b-1 .p-t-2
样式的 UI 的不同之处。
个人总结的一些开发实践
-
对于入口文件 app.vue,应确保其作为容器组件,尽量减少业务逻辑的耦合。
-
在划分组件和视图时,要遵循
components
和views
的原则,以减小粒度并分离业务逻辑。 -
善用
hooks
,尤其在项目中逻辑较为简单时,例:我抽了个useLocale
hook,供参考。 -
在划分视图时,要保持与入口的清晰一致, 如
App.vue
它会为你带来意想不到的好处。 -
在组件开发时不要刻意进行优化。我的建议是,待一个大的
page/view
开发结束后再考虑优化的事情
过早的优化属于浪费精力。但如果优化过晚,特别是在多人合作时,一些未提取的模板可能会被他人复制,然后进行修改,一旦变得不可控,进行优化就会需要更多的精力。
- 布局、文字等细节不应等到 UI 设计确定后再考虑,而应从决定支持响应式、i18n 时开始考虑。
我前公司的项目中,基本上都需要支持国际化。对于布局,务必要考虑文本所带来的影响,例如文字不固定长度,中文切换到英文长度变化带来的影响等,这些细节可以尽早地写入到 common.css 中作为公共样式。
- 过渡和动画可以起到画龙点睛的作用,但如果使用,尽量多尝试不同的 cubic-bezier。
在可控的情况下,要减少 transition-all 的使用,而以 transition-opacity、transition-transform 为例。否则,这与 TypeScript 中使用 any 有何区别呢?
我们的项目很简单,在实际开发中远不止这些,推荐参考 dev.to 、MDN 等
终于上线啦!别高兴太早
网站既然都放到了生产环境,F12 是常有的事 … 那有必要 print 秀一波 console.log 了,啊,不是!
小红红:停停停!兄弟,你是来捣乱的嘛?
性能才是我们要关注的重点好吧。先来一波分析 performance. 这是我自己网站的加载情况
实际可自行 performance API 或运用 lighthouse 等工具。这里就简单示范下
有点搞笑?!就这么少的文件,只有一个单页,怎么会加载得如此缓慢呢?代码写出来了,也在本地运行了,但放到生产环境后才发现问题如此突出。
没错,你也许以为这个项目已经完成了,但实际上,这只是一个开始。还有很长的路要走,兄弟们,我们的任务尚且艰巨。
构建优化
也是为了面试,正好实践一下 = = 以前也没配过这些,vue-cli 直接搞定,反正需要啥用啥,直接看文档即可 cn.vitejs.dev/.
大方向嘛无外乎两点:减少构建和打包耗时,减小打包资源体积
这点 vite 默认配置就有了,不用担心。这里我加了一个插件 compression
,它的作用是打包压缩且生成 gzip,感兴趣的小伙伴自行文档
Vite 代码拆包插件。支持多种拆包策略,可避免手动操作 manualChunks
潜在的循环依赖问题。
export default defineConfig({
plugins: [
vue(),
compression(),
chunkSplitPlugin({
strategy: 'default',
customSplitting: {
// vue相关会被打包到一个名为`render-vendor`的 chunk 里面(包括它们的一些依赖,如 object-assign)
'vue-vendor': [/vue/, /pinia/],
'tailwind-vendor': [/tailwindcss/],
'fri-vendor': [/fri-element-plus/],
// 源码中目录的代码都会打包进 [name] 这个 chunk 中
styles: [/src\/styles/],
locales: [/src\/locales/],
},
}),
],
})
原来是之前的练手项目导致了这些问题。动态导入却没有实现只导入所需的组件和样式,导致页面大小异常庞大。(若有时间,再优化这项目吧,这里直接去掉了) 实现代码拆分(split-chunks),来减少页面加载体积。
对于使用开源 UI 库的情况,大多数都会支持这样的特性,比如 ElementPlusResolver
等,以此来解决该问题
这里假装已经优化了:囧 (如果 UI 库支持,可实现按需加载)
export default defineConfig({
plugins: [
// ...
vitePluginImportus([
{
libraryName: 'ElementPlus',
libraryDirectory: 'es',
style: 'css',
},
]),
],
})
其实你还可以考虑,在你的项目中并不一定需要那么大而全的 UI 或工具库。既然已经开始着手进行优化,那就一丝不苟地将这些练手用的 UI 库移除吧。
同时,在此时可以思考一下,这些库、UI 组件等是否真的是你项目中所必需
的,是否存在其他更优的替代方案,是否支持按需加载等功能。
我又开始进行一番调整,将这些添加的”玩具”组件库一一移除了。
- 获取所有包含 assetDir 的包
- 过滤目标文件(rm .map 文件或忽略旧版本)
- 获取最终输出的 html 文件内容
- 在头部附加
<link rel="prefetch" href="xxxx">
通过 prefetch 也能尽早渲染页面,从而达到优化的效果
- 路由按需加载 (这里没用到就略过了)
构建上还有很多,主要是根据自身实际需求来就好。适合的就是最好的
优化后快多了不是,但还没完,别急,马上就好了,虽然我们打包了 gz 后缀,但其实还没生效呢。
请求优化
由于近期正在找工作,我发现简历内容可能会需要频繁更新。为了确保浏览器不会缓存旧的信息,我采用以下缓存策略:(仅供参考,可根据你的情况调整)
- index.html 不使用任何缓存策略,因为文件体积本身很小
- js、css、pdf、md 等可能频繁变动的文件,缓存 1 天
- svg、jpg、pdf 和字体文件等不会经常更改的文件,缓存 7 天
- 同时,我也开启了 gzip 压缩功能。
下面是我的 nginx 配置,供大家参考:
server {
server_name resume.fridolph.top;
# ssl和证书相关配置省略了
location / {
index index.html;
add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
}
location ~* ^.+\.(jpg|png|svg|ico|woff|woff2)$ {
log_not_found off; # 关闭日志
expires 7d; # 缓存时间7天
add_header Cache-Control max-age=604800;
}
location ~* ^.+\.(css|js|md|pdf|)$ {
expires 1d; # 可能会频繁变动的资源只缓存1天
add_header Cache-Control max-age=86400;
}
# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间。一般设置1和2
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml image/jpeg image/png;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
}
优化暂时搞定,秒开!
总体来看,作为一个“简单”的项目,我相信它应该达标了吧!呜呜呜,简历兄你完成了自己的使命,一路走好。
当然,优化远不止这么简单,这里只例举了一些常见方式,可根据需要将这些优化技巧运用到你的项目中。
再强调一下 Tss 四大核心概念:
- 效用第一
- 处理状态
- 响应式设计
- 重用样式
这才是我们在项目中使用的目的,tss 只是其中一个手段。感谢您看到这里。文章内容基本结束,本文只是引子,Tss 本身不仅仅这些内容。若通过本文能引起你的兴趣并学习这也是我的荣幸。
写在最后
实践才是最好的老师
,通过这段时间的实践,通过不断地思考和优化项目结构、组件,以及功能的实现。尽管这只是一个简单的项目,但我已经不再是简单的复制粘贴,而是把它当成一件新鲜的事物,用来检验自己的学习成果。我发现 Tss 非常适合模块化开发
,随着熟练度提高用起来也越来越顺手。
如果有同学不需要国际化的版本,可以切换到 github.com/Fridolph/my… 分支的dev/无国际化看这分支
。但这个分支比较早,缺少很多细节和后续的优化,再贴一下项目地址:
- github: github.com/Fridolph/my…
- 在线浏览:resume.fridolph.top
如果您觉得这对您有所帮助,求个 Star 不过分吧! 如果有不足之处或者观点不一致的地方,请多批评指教,谢谢。
本来想拆成几篇来写,感觉没必要。一步到位,您的点赞对我来说将是最大的认可。支持一下”萌新”吧!在春节期间写文章确实不容易,感谢大家的关注和支持。
恭祝大家工作顺利,平安健康,万事如意,一帆风顺!和我一样没工作的小伙伴在新的一年能找到自己满意的工作,生活不易,共勉之!
原文链接:https://juejin.cn/post/7334929489195909170 作者:霪霖笙箫