🎨 CSS 写到手抽筋?Stylus 说:‘让我来!’

前言

还在手动重复写 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)

优势与使用场景

  1. 变量支持:避免重复值,便于主题切换
  2. 嵌套规则:更清晰的DOM结构表示
  3. 混合(Mixins) :复用样式块
  4. 函数与运算:动态计算样式值
  5. 简洁语法:可选的花括号、分号和冒号

编译与使用

安装Stylus后,可以通过命令行编译.styl文件:

bash 复制代码
npm install -g stylus
stylus -w common.styl -o common.css
  1. 第一个语句是用来安装stylus的直接运行就好
  2. 第二个语句是你编译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:初始缩放比例为1
  • user-scalable=no:禁止用户缩放
  • viewport-fit=cover:覆盖整个屏幕(针对刘海屏设备)

易错点:

  • 拼写错误如 user-scalable 写成 user-scalabe
  • 错误理解 initial-scale 的作用
  • 在需要用户缩放功能的场景错误地设置 user-scalable=no
  • 忽略 viewport-fit=cover 导致刘海屏设备显示问题
  • 多个属性间缺少逗号分隔(viewport 内容是用逗号分隔的)
相关推荐
架构技术专栏8 小时前
Agent 工具调用:从工具声明到执行反馈
前端·人工智能·后端
别动我齐刘海8 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
志尊宝9 小时前
Vue3 零基础每日笔记(071):暗黑模式与主题切换——CSS 变量 + Element Plus 暗黑方案
前端·css·笔记·vue·html·前端开发·软件开发
刘发财10 小时前
前端如何生成10000页PDF文件?
前端·javascript·开源
A接拉起00710 小时前
2026 年 JavaScript 开发者需要了解的知识
前端·javascript·后端
用户114818678948410 小时前
面试官:大屏项目里十几个模块同时渲染上万条数据,怎么做到不卡?
前端·性能优化
前端市界10 小时前
Pro React Admin: 统一工程底座,承载多种应用...
前端·github
葡萄城技术团队11 小时前
dsh-spreadjs-editor 当前支持什么:安装前需要了解的几件事
前端
沫璃染墨11 小时前
《Qt从零入门系列(十三):QSS样式表详解——从选择器与层叠规则到控件美化实战》
开发语言·css·c++·qt·ui·qt6.3·qss
三*一11 小时前
优化修整算法
前端·javascript·算法