前端面试题 —— CSS (二)

目录

一、transition 和 animation 的区别

二、什么是物理像素,逻辑像素和像素密度,为什么在移动端开发时需要用到@3x, @2x 这种图片? 

三、margin 和 padding 的使用场景

四、CSS 优化和提高性能的方法有哪些?

五、display:inline-block 什么时候会显示间隙?

六、对 CSS 工程化的理解

七、如何判断元素是否到达可视区域

八、如何根据设计稿进行移动端适配?

九、响应式设计的概念及基本原理

十、使用 clear 属性清除浮动的原理?

十一、absolute 与 fixed 共同点与不同点

十二、实现一个宽高自适应的正方形

十三、为什么有时候⽤ translate 来改变位置⽽不是定位?

十四、li 与 li 之间有看不见的空白间隔是什么原因引起的?如何解决?

十五、替换元素的概念及计算规则---不需要看

十六、对line-height 的理解及其赋值方式

十七、::before 和 :after 的双冒号和单冒号有什么区别?

十八、元素的层叠顺序

十九、对 sticky 定位的理解

二十、对 requestAnimationframe 的理解


一、transition 和 animation 的区别

  • transition 是过度属性,强调过度,它的实现需要触发一个事件(比如鼠标移动上去,焦点,点击等)才执行动画。它类似于 flash 的补间动画,设置一个开始关键帧,一个结束关键帧。
  • animation 是动画属性,它的实现不需要触发事件,设定好时间之后可以自己执行,且可以循环一个动画。它也类似于 flash 的补间动画,但是它可以设置多个关键帧(用@keyframe 定义)完成动画。

二、什么是物理像素,逻辑像素和像素密度,为什么在移动端开发时需要用到@3x, @2x 这种图片? 

以 iPhone XS 为例,当写 CSS 代码时,针对于单位 px,其宽度为 414px & 896px,也就是说当赋予一个 DIV 元素宽度为 414px,这个 DIV 就会填满手机的宽度。

而如果有一把尺子来实际测量这部手机的物理像素,实际为 1242\*2688 物理像素;经过计算可知,1242/414=3,也就是说,在单边上,一个逻辑像素=3 个物理像素,就说这个屏幕的像素密度为 3,也就是常说的 3 倍屏。

对于图片来说,为了保证其不失真,1 个图片像素至少要对应一个物理像素,假如原始图片是 500300 像素,那么在 3 倍屏上就要放一个 1500900 像素的图片才能保证 1 个物理像素至少对应一个图片像素,才能不失真。

当然,也可以针对所有屏幕,都只提供最高清图片。虽然低密度屏幕用不到那么多图片像素,而且会因为下载多余的像素造成带宽浪费和下载延迟,但从结果上说能保证图片在所有屏幕上都不会失真。

还可以使用 CSS 媒体查询来判断不同的像素密度,从而选择不同的图片: 

my-image { 
  background: (low.png); 
}
@media only screen and (min-device-pixel-ratio: 1.5) {
  #my-image { background: (high.png); }
}

三、margin 和 padding 的使用场景

  • 需要在 border 外侧添加空白,且空白处不需要背景(色)时,使用 margin
  • 需要在 border 内测添加空白,且空白处需要背景(色)时,使用 padding

四、CSS 优化和提高性能的方法有哪些?

加载性能:

  1. css 压缩:将写好的 css 进行打包压缩,可以减小文件体积。
  2. css 单一样式:当需要下边距和左边距的时候,很多时候会选择使用 margin:top 0 bottom 0;但 margin-bottom:bottom;margin-left:left;执行效率会更高。
  3. 减少使用@import,建议使用 link,因为后者在页面加载时一起加载,前者是等待页面加载完成之后再进行加载。

