前言
还在手动重复写 margin: 0; padding: 0;
?还在为兼容性疯狂加 -webkit-
前缀?大厂前端早已不用原始 CSS 硬刚了!Stylus 作为一款现代化 CSS 预处理器,让你写样式像写 JavaScript 一样爽快。
Stylus:高效的CSS预处理器
基本特性
Stylus是一种CSS预处理器,提供了许多CSS不具备的高级功能:
stylus
// 定义变量
$background_color = rgba(255, 255, 255, 0.95)
.wrapper
background $background_color
box-shadow 0 0 0 10px rgba(0, 0, 0, 0.1)
优势与使用场景
- 变量支持:避免重复值,便于主题切换
- 嵌套规则:更清晰的DOM结构表示
- 混合(Mixins) :复用样式块
- 函数与运算:动态计算样式值
- 简洁语法:可选的花括号、分号和冒号
编译与使用
安装Stylus后,可以通过命令行编译.styl文件:
bash
npm install -g stylus
stylus -w common.styl -o common.css
- 第一个语句是用来安装
stylus
的直接运行就好 - 第二个语句是你编译
common.styl
文件时使用的,也就是你写CSS
代码时使用的,因为浏览器并不能直接编译.styl
文件,所以你要先将.styl
文件编译成.css
文件,也就是用上面给的那个命令,注意要自己切换成自己的.styl
文件名,后面的css
名可以随便取一个自己想要的
插件的使用
我们要想使用stylus
,除了要全局安装之外还要下载一下下面的这个插件。
我们要先进入插件市场,然后搜索stylus
,点击我选择的那个插件点击安装即可

案例实战
先看效果,再上代码,最后在分析考点易错点
效果
下面是我们实现的一个简单的效果界面图

