CSS预处理器、移动端适配
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
1、预处理器概念
1.1、CSS编写的痛点
CSS作为一种样式语言, 本身用来给HTML元素添加样式是没有问题的。
但是目前前端项目已经越来越复杂, 不再是简简单单的几行CSS就可以搞定的, 我们需要几千行甚至上万行的CSS来完成页面的美化工作。
随着代码量的增加, 必然会造成很多的编写不便
- 比如大量的重复代码, 虽然可以用类来勉强管理和抽取, 但是使用起来依然不方便
- 比如无法定义变量当然目前已经支持, 如果一个值被修改, 那么需要修改大量代码, 可维护性很差; (比如主题颜色)
- 比如没有专门的作用域和嵌套, 需要定义大量的id/class来保证选择器的准确性, 避免样式混淆;
- 等等一系列的问题
所以有一种对CSS称呼是 “面向命名编程”;
社区为了解决CSS面临的大量问题, 出现了一系列的CSS预处理器(CSS_preprocessor)
- CSS 预处理器是一个能让你通过预处理器自己独有的语法来生成CSS的程序;
- 市面上有很多CSS预处理器可供选择且绝大多数CSS预处理器会增加一些原生CSS不具备的特性;
- 代码最终会转化为CSS来运行, 因为对于浏览器来说只识别CSS;
1.2、常见的CSS预处理器
常见的预处理器有哪些呢? 目前使用较多的是三种预处理器:
Sass/Scss
- 2007年诞生最早也是最成熟的CSS预处理器拥有ruby社区的支持是属于Haml一种模板系统的一部分;
- 目前受LESS影响已经进化到了全面兼容CSS的SCSS;
Less:
- 2009年出现受SASS的影响较大但又使用CSS的语法让大部分开发者更容易上手;
- 比起SASS来可编程功能不够不过优点是使用方式简单、便捷兼容CSS并且已经足够使用
- 另外反过来也影响了SASS演变到了SCSS的时代
- 著名的Twitter Bootstrap就是采用LESS做底层语言的也包括React的UI框架AntDesign。
Stylus:
- 2010年产生来自Node.js社区主要用来给Node项目进行CSS预处理支持;
- 语法偏向于Python, 使用率相对于Sass/Less少很多
2、Less
2.1、认识Less
Less Leaner Style Sheets 的缩写 是一门CSS 扩展语言, 并且兼容CSS。
- Less增加了很多相比于CSS更好用的特性;
- 比如定义变量、混入、嵌套、计算等等
- Less最终需要被编译成CSS运行于浏览器中包括部署到服务器中
2.2、less代码的编译
less代码如何被编译成CSS代码运行呢
方式一下载Node环境通过npm包管理下载less工具使用less工具对代码进行编译
方法二通过VSCode插件来编译成CSS或者在线编译Less Preview (online playground)
方式三引入CDN的less编译代码对less进行实时的处理
<script src="https://cdn.jsdelivr.net/npm/less@4" ></script>
方式四将less编译的js代码下载到本地执行js代码对less进行编译
2.3、Less兼容CSS
- Less是兼容CSS的所以我们可以在Less文件中编写所有的CSS代码
- 只是将css的扩展名改成了.less结尾而已
2.4、变量Variables
在一个大型的网页项目中我们CSS使用到的某几种属性值往往是特定的
- 比如我们使用到的主题颜色值那么每次编写类似于#f3c258格式的语法
- 一方面是记忆不太方便需要重新编写或者拷贝样式
- 另一方面如果有一天主题颜色改变我们需要修改大量的代码
- 所以我们可以将常见的颜色或者字体等定义为变量来使用
在Less中使用如下的格式来定义变量
@变量名: 变量值;
2.5、嵌套Nesting
在之前的项目中当我们需要找到一个内层的元素时往往需要嵌套很多层的选择器
Less提供了选择器的嵌套
特殊符号& 表示当前选择器的父级
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Document</title>
<link href="./less/02_less的语法-兼容CSS-变量-嵌套.less" rel="stylesheet/less">
<script src="https://cdn.jsdelivr.net/npm/less@4" ></script>
</head>
<body>
<!-- 1.兼容css -->
<!-- <div class="box">
我是box
</div> -->
<!-- 2.定义变量 -->
<div class="box">
<p class="pel">我是p元素</p>
<h1>我是h1元素 <span class="keyword">关键字</span></h1>
<p>
我是一个大的段落
<a class="link" href="#">百度一下</a>
</p>
</div>
<!-- &符号的练习 -->
<ul class="list">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
<li class="item">4</li>
<li class="item">5</li>
</ul>
</body>
</html>
less代码
// 1.兼容CSS代码
// .box {
// width: 100px;
// height: 100px;
// background-color: orange;
// font-size: 20px;
// color: #fff;
// }
// 2.定义变量
@mainColor: #a40011;
@smallFontSize: 12px;
@normalFontSize: 14px;
@bigFontSize: 18px;
// .box .pel {
// color: @mainColor;
// font-size: @normalFontSize;
// }
// .box h1 .keyword .section .list .item a .desc {
// color: @mainColor;
// font-size: @bigFontSize;
// }
// .box p .link {
// color: @mainColor;
// font-size: @smallFontSize;
// }
// 3.选择器的嵌套
.box {
.pel {
color: @mainColor;
font-size: @normalFontSize;
}
h1 {
.keyword {
color: @mainColor;
font-size: @bigFontSize;
}
}
p {
a.link {
color: @mainColor;
font-size: @smallFontSize;
background-color: #0f0;
&:hover {
color: #00f;
}
}
}
}
// &符号的练习
.list {
.item {
font-size: 20px;
&:hover {
color: @mainColor;
}
&:nth-child(1) {
color: orange;
}
&:nth-child(2) {
color: #00f;
}
}
}
2.5、运算Operations
在Less中算术运算符 +、-、*、/ 可以对任何数字、颜色或变量进行运算。
- 算术运算符在加、减或比较之前会进行单位换算计算的结果以最左侧操作数的单位类型为准
- 如果单位换算无效或失去意义则忽略单位
2.6、混合Mixins
在原来的CSS编写过程中多个选择器中可能会有大量相同的代码
- 我们希望可以将这些代码进行抽取到一个独立的地方任何选择器都可以进行复用
- 在less中提供了混入Mixins来帮助我们完成这样的操作
混合Mixin是一种将一组属性从一个规则集或混入到另一个规则集的方法。
注意混入在没有参数的情况下小括号可以省略但是不建议这样使用
混入也可以传入变量
映射Maps
混入和映射结合混入也可以当做一个自定义函数来使用
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Document</title>
<link href="./less/03_less语法-运算-混入-映射.less" rel="stylesheet/less">
<script src="https://cdn.jsdelivr.net/npm/less@4" ></script>
</head>
<body>
<!-- 1.less中的运算 -->
<!-- <div class="box">
我是box
</div> -->
<!-- 2.混入mixins -->
<div class="box1">
向阳草木青明媚春光暖。伴随着天气晴好、气温回升沉闷了一个冬日的人们纷纷走出家门与大自然亲密接触恣意享受春日好时光。
</div>
<div class="box2">
“露营热”不断升温的背后是人们生活方式日趋多元、生活观念不断蝶变的折射。
</div>
</body>
</html>
less代码
// 1.运算
// .box {
// font-size: 20px;
// width: 10% + 50px;
// height: 100px;
// background-color: #ff0000 + #00ff00;
// }
// px to rem
// 2.混入
// 2.1. 混入的基本使用
.nowrap_ellipsis {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
// 2.2.混入是可以传递参数(定义变量)的
.box_border(@borderWidth: 5px, @borderColor: purple) {
border: @borderWidth solid @borderColor;
}
// 2.3.混入和映射(Map)结合使用
// 作用: 弥补less中不能自定义函数的缺陷
.box_size {
width: 100px;
height: 100px;
}
.box1 {
width: .box_size()[width];
background-color: #f00;
.nowrap_ellipsis();
.box_border();
}
.box2 {
width: 150px;
background-color: #0f0;
.nowrap_ellipsis();
.box_border(10px, orange);
}
2.7、less其他语法补充
extend继承
- 和mixins作用类似用于复用代码
- 和mixins相比继承代码最终会转化成并集选择器
Less内置函数
- Less 内置了多种函数用于转换颜色、处理字符串、算术运算等。
- 内置函数手册 Less 函数 | Less.js 中文文档 - Less 中文网
作用域Scope
- 在查找一个变量时首先在本地查找变量和混合mixins
- 如果找不到则从“父”级作用域继承
注释Comments在Less中块注释和行注释都可以使用
导入Importing
- 导入的方式和CSS的用法是一致的
- 导入一个 .less 文件此文件中的所有变量就可以全部使用了
- 如果导入的文件是 .less 扩展名则可以将扩展名省略掉
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Document</title>
<link href="./less/04_less语法-继承-函数-注释-导入.less" rel="stylesheet/less">
<script src="./js/lessc.js"></script>
</head>
<body>
<div class="box">
我是box文本
<div class="item">
我是item文本
<span>呵呵呵呵</span>
我是结束文本
</div>
</div>
</body>
</html>
less代码
// 1.extend
// .box_border {
// border: 5px solid #f00;
// }
// .box {
// width: 100px;
// background-color: orange;
// // .box_border();
// &:extend(.box_border);
// }
// 2.内置函数
// .box {
// color: color(skyblue);
// width: convert(100px, "in");
// font-size: ceil(18.5px);
// background-color: orange;
// }
// 3.作用域(scope)
@mainColor: #f00;
.box_mixin {
@mainColor: orange;
}
.box {
// @mainColor: #0f0;
.item {
span {
color: @mainColor;
.box_mixin();
// @mainColor: #00f;
}
}
}
// 4.注释(comment)
// 单行注释
/* 多行注释 */
3、认识Sass和Scss
- 事实上最初Sass 是Haml的一部分Haml 是一种模板系统由 Ruby 开发者设计和开发。
- 所以Sass的语法使用的是类似于Ruby的语法没有花括号没有分号具有严格的缩进
我们会发现它的语法和CSS区别很大后来官方推出了全新的语法SCSS意思是Sassy CSS他是完全兼容CSS的。
目前在前端学习SCSS直接学习SCSS即可
- SCSS的语法也包括变量、嵌套、混入、函数、操作符、作用域等
- 通常也包括更为强大的控制语句、更灵活的函数、插值语法等
- 大家可以根据之前学习的less语法来学习一些SCS语法
- Sass: Sass Basics (sass-lang.com)
4、移动端适配
4.1、什么是移动端适配
移动互联网的快速发展让人们已经越来越习惯于使用手机来完成大部分日常的事务。
- 前端我们已经学习了大量HTML、CSS的前端开发知识并且也进行了项目实战
- 这些知识也同样适用于移动端开发但是如果想让一个页面真正适配于移动端我们最好多了解一些移动端的知识
移动端开发目前主要包括三类
- 原生App开发iOS、Android、RN、uniapp、Flutter等
- 小程序开发原生小程序、uniapp、Taro等
- Web页面移动端的Web页面可以使用浏览器或者webview浏览
因为目前移动端设备较多所以我们需要对其进行一些适配。
这里有两个概念
- 自适应根据不同的设备屏幕大小来自动调整尺寸、大小
- 响应式会随着屏幕的实时变动而自动调整是一种自适应
4.2、认识视口viewport
在前面我们已经简单了解过视口的概念了
- 在一个浏览器中我们可以看到的区域就是视口viewport
- 我们说过fixed就是相对于视口来进行定位的
- 在PC端的页面中我们是不需要对视口进行区分因为我们的布局视口和视觉视口是同一个
但是在移动端不太一样你布局的视口和你可见的视口是不太一样的。
- 这是因为移动端的网页窗口往往比较小我们可能会希望一个大的网页在移动端可以完整的显示
- 所以在默认情况下移动端的布局视口是大于视觉视口的
所以在移动端我们可以将视口划分为三种情况
- 布局视口layout viewport
- 视觉视口visual layout
- 理想视口ideal layout
这些概念的区分事实上来自ppk他也是对前端贡献比较大的一个人特别是在移动端浏览器
A tale of two viewports — part two
4.3、布局视口和视觉视口
布局视口layout viewport
默认情况下一个在PC端的网页在移动端会如何显示呢
- 第一它会按照宽度为980px来布局一个页面的盒子和内容
- 第二为了显示可以完整的显示在页面中对整个页面进行缩小
我们相对于980px布局的这个视口称之为布局视口layout viewport
- 布局视口的默认宽度是980px
视觉视口visual viewport
- 如果默认情况下我们按照980px显示内容那么右侧有一部分区域就会无法显示所以手机端浏览器会默认对页面进行缩放以显示到用户的可见区域中
- 那么显示在可见区域的这个视口就是视觉视口visual viewport
在Chrome上按shift+鼠标左键可以进行缩放。
4.4、理想视口ideal viewport
如果所有的网页都按照980px在移动端布局那么最终页面都会被缩放显示。
- 事实上这种方式是不利于我们进行移动的开发的我们希望的是设置100px那么显示的就是100px
- 如何做到这一点呢通过设置理想视口ideal viewport
理想视口ideal viewport
- 默认情况下的layout viewport并不适合我们进行布局
- 我们可以对layout viewport进行宽度和缩放的设置以满足正常在一个移动端窗口的布局
- 这个时候可以设置meta中的viewport
值 | 可能的附加值 | 描述 |
width | 一个正整数或者字符串 device-width | 定义 viewport 的宽度。 |
height | 一个正整数或者字符串 device-height | 定义 viewport 的高度。未被任何浏览器使用。 |
initial-scale | 一个 0.0 和 10.0 之间的正数 | 定义设备宽度与 viewport 大小之间的缩放比例。 |
maximum-scale | 一个 0.0 和 10.0 之间的正数 | 定义缩放的最大值必须大于等于 minimum-scale否则表现将不可预测。 |
minimum-scale | 一个 0.0 和 10.0 之间的正数 | 定义缩放的最小值必须小于等于 maximum-scale否则表现将不可预测。 |
user-scalable | yes 或者 no | 默认为 yes如果设置为 no将无法缩放当前页面。浏览器可以忽略此规则 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<!-- width: 设置布局视口的宽度 -->
<meta content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
name="viewport">
<title>Document</title>
<style>
body {
margin: 0;
padding: 0;
}
.box {
width: 100px;
height: 100px;
background-color: orange;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>
5、移动端适配方案
移动端的屏幕尺寸通常是非常繁多的很多时候我们希望在不同的屏幕尺寸上显示不同的大小
- 比如我们设置一个100x100的盒子
- 在375px的屏幕上显示是100x100;
- 在320px的屏幕上显示是90+x90+;
- 在414px的屏幕上显示是100+x100+;
- 其他尺寸也是类似比如padding、margin、border、left甚至是font-size等等
这个时候我们可能可以想到一些方案来处理尺寸
- 方案一百分比设置
- 因为不同属性的百分比值相对的可能是不同参照物所以百分比往往很难统一
- 所以百分比在移动端适配中使用是非常少的
- 方案二rem单位+动态html的font-size
- 方案三vw单位
- 方案四flex的弹性布局
5.1、rem+动态html的font-size
rem单位是相对于html元素的font-size来设置的那么如果我们需要在不同的屏幕下有不同的尺寸可以动态的修改html的font-size尺寸。
比如如下案例
- 设置一个盒子的宽度是2rem
- 设置不同的屏幕上html的font-size不同
屏幕尺寸 | html的font-size | 盒子的设置宽度 | 盒子的最终宽度 |
375px | 37.5px | 1rem | 37.5px |
320px | 32px | 1rem | 32px |
414px | 41.4px | 1rem | 41.4px |
这样在开发中我们只需要考虑两个问题
- 问题一针对不同的屏幕设置html不同的font-size
- 问题二将原来要设置的尺寸转化成rem单位
5.2、rem的font-size尺寸
方案一媒体查询
- 可以通过媒体查询来设置不同尺寸范围内的屏幕html的font-size尺寸
- 缺点
- 我们需要针对不同的屏幕编写大量的媒体查询
- 如果动态改变尺寸不会实时的进行更新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Document</title>
<style>
@media screen and (min-width: 320px) {
html {
font-size: 20px;
}
}
@media screen and (min-width: 375px) {
html {
font-size: 24px;
}
}
@media screen and (min-width: 414px) {
html {
font-size: 28px;
}
}
@media screen and (min-width: 480px) {
html {
font-size: 32px;
}
}
.box {
width: 5rem;
height: 5rem;
background-color: orange;
}
</style>
</head>
<body>
<div class="box">
</div>
</body>
</html>
方案二编写js代码
- 如果希望实时改变屏幕尺寸时font-size也可以实时更改可以通过js代码
- 方法
- 根据html的宽度计算出font-size的大小并且设置到html上
- 监听页面的实时改变并且重新设置font-size的大小到html上
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Document</title>
<script src="./js/hy_flexible.js"></script>
<style>
body {
font-size: 16px;
}
.box {
width: 5rem;
height: 5rem;
background-color: orange;
}
p {
font-size: 0.5rem;
}
</style>
</head>
<body>
<div class="box">
</div>
<p>我是文本</p>
<!-- 动态的修改html的font-size -->
<span>哈哈哈哈哈哈</span>
</body>
</html>
js代码
// 1.获取html的元素
const htmlEl = document.documentElement
function setRemUnit() {
// 2.获取html的宽度(视口的宽度)
const htmlWidth = htmlEl.clientWidth
// 3.根据宽度计算一个html的font-size的大小
const htmlFontSize = htmlWidth / 10
// 4.将font-size设置到html上
htmlEl.style.fontSize = htmlFontSize + "px"
}
// 保证第一次进来时, 可以设置一次font-size
setRemUnit()
// 当屏幕尺寸发生变化时, 实时来修改html的font-size
window.addEventListener("resize", setRemUnit)
方案三lib-flexible库
- 事实上lib-flexible库做的事情是相同的你也可以直接引入它
html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Document</title>
<script src="./js/lib_flexible.js"></script>
<style>
.box {
width: 5rem;
height: 5rem;
background-color: orange;
}
p {
font-size: 0.5rem;
}
</style>
</head>
<body>
<div class="box">
</div>
<p>我是文本</p>
</body>
</html>
js代码
(function flexible(window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// adjust body font size
function setBodyFontSize() {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
} else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit() {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
5.3、rem的单位换算
方案一手动换算
- 比如有一个在375px屏幕上100px宽度和高度的盒子
- 我们需要将100px转成对应的rem值
- 100/37.5=2.6667其他也是相同的方法计算即可
方案二less/scss函数
方案三postcss-pxtorem目前在前端的工程化开发中我们可以借助于webpack的工具来完成自动的转化
方案四VSCode插件px to rem 的插件在编写时自动转化
5.4、适配方案 - vw
在flexible GitHub上已经有写过这样的一句话
所以它更推荐使用viewport的两个单位vw、wh。
vw的兼容性如何呢
5.5、vw和rem的对比
rem事实上是作为一种过渡的方案它利用的也是vw的思想。
- 前面不管是我们自己编写的js还是flexible的源码
- 都是将1rem等同于设计稿的1/10在利用1rem计算相对于整个屏幕的尺寸大小
- 那么我们来思考1vw不是刚好等于屏幕的1/100吗
- 而且相对于rem还更加有优势
vw相比于rem的优势
- 优势一不需要去计算html的font-size大小也不需要给html设置这样一个font-size
- 优势二不会因为设置html的font-size大小而必须给body再设置一个font-size防止继承
- 优势三因为不依赖font-size的尺寸所以不用担心某些原因html的font-size尺寸被篡改页面尺寸混乱
- 优势四vw相比于rem更加语义化1vw刚才是1/100的viewport的大小;
- 优势五可以具备rem之前所有的优点
vw我们只面临一个问题将尺寸换算成vw的单位即可
所以目前相比于rem更加推荐大家使用vw但是理解rem依然很重要
5.6、vw的单位换算
方案一手动换算
- 比如有一个在375px屏幕上100px宽度和高度的盒子
- 我们需要将100px转成对应的vw值
- 100/3.75=26.667其他也是相同的方法计算即可
方案二less/scss函数
方案三postcss-px-to-viewport-8-plugin和rem一样在前端的工程化开发中我们可以借助于webpack的工具来完成自动的转化
方案四VSCode插件px to vw 的插件在编写时自动转化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Document</title>
<style>
/*
rem:
1> 动态的设置html的font-size
font-size: 视口的宽度 / 10
2> 换算成rem的单位
*/
/* html {
font-size: 10vw;
} */
/*
1> 动态的设置html的font-size
font-size: 视口的宽度 / 100 -> 1vw
*/
.box {
width: 26.667vw;
height: 26.666vw;
background-color: orange;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
</html>