选择器性能:

  1. 关键选择器(key selector)。选择器的最后面的部分为关键选择器(即用来匹配目标元素的部分)。CSS 选择符是从右到左进行匹配的。当使用后代选择器的时候,浏览器会遍历所有子元素来确定是否是指定的元素等等。
  2. 如果规则拥有 ID 选择器作为其关键选择器,则不要为规则增加标签。过滤掉无关的规则(这样样式系统就不会浪费时间去匹配它们了)。
  3. 避免使用通配规则,如\*{}计算次数惊人,只对需要用到的元素进行选择。
  4. 尽量少的去对标签进行选择,而是用 class。
  5. 尽量少的去使用后代选择器,降低选择器的权重值。后代选择器的开销是最高的,尽量将选择器的深度降到最低,最高不要超过三层,更多的使用类来关联每一个标签元素。
  6. 了解哪些属性是可以通过继承而来的,然后避免对这些属性重复指定规则。

渲染性能:

  1. 慎重使用高性能属性:浮动、定位。
  2. 尽量减少页面重排、重绘。
  3. 去除空规则:{}。空规则的产生原因一般来说是为了预留样式。去除这些空规则无疑能减少 css 文档体积。
  4. 属性值为 0 时,不加单位。
  5. 属性值为浮动小数 0.\*\*,可以省略小数点之前的 0。
  6. 标准化各种浏览器前缀:带浏览器前缀的在前。标准属性在后。
  7. 不使用@import 前缀,它会影响 css 的加载速度。
  8. 选择器优化嵌套,尽量避免层级过深。
  9. css 雪碧图,同一页面相近部分的小图标,方便使用,减少页面的请求次数,但是同时图片本身会变大,使用时,优劣考虑清楚,再使用。
  10. 正确使用 display 的属性,由于 display 的作用,某些样式组合会无效,徒增样式体积的同时也影响解析性能。
  11. 不滥用 web 字体。对于中文网站来说 WebFonts 可能很陌生,国外却很流行。web fonts 通常体积庞大,而且一些浏览器在下载 web fonts 时会阻塞页面渲染损伤性能。

可维护性、健壮性:

  1. 将具有相同属性的样式抽离出来,整合并通过 class 在页面中进行使用,提高 css 的可维护性。
  2. 样式与内容分离:将 css 代码定义到外部 css 中。

五、display:inline-block 什么时候会显示间隙?

  • 有空格时会有间隙,可以删除空格解决;
  • margin 正值时,可以让 margin 使用负值解决;
  • 使用 font-size 时,可通过设置 font-size: 0;、letter-spacing、word-spacing 解决;

六、对 CSS 工程化的理解

CSS 工程化是为了解决以下问题:

  1. 宏观设计:CSS 代码如何组织、如何拆分、模块结构怎样设计?
  2. 编码优化:怎样写出更好的 CSS?
  3. 构建:如何处理我的 CSS,才能让它的打包结果最优?
  4. 可维护性:代码写完了,如何最小化它后续的变更成本?如何确保任何一个同事都能轻松接手?

以下三个方向都是时下比较流行的、普适性非常好的 CSS 工程化实践:

  • 预处理器:Less、 Sass 等
  • 重要的工程化插件: PostCss
  • Webpack loader 等 

基于这三个方向,可以衍生出一些具有典型意义的子问题,这里我们逐个来看:

(1)预处理器:为什么要用预处理器?它的出现是为了解决什么问题?

预处理器,其实就是 CSS 世界的“轮子”。预处理器支持我们写一种类似 CSS、但实际并不是 CSS 的语言,然后把它编译成 CSS 代码:

那为什么写 CSS 代码写得好好的,偏偏要转去写 "类 CSS" 呢?这就和本来用 JS 也可以实现所有功能,但最后却写 React 的 jsx 或者 Vue 的模板语法一样——为了爽!要想知道有了预处理器有多爽,首先要知道的是传统 CSS 有多不爽。