代码
styl
$background_color = rgba(255, 255, 255, 0.95)
html
box-sizing border-box
min-height 100vh
display flex
flex-direction column
justify-content center
align-items center
text-align center
background url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat
background-size cover
*
box-sizing border-box
.wrapper
padding 20px
min-width 350px
background $background_color
box-shadow 0 0 0 10px rgba(0, 0, 0, 0.1)
h2
text-align center
margin 0
font-weight 200
body
color pink
.plates
margin 0
padding 0
text-align left
list-style: none
li
border-bottom 1px solid rgba(0, 0, 0, 0.2)
padding 10px 0px
display flex
label
flex 1
cursor pointer
input
display none
.add-items
margin-top 20px
input
padding 10px
outline 0
border 1px solid rgba(0, 0, 0, 0.1)
我们可以看到.styl
文件不用去写:
和{}
了,而且可以直接层叠样式
当我们运行stylus -w common.styl -o common.css
命令时,它会实时的将common.styl
文件编译成common.css
,你可以根据自己的需求来编写,让我们看看它帮我写好的common.css
文件吧
css
html {
box-sizing: border-box;
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
background: url("http://wes.io/hx9M/oh-la-la.jpg") center no-repeat;
background-size: cover;
}
* {
box-sizing: border-box;
}
.wrapper {
padding: 20px;
min-width: 350px;
background: rgba(255,255,255,0.95);
box-shadow: 0 0 0 10px rgba(0,0,0,0.1);
}
.wrapper h2 {
text-align: center;
margin: 0;
font-weight: 200;
}
body {
color: #ffc0cb;
}
.plates {
margin: 0;
padding: 0;
text-align: left;
list-style: none;
}
.plates li {
border-bottom: 1px solid rgba(0,0,0,0.2);
padding: 10px 0px;
display: flex;
}
.plates label {
flex: 1;
cursor: pointer;
}
.plates input {
display: none;
}
.add-items {
margin-top: 20px;
}
.add-items input {
padding: 10px;
outline: 0;
border: 1px solid rgba(0,0,0,0.1);
}
javascript
// 获取DOM元素
// 获取添加项目的表单元素
const addItems = document.querySelector('.add-items');
// 获取显示项目列表的元素
const itemsList = document.querySelector('.plates');
// 从本地存储获取项目数据,如果没有则初始化为空数组
let items = JSON.parse(localStorage.getItem('tapasItems')) || [];
// 添加新项目函数
function addItem(e) {
// 阻止表单默认提交行为
e.preventDefault();
// 获取输入框中的文本值
const text = this.querySelector('[name=item]').value;
// 创建新项目对象
const item = {
text, // 项目文本
done: false // 完成状态初始为false
};
// 将新项目添加到数组中
items.push(item);
// 更新列表显示
populateList(items, itemsList);
// 将更新后的数组保存到本地存储
localStorage.setItem('tapasItems', JSON.stringify(items));
// 重置表单
this.reset();
}
// 渲染项目列表函数
function populateList(plates = [], platesList) {
// 使用map方法将数组转换为HTML字符串
platesList.innerHTML = plates.map((plate, i) => {
return `
<li>
<!-- 复选框,绑定data-index属性用于标识项目索引 -->
<input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''}>
<!-- 项目文本标签 -->
<label for="item${i}">${plate.text}</label>
</li>
`;
}).join(''); // 将数组转换为字符串
}
// 切换项目完成状态函数
function toggleDone(e) {
// 如果点击的不是input元素则直接返回
if (!e.target.matches('input')) return;
// 获取被点击元素的data-index属性值
const el = e.target;
const index = el.dataset.index;
// 切换项目的完成状态
items[index].done = !items[index].done;
// 更新本地存储
localStorage.setItem('tapasItems', JSON.stringify(items));
// 重新渲染列表
populateList(items, itemsList);
}
// 添加事件监听器
// 表单提交事件 - 添加新项目
addItems.addEventListener('submit', addItem);
// 列表点击事件 - 切换项目完成状态
itemsList.addEventListener('click', toggleDone);
// 初始化加载 - 页面加载时渲染已有项目
populateList(items, itemsList);
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">
<title>Document</title>
<link rel="stylesheet" href="./common.css">
</head>
<body>
<div class="wrapper">
<h2>Local TAPAS</h2>
<p>请添加您的TAPAS</p>
<ul class="plates">
<li>Loading Tapas ...</li>
</ul>
<form action="" class="add-items">
<input
type="text"
placeholder="Item Name"
required <!-- 让输入框变成必须填写 -->
name="item"
>
<input type="submit" value="+ Add Item">
</form>
</div>
<script src="./common.js">
</script>
</body>
</html>
分析考点易错点
1. Stylus 变量 $background_color
考点:
- Stylus 中变量的定义和使用
- RGBA 颜色值的表示方法
答案:
$background_color = rgba(255, 255, 255, 0.95)
定义了一个半透明白色背景变量- 在 Stylus 中,变量名可以包含
$
符号,但不是必须的 - 可以直接在样式中引用变量,如
background $background_color
易错点:
- 忘记变量名前加
$
(虽然 Stylus 允许不加,但加了更清晰) - RGBA 值写错格式,如漏掉 alpha 通道或使用错误范围值
- 变量作用域问题(Stylus 变量有作用域概念)
2. 背景图片设置
考点:
- CSS 背景属性的简写方式
background-size: cover
的作用- 多背景属性的正确顺序
答案:
styl
background url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat
background-size cover
等价于 CSS:
css
background-image: url('http://wes.io/hx9M/oh-la-la.jpg');
background-position: center;
background-repeat: no-repeat;
background-size: cover;
易错点:
-
混淆
cover
和contain
的区别:cover
:完全覆盖容器,可能裁剪图片contain
:完整显示图片,可能留白
-
背景图片 URL 未加引号导致错误
-
多个背景属性顺序错误(简写时有特定顺序要求)
-
忘记设置
no-repeat
导致图片平铺
3. localStorage 使用
考点:
- localStorage 的 API 使用
- JSON 序列化与反序列化
- 数据持久化策略
答案:
javascript
// 存储数据
localStorage.setItem('tapasItems', JSON.stringify(items));
// 读取数据
let items = JSON.parse(localStorage.getItem('tapasItems')) || [];
易错点:
- 忘记使用
JSON.stringify
直接存储对象,导致存储为[object Object]
- 读取时忘记使用
JSON.parse
,导致得到的是字符串而非对象 - 未处理
getItem
返回 null 的情况(代码中使用|| []
做了默认值处理) - 存储大量数据超出 localStorage 容量限制(通常 5MB)
- 不考虑隐私模式下 localStorage 可能不可用的情况
4. Viewport Meta 标签
考点:
- 响应式设计基础
- 移动端视口控制
- 各属性的含义
答案:
html
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">
各属性含义:
width=device-width
:视口宽度等于设备宽度initial-scale=1
:初始缩放比例为1user-scalable=no
:禁止用户缩放viewport-fit=cover
:覆盖整个屏幕(针对刘海屏设备)
易错点:
- 拼写错误如
user-scalable
写成user-scalabe
- 错误理解
initial-scale
的作用 - 在需要用户缩放功能的场景错误地设置
user-scalable=no
- 忽略
viewport-fit=cover
导致刘海屏设备显示问题 - 多个属性间缺少逗号分隔(viewport 内容是用逗号分隔的)