随着前端业务复杂度的提高,前端工程中对 CSS 提出了以下的诉求:

  1.  宏观设计上:我们希望能优化 CSS 文件的目录结构,对现有的 CSS 文件实现复用
  2. 编码优化上:我们希望能写出结构清晰、简明易懂的 CSS,需要它具有一目了然的嵌套层级关系,而不是无差别的一铺到底写法;我们希望它具有变量特征、计算能力、循环能力等等更强的可编程性,这样我们可以少写一些无用的代码
  3. 可维护性上:更强的可编程性意味着更优质的代码结构,实现复用意味着更简单的目录结构和更强的拓展能力,这两点如果能做到,自然会带来更强的可维护性

这三点是传统 CSS 所做不到的,也正是预处理器所解决掉的问题。预处理器普遍会具备这样的特性:

  • 嵌套代码的能力,通过嵌套来反映不同 css 属性之间的层级关系 
  • 支持定义 css 变量
  • 提供计算函数
  • 允许对代码片段进行 extend 和 mixin
  • 支持循环语句的使用
  • 支持将 CSS 文件模块化,实现复用

(2)PostCss:PostCss 是如何工作的?我们在什么场景下会使用 PostCss?

PostCss 仍然是一个对 CSS 进行解析和处理的工具,它会对 CSS 做这样的事情:

它和预处理器的不同就在于,预处理器处理的是 类 CSS,而 PostCss 处理的就是 CSS 本身。Babel 可以将高版本的 JS 代码转换为低版本的 JS 代码。PostCss 做的是类似的事情:它可以编译尚未被浏览器广泛支持的先进的 CSS 语法,还可以自动为一些需要额外兼容的语法增加前缀。更强的是,由于 PostCss 有着强大的插件机制,支持各种各样的扩展,极大地强化了 CSS 的能力。

PostCss 在业务中的使用场景非常多:

  • 提高 CSS 代码的可读性:PostCss 其实可以做类似预处理器能做的工作;
  • 当我们的 CSS 代码需要适配低版本浏览器时,PostCss 的 [Autoprefixer](https://github.com/postcss/autoprefixer) 插件可以帮助我们自动增加浏览器前缀;
  • 允许我们编写面向未来的 CSS:PostCss 能够帮助我们编译 CSS next 代码;

(3)Webpack 能处理 CSS 吗?如何实现?

Webpack 能处理 CSS 吗?

  • Webpack 在裸奔的状态下,是不能处理 CSS 的,Webpack 本身是一个面向 JavaScript 且只能处理 JavaScript 代码的模块化打包工具
  • Webpack 在 loader 的辅助下,是可以处理 CSS 的

如何用 Webpack 实现对 CSS 的处理:

  • Webpack 中操作 CSS 需要使用的两个关键的 loader:css-loader 和 style-loader

每个 loader 都做了什么事情:

  • css-loader:导入 CSS 模块,对 CSS 代码进行编译处理
  • style-loader:创建 style 标签,把 CSS 内容写入标签

在实际使用中,css-loader 的执行顺序一定要安排在 style-loader 的前面。因为只有完成了编译过程,才可以对 css 代码进行插入;若提前插入了未编译的代码,那么 webpack 是无法理解该代码,它会无情报错。

七、如何判断元素是否到达可视区域

以图片显示为例:

  • window.innerHeight 是浏览器可视区的高度;
  • document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的距离;
  • imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离);
  • 内容达到显示区域的:img.offsetTop < window.innerHeight + document.body.scrollTop;

八、如何根据设计稿进行移动端适配?

移动端适配主要有两个维度:

  • 适配不同像素密度,针对不同的像素密度,使用 CSS 媒体查询,选择不同精度的图片,以保证图片不会失真;
  • 适配不同屏幕大小,由于不同的屏幕有着不同的逻辑像素大小,所以如果直接使用 px 作为开发单位,会使得开发的页面在某一款手机上可以准确显示,但是在另一款手机上就会失真。为了适配不同屏幕的大小,应按照比例来还原设计稿的内容。

为了能让页面的尺寸自适应,可以使用 rem,em,vw,vh 等相对单位。

九、响应式设计的概念及基本原理

响应式网站设计(Responsive Web design)是一个网站能够兼容多个终端,而不是为每一个终端做一个特定的版本。

关于原理: 基本原理是通过媒体查询(@media)查询检测不同的设备屏幕尺寸做处理。

关于兼容: 页面头部必须有 meta 声明的 viewport。

<meta name="viewport" content=width="device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

十、使用 clear 属性清除浮动的原理?

使用 clear 属性清除浮动,其语法如下:

clear: none|left|right|both;

如果单看字面意思,clear:left 是清除左浮动,clear:right 是清除右浮动,实际上,这种解释是有问题的,因为浮动一直还在,并没有清除。

官方对 clear 属性解释:元素盒子的边不能和前面的浮动元素相邻,对元素设置 clear 属性是为了避免浮动元素对该元素的影响,而不是清除掉浮动。

还需要注意 clear 属性指的是元素盒子的边不能和前面的浮动元素相邻,注意这里前面的3 个字,也就是 clear 属性对后面的浮动元素是不闻不问的。考虑到 float 属性要么是 left,要么是 right,不可能同时存在,同时由于 clear 属性对后面的浮动元素不闻不问,因此,当 clear:left 有效的时候,clear:right 必定无效,也就是此时 clear:left 等同于设置 clear:both;同样地,clear:right 如果有效也是等同于设置 clear:both。由此可见,clear:left 和 clear:right 这两个声明就没有任何使用的价值,至少在 CSS 世界中是如此,直接使用 clear:both 吧。

一般使用伪元素的方式清除浮动:

.clear::after {
  content:'';
  display: block;
  clear:both;
}

clear 属性只有块级元素才有效的,而::after 等伪元素默认都是内联水平,这就是借助伪元素清除浮动影响时需要设置 display 属性值的原因。

十一、absolute 与 fixed 共同点与不同点

共同点:

  • 改变行内元素的呈现方式,将 display 置为 inline-block
  • 使元素脱离普通文档流,不再占据文档物理空间
  • 覆盖非定位文档元素

不同点:

  • absolute 与 fixed 的根元素不同,absolute 的根元素可以设置,fixed 根元素是浏览器。
  • 在有滚动条的页面中,absolute 会跟着父元素进行移动,fixed 固定在页面的具体位置。

十二、实现一个宽高自适应的正方形

  • 利用 vw 来实现:
.square {
  width: 10%;
  height: 10vw;
  background: tomato;
}
  • 利用元素的 margin/padding 百分比是相对父元素 width 的性质来实现:
.square {
  width: 20%;
  height: 0;
  padding-top: 20%;
  background: orange;
}
  • 利用子元素的 margin-top 的值来实现:
.square {
  width: 30%;
  overflow: hidden;
  background: yellow;
}
.square::after {
  content: '';
  display: block;
  margin-top: 100%;
}

十三、为什么有时候⽤ translate 来改变位置⽽不是定位?

translate 是 transform 属性的⼀个值。改变 transform 或 opacity 不会触发浏览器重新布局(reflow)或重绘(repaint),只会触发复合(compositions)。⽽改变绝对定位会触发重新布局,进⽽触发重绘和复合。transform 使浏览器为元素创建⼀个 GPU 图层,但改变绝对定位会使⽤到 CPU。 因此 translate()更⾼效,可以缩短平滑动画的绘制时间。 ⽽ translate 改变位置时,元素依然会占据其原始空间,绝对定位就不会发⽣这种情况。

十四、li 与 li 之间有看不见的空白间隔是什么原因引起的?如何解决?

浏览器会把 inline 内联元素间的空白字符(空格、换行、Tab 等)渲染成一个空格。为了美观,通常是一个<li>放在一行,这导致<li>换行后产生换行字符,它变成一个空格,占用了一个字符的宽度。

解决办法:

(1)为<li>设置 float:left;。不足:有些容器是不能设置浮动,如左右切换的焦点图等。

(2)将所有<li>写在同一行。不足:代码不美观。

(3)将<ul>内的字符尺寸直接设为 0,即 font-size:0;。不足:<ul>中的其他字符尺寸也被设为 0,需要额外重新设定其他字符尺寸,且在 Safari 浏览器依然会出现空白间隔。

(4)消除<ul>的字符间隔 letter-spacing:-8px;,不足:这也设置了<li>内的字符间隔,因此需要将<li>内的字符间隔设为默认 letter-spacing:normal;。

十五、替换元素的概念及计算规则---不需要看

通过修改某个属性值呈现的内容就可以被替换的元素就称为“替换元素”。

替换元素除了内容可替换这一特性以外,还有以下特性:

  • 内容的外观不受页面上的 CSS 的影响:用专业的话讲就是在样式表现在 CSS 作用域之外。如何更改替换元素本身的外观需要类似 appearance 属性,或者浏览器自身暴露的一些样式接口。
  • 有自己的尺寸:在 Web 中,很多替换元素在没有明确尺寸设定的情况下,其默认的尺寸(不包括边框)是 300 像素 ×150 像素。
  • 在很多 CSS 属性上有自己的一套表现规则:比较具有代表性的就是 vertical-align 属性,对于替换元素和非替换元素,vertical-align 属性值的解释是不一样的。比方说 vertical-align 的默认值的 baseline,很简单的属性值,基线之意,被定义为字符 x 的下边缘,而替换元素的基线却被硬生生定义成了元素的下边缘。
  • 所有的替换元素都是内联水平元素:也就是替换元素和替换元素、替换元素和文字都是可以在一行显示的。但是,替换元素默认的 display 值却是不一样的,有的是 inline,有的是 inline-block。

替换元素的尺寸从内而外分为三类:

  • 固有尺寸:指的是替换内容原本的尺寸。例如,图片、视频作为一个独立文件存在的时候,都是有着自己的宽度和高度的。
  • HTML 尺寸:只能通过 HTML 原生属性改变,这些 HTML 原生属性包括的 width 和 height 属性、的 size 属性。
  • CSS 尺寸:特指可以通过 CSS 的 width 和 height 或者 max-width/min-width 和 max-height/min-height 设置的尺寸,对应盒尺寸中的 content box。

这三层结构的计算规则具体如下:

(1)如果没有 CSS 尺寸和 HTML 尺寸,则使用固有尺寸作为最终的宽高。

(2)如果没有 CSS 尺寸,则使用 HTML 尺寸作为最终的宽高。

(3)如果有 CSS 尺寸,则最终尺寸由 CSS 属性决定。

(4)如果“固有尺寸”含有固有的宽高比例,同时仅设置了宽度或仅设置了高度,则元素依然按照固有的宽高比例显示。

(5)如果上面的条件都不符合,则最终宽度表现为 300 像素,高度为 150 像素。

(6)内联替换元素和块级替换元素使用上面同一套尺寸计算规则。

十六、对line-height 的理解及其赋值方式

(1)line-height 的概念:

  • line-height 指一行文本的高度,包含了字间距,实际上是下一行基线到上一行基线距离;
  • 如果一个标签没有定义 height 属性,那么其最终表现的高度由 line-height 决定;
  • 一个容器没有设置高度,那么撑开容器高度的是 line-height,而不是容器内的文本内容;
  • 把 line-height 值设置为 height 一样大小的值可以实现单行文字的垂直居中;
  • line-height 和 height 都能撑开一个高度;

(2)line-height 的赋值方式:

  • 带单位:px 是固定值,而 em 会参考父元素 font-size 值计算自身的行高
  • 纯数字:会把比例传递给后代。例如,父级行高为 1.5,子元素字体为 18px,则子元素行高为 1.5 \* 18 = 27px
  • 百分比:将计算后的值传递给后代

十七、::before 和 :after 的双冒号和单冒号有什么区别?

(1)冒号(:)用于CSS3伪类,双冒号(::)用于CSS3伪元素。

(2)::before就是以一个子元素的存在,定义在元素主体内容之前的一个伪元素。并不存在于dom之中,只存在在页面之中。

注意::before 和 :after 这两个伪元素,是在CSS2.1里新出现的。起初,伪元素的前缀使用的是单冒号语法,但随着Web的进化,在CSS3的规范里,伪元素的语法被修改成使用双冒号,成为::before、::after。

十八、元素的层叠顺序

层叠顺序,英文称作 stacking order,表示元素发生层叠时有着特定的垂直显示顺序。下面是盒模型的层叠规则:

 对于上图,由上到下分别是:

(1)背景和边框:建立当前层叠上下文元素的背景和边框。

(2)负的 z-index:当前层叠上下文中,z-index 属性值为负的元素。

(3)块级盒:文档流内非行内级非定位后代元素。

(4)浮动盒:非定位浮动元素。

(5)行内盒:文档流内行内级非定位后代元素。

(6)z-index:0:层叠级数为 0 的定位元素。

(7)正 z-index:z-index 属性值为正的定位元素。

注意:当定位元素 z-index:auto;,生成盒在当前层叠上下文中的层级为 0,不会建立新的层叠上下文,除非是根元素。

十九、对 sticky 定位的理解

sticky 英文字面意思是粘贴,所以可以把它称之为粘性定位。语法:position: sticky; 基于用户的滚动位置来定位。

粘性定位的元素是依赖于用户的滚动,在 position:relative; 与 position:fixed; 定位之间切换。它的行为就像 position:relative; 而当页面滚动超出目标区域时,它的表现就像 position:fixed;,它会固定在目标位置。元素定位表现为在跨越特定阈值前为相对定位,之后为固定定位。这个特定阈值指的是 top, right, bottom 或 left 之一,换言之,指定 top, right, bottom 或 left 四个阈值其中之一,才可使粘性定位生效。否则其行为与相对定位相同。

二十、对 requestAnimationframe 的理解

实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout 来实现,CSS3 中可以使用 transition 和 animation 来实现,HTML5 中的 canvas 也可以实现。除此之外,HTML5 提供一个专门用于请求动画的 API,那就是 requestAnimationFrame,顾名思义就是请求动画帧。

MDN 对该方法的描述:

  •  window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

语法:window.requestAnimationFrame(callback); 其中,callback 是下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。该回调函数会被传入 DOMHighResTimeStamp 参数,它表示 requestAnimationFrame() 开始去执行回调函数的时刻。该方法属于宏任务,所以会在执行完微任务之后再去执行。

取消动画:使用 cancelAnimationFrame()来取消执行动画,该方法接收一个参数——requestAnimationFrame 默认返回的 id,只需要传入这个 id 就可以取消动画了。

优势:

  • CPU 节能:使用 SetTinterval 实现的动画,当页面被隐藏或最小化时,SetTinterval 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费 CPU 资源。而 RequestAnimationFrame 则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统走的 RequestAnimationFrame 也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了 CPU 开销。
  • 函数节流:在高频率事件( resize, scroll 等)中,为了防止在一个刷新间隔内发生多次函数执行,RequestAnimationFrame 可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销,一个刷新间隔内函数执行多次时没有意义的,因为多数显示器每 16.7ms 刷新一次,多次绘制并不会在屏幕上体现出来。
  • 减少 DOM 操作:requestAnimationFrame 会把每一帧中的所有 DOM 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒 60 帧。
  • setTimeout 执行动画的缺点:它通过设定间隔时间来不断改变图像位置,达到动画效果。但是容易出现卡顿、抖动的现象;原因是:settimeout 任务被放入异步队列,只有当主线程任务执行完后才会执行队列中的任务,因此实际执行时间总是比设定时间要晚;settimeout 的固定时间间隔不一定与屏幕刷新间隔时间相同,会引起丢帧。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想散在风中

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值