全文主要参考如下gituhub项目,在此基础上补充了一些自己的东西:

https://github.com/febobo/web-interview

项目网站:web前端面试 - 面试官系列

一个非常全面,细致的前端面试题库,十分的推荐,深刻体会到github的强大之处了。

说说css盒模型

浏览器在渲染html元素的时候,会把所有元素表示为一个一个矩形的盒子。

盒模型组成

  • content

    即实际内容,显示文本和图像

  • padding

    即内边距,内边距是透明的,取值不能为负,背景图片可以扩展到padding。

  • border

    即边框,由粗细、样式、颜色三部分组成。例如border:1px solid black

  • margin

    即外边距,在元素外创建额外的空白,空白通常指不能放其他元素的区域。

盒模型分类

  • w3c标准盒模型(content-box)

    width/height的范围只包括content,虽说是标准盒模型,怎么感觉没有ie盒模型好用捏。

  • ie盒模型(border-box)

    width/height的范围包括content,border,padding

1
box-sizing: content-box|border-box|inherit;//指定元素使用哪种盒模型来渲染,inherit表示继承父元素盒模型

说说em/px/rem/vh/vw区别

  • em

    是一个相对单位,和rem不同的是,相对的是父元素的字体大小

  • px

    1px即1个css像素,是绝对单位。

  • rem

    是一个相对单位,相对的是根元素html的字体大小。

    如果想要简化font-size的转化,我们可以修改html的字体大小。

    1
    html {font-size: 10px  }
  • vw/vh

    视口的宽高划分为100等份,1vw表示视口宽度的1/100,1vh表示视口高度的1/100,是一个相对单位,相对的是视口的宽/高,而百分比布局相对的是父元素的宽/高

    这里的视口,在PC端指的就是浏览器窗口,在移动端指的就是布局视口,一般就是设备屏幕

    1
    <meta name="viewport" content="width=device-width, initial-scale=1.0"> #指定布局视口宽度为设备宽度

设备像素、css像素、设备独立像素、dpr、ppi 之间的区别

  • css像素

    在web编程中,css代码中的像素,一般1px就是一个css像素

  • 设备像素

    设备像素(device pixels),又称为物理像素,指设备能控制显示的最小物理单位,就是显示屏上的一个一个像素点。

    从屏幕在工厂生产出的那天起,它上面的物理像素点就固定不变了。

    屏幕的分辨率通常为a×b的格式,分别指的是宽,高上的物理像素点的个数。

  • 设备独立像素

    设备独立像素指的是与设备无关的逻辑像素,以通过程序控制使用的虚拟像素,一个设备独立像素可能会对应多个设备像素

    我们在游戏中可以调节游戏的分辨率,实际就是在调节设备独立像素

    在屏幕未缩放情况下(100%),1css像素=1设备独立像素,我们也可以推断出缩放屏幕,其实就是在改变css像素设备独立像素的比例关系

  • dpr

    dpr = 设备像素/设备独立像素,在PC端,dpr = 1,在移动端,dpr>=1

    我们通常讨论的是不缩放的情况下,也就是1css像素就是1设备独立像素,因此dpr又可以被认等于设备像素/css像素

    当设备像素比为1:1时,使用1(1×1)个设备像素显示1个CSS像素

    当设备像素比为2:1时,使用4(2×2)个设备像素显示1个CSS像素

    当设备像素比为3:1时,使用9(3×3)个设备像素显示1个CSS像素

  • PPI

    pixel per inch每英寸像素点的个数,这个PPI越大,说明物理像素点的密度更大,像素点一般也更多,像素点大小也越小,能更细致的展现图像。

    比如,iPhone 3GS 和 iPhone 4/4s 的尺寸都是 3.5 寸,但 iPhone 3GS 的分辨率是 320x480,iPhone 4/4s 的分辨率是 640x960

    这意味着,iPhone 3GS 在上有320个物理像素,iPhone 4/4s 在上有 640 个物理像素,也就是说iPhone 4/4s的PPI更大

    如果我们按照真实的物理像素进行布局,比如说我们按照 320 物理像素进行布局,到了 640 物理像素的手机上就会有一半的空白,为了避免这种问题,就出现了虚拟像素单位

    我们统一 iPhone 3GS 和 iPhone 4/4s 都是 320 个虚拟像素,只是在 iPhone 3GS 上,最终 1 个虚拟像素换算成 1 个物理像素,在 iphone 4s 中,1 个虚拟像素最终换算成 2 个物理像素(DPR=2)

    可以看到光提高PPI还是不够的,为了解决空白问题吗,还要修改DPR

    我们思考一下,为什么原本能用1个物理像素表示的,为了解决空白问题就硬要用2个物理像素表示呢,就非得让屏幕中的所有像素点参与展示吗,这样不就等同于放大图片吗,放大图片不是会变模糊吗,因为图片本身没有这么多细节(分辨率<实际用来渲染的像素数目),所以对于PPI更高的设备,我们要准备分辨率更高的图片(分辨率越高的图片,文件大小越大,占用的物理像素点越多,和屏幕分辨率是一个概念),才能完美的解决空白问题。

    举个例子,我们有50x50分辨率(在PC端,DPR为1的设备,就是50x50px)的图片,放到iPhone 3GS上(DPR=1)会占用50x50像素(物理像素),放到iPhone4/4s上(DPR=2),会占用100*100像素(物理像素),因为图片分辨率 < 实际参与渲染的物理像素,所以会变模糊。

    如果我们放一个 100x100 分辨率(在PC端,DPR为1的设备,就是100x100px)图片,然后手动的把这个图片缩小为 50x50(css像素,即px),放到iPhone4/4s上,会占用100*100像素(物理像素),因为图片分辨率 = 实际参与渲染的物理像素,所以会被清晰的展示。

    所以一般情况下,我们认为PPI越大,图像越清晰,因为我们能用更多更小的像素来渲染图片。

如何实现元素隐藏

  • display:none
    • 能实现元素隐藏,而且元素不再占有原来的位置
    • 会触发页面的重排重绘
    • 元素对应的dom对象仍然存在,无法再响应点击事件
  • visibility:hidden
    • 能实现元素隐藏,而且元素占有原来的位置
    • 只会触发页面的重绘
    • 元素对应的dom对象仍然存在,无法再响应点击事件
  • opacity:0
    • 能实现元素隐藏,而且元素占有原来的位置
    • 元素对应的dom对象仍然存在,且能响应点击事件
    • 一般情况下也会引发重绘

总结

这三种方法都能实现元素隐藏,display:none会让元素不再占有原来的位置,而其他两种则会保留原来的位置。

添加了display:nonevisibility:hidden的元素都无法再响应点击事件,而添加了opacity:0的元素可以。

谈谈你对BFC的理解

什么是BFC

也叫块级格式化上下文,可以理解为css中的一种属性,开启了bfc的盒子被视为一块独立的渲染区域,内部元素不会影响外部元素的布局。

如何开启BFC

  • html标签默认开启了BFC
  • visibility的值不为visible的元素
  • 添加了绝对定位固定定位的元素
  • 开启了浮动的元素
  • 开启了flex布局或者grid布局的元素(这2种布局的内部元素布局方式确实独特)

开启了BFC有什么作用

  • 可以用来清除浮动

    BFC盒子内部的浮动元素也参与BFC盒子高度的计算。

    所以浮动元素,绝对定位元素。固定定位元素,不需要担心浮动元素不参与自身高度的计算 。

  • 开启了BFC的元素不会与浮动元素重叠

    所以浮动元素不会被浮动元素压住。

  • 可以用来解决边距塌陷问题

    如果两个相邻的盒子存在边距塌陷问题,只要让其中一个盒子包裹一个开启了BFC的盒子就能解决边距塌陷问题。

介绍一下flex弹性布局

开启Flex布局的元素(display:flex),称为flex容器container,它的所有子元素自动成为容器成员,称为flex项目item

容器属性

包括flex-directionflex-wrapflex-flowjustify-contentalign-itemsalign-content

  • flex-direction

    决定主轴的方向(即项目的排列方向)

    1
    2
    3
    .container {   
    flex-direction: row | row-reverse | column | column-reverse;
    }
    • row(默认值):主轴为水平方向,起点在左端,从左到右摆放
    • row-reverse:主轴为水平方向,起点在右端,从右到左摆放
    • column:主轴为垂直方向,起点在上沿。从上到下摆放
    • column-reverse:主轴为垂直方向,起点在下沿,从下到上摆放
  • flex-wrap

    用来决定子元素一行放不下了是否换行

    1
    2
    3
    .container {  
    flex-wrap: nowrap | wrap | wrap-reverse;
    }
    • nowrap(默认值):不换行
    • wrap:换行,第一行在下方
    • wrap-reverse:换行,第一行在上方
  • flex-flow

    flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap

    1
    2
    3
    .box {
    flex-flow: <flex-direction> || <flex-wrap>;
    }
  • justify-content

    定义了项目(子元素)在主轴上的对齐方式

    1
    2
    3
    .box {
    justify-content: flex-start | flex-end | center | space-between | space-around;
    }
    • flex-start(默认值):开始处(start)开始对齐

    • flex-end:结束处(end)开始对齐

    • center:居中对齐

    • space-between:两端对齐,项目之间的间隔都相等

    • space-around:两个项目两侧间隔相等

    1
    2
    3
    4
    5
    <div class="box">
    <div class="a">a</div>
    <div class="b">b</div>
    <div class="c">c</div>
    </div>
  • align-items

    定义子元素如何在侧轴上对齐(垂直于主轴),只适用于只有一根主轴线的时候(如果主轴为row,就是只有一行)

    1
    2
    3
    .box {
    align-items: flex-start | flex-end | center | baseline | stretch;
    }
    • flex-start:交叉轴的起点对齐
    • flex-end:交叉轴的终点对齐
    • center:交叉轴的中点对齐, 就是居中对齐
    • baseline: 项目的第一行文字的基线对齐
    • stretch(默认值,拉伸):如果项目未设置高度或设为auto,将占满整个容器的高度。
  • align-content

    定义多根主轴的对齐方式。如果项目只有一根主轴线(比如主轴是row的时候,只有一行子元素),该属性不起作用。

    1
    2
    3
    .box {
    align-content: flex-start | flex-end | center | space-between | space-around | stretch;
    }
    • flex-start:与交叉轴的起点对齐
    • flex-end:与交叉轴的终点对齐
    • center:与交叉轴的中点对齐
    • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布
    • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍
    • stretch(默认值):轴线占满整个交叉轴

    可以看到,这些属性值和justify-content的属性值是几乎一样的,如果主轴有多根,可以把每根主轴当作侧轴上的一个元素

容器成员属性

  • order

    定义项目的排列顺序。数值越小,排列越靠前,默认为0,不指定order属性,那就是order:0

    默认情况,子元素的排列顺序是在html文档中的书写顺序,而指定order能改变这个顺序。

    1
    2
    3
    .item {
    order: <integer>;
    }
  • flex-grow

    当容器设为flex-wrap: nowrap(默认值);即不换行的时候,如果一行没有占满:

    • 默认为0,即如果一行没有占满,也不放大。

    • 如果子元素flow-grow的值不为0,无论是否指定宽度,都会根据flex-grow的值来分割剩余空间

    • 子元素flex-grow属性都为1,则它们将等分剩余空间。

  • flex-shrink

    定义了子元素的缩小比例(容器宽度<子元素总宽度时如何收缩),默认为1

    如果所有子元素的flex-shrink属性都为1,当空间不足时,都将等比例缩小

    如果一个子元素的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    .box {
    width: 250px;
    height: 400px;
    display: flex;
    background-color: antiquewhite;
    }
    .a {
    width: 100px;
    height: 100px;
    background-color: aqua;
    }
    .b {
    width: 100px;
    height: 100px;
    background-color: red;
    }
    .c {
    width: 100px;
    height: 100px;
    background-color: gold;
    }

    可以看到即便子元素指定了宽度,如果一行放不下,也会等比例缩小。我们可以通过修改flex-shrink属性来修改具体的缩小规则。

  • flex-basis

    设置的是元素在主轴上的初始尺寸,所谓的初始尺寸就是元素在flex-growflex-shrink生效前的尺寸。

    • auto: 默认值,这意味着项目将根据其内容确定其初始大小,如设置了width则元素尺寸由width/height决定
    • 固定值:你可以指定一个具体的长度单位,如像素 (px)、百分比 (%) 等。这会设置项目的初始大小,不考虑内容的自然尺寸。
    • 0:完全依赖flex-grow 来分配多余的空间。这种方式常用于创建等分布局,如果令一个元素的flex-basis:0,由于flex-grow默认为0,此时无论是否指定宽度,在主轴上的宽度都为0
  • flex

    flex属性是flex-grow, flex-shrinkflex-basis的简写,默认值为0 1 auto,也是比较难懂的一个复合属性。

    我们通常的书写方式flex:1修改的是flex-grow的值,即规定了分割剩余空间时的权重或者份数。

  • align-self

    允许侧轴上的单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性,但是不能覆盖align-content的,

    默认值为auto,表示继承父元素的align-items属性。

    1
    2
    3
    .item {
    align-self: auto | flex-start | flex-end | center | baseline | stretch;
    }

介绍一下grid网格布局

通过display:grid/inline-grid就能开启网格布局

1
2
3
4
5
6
7
<div class="container">
<div class="item item-1">
<p class="sub-item"></p >
</div>
<div class="item item-2"></div>
<div class="item item-3"></div>
</div>

上述代码实例中,.container元素就是网格布局容器,.item元素就是网格的项目,由于网格元素只能是容器的顶层子元素,所以p元素并不是网格元素

同样,Grid 布局属性可以分为两大类:

  • 容器属性
  • 项目属性

容器属性

  • display

    • display:grid 则该容器是一个块级元素
    • display: inline-grid 则容器元素为行内元素
  • grid-template-columns/grid-template-rows

    grid-template-columns 属性设置列宽grid-template-rows 属性设置行高

    1
    2
    3
    4
    5
    6
    7
    8
    .wrapper {
    display: grid;
    /* 声明了三列,宽度分别为 200px 200px 200px */
    grid-template-columns: 200px 200px 200px;
    grid-gap: 5px;
    /* 声明了两行,行高分别为 50px 50px */
    grid-template-rows: 50px 50px;
    }

    以上表示固定列宽为 200px,行高为 50px

    上述代码可以看到重复写单元格宽高,通过使用repeat()函数,可以简写重复的值

    • 第一个参数是重复的次数
    • 第二个参数是重复的

    所以上述代码可以简写成

    1
    2
    3
    4
    5
    6
    .wrapper {
    display: grid;
    grid-template-columns: repeat(3,200px);
    grid-gap: 5px;
    grid-template-rows:repeat(2,50px);
    }

    除了上述的repeact关键字,还有:

    • auto-fill:示自动填充,让一行(或者一列)中尽可能的容纳更多的单元格,只能在repeat函数中使用

      1
      2
      //表示列宽是 200 px,但列的数量是不固定的,只要浏览器能够容纳得下,就可以放置更多的列
      grid-template-columns: repeat(auto-fill, 200px)
    • fr:片段,为了方便表示比例关系,非常类似flex布局中的份数。

      1
      2
      //表示第一个列宽设置为 200px,后面剩余的宽度分为两部分,宽度分别为剩余宽度的 1/32/3
      grid-template-columns: 200px 1fr 2fr
    • minmax:产生一个长度范围,表示长度就在这个范围之中都可以应用到网格项目中。第一个参数就是最小值,第二个参数就是最大值

      minmax(100px, 1fr)表示列宽不小于(>=)100px,不大于(<=)1fr

      1
      grid-template-columns:repeat(auto-fill,minmax(260px,1fr))
    • auto:由浏览器自己决定长度

      1
      2
      //表示第一第三列为 100px,中间由浏览器决定长度
      grid-template-columns: 100px auto 100px
  • gap

    定义宫格之间的间隔

容器成员属性

CSS如何画一个三角形

可以通过指定边框(border)样式实现。

1
2
3
4
5
6
.border {
width: 50px;
height: 50px;
border: 50px solid;
border-color: #96ceb4 #ffeead #d9534f #ffad60;
}

这样一个盒子的样式如下:

当盒子的宽高不断减小,那每个边框是不是就会变成一个三角形呢?

这样会得到四个三角形,但是我们只要一个,我们可以把其他边框的颜色变透明,但是这样隐藏的部分仍然占据部分高度

比如我们只要下面的三角形,那我们就可以让上边框的的宽度变为0,效果就是这样。

然后再让左右边框变为透明的就好了。

1
2
3
4
5
6
7
.border {
width: 0;
height: 0;
border-style: solid;
border-width: 0 50px 50px 50px;
border-color: transparent transparent #d9534f transparent;
}

如何实现元素水平居中,垂直居中

水平居中

  • 行内元素/行内块元素

    • 给父元素添加text-align:center

    • 给父元素添加flex布局

      1
      2
      display: flex; 
      justify-content: center;
    • 给自身添加绝对定位

      1
      2
      3
      position: absolute;
      left: 50%;
      transform: translateX(-50%);
  • 块级元素

    • 添加margin: 0 auto,要求块级元素的宽度确定

    • 给父元素添加flex布局

      1
      2
      display: flex; 
      justify-content: center;
    • 给自身添加绝对定位

      1
      2
      3
      position: absolute;
      left: 50%;
      transform: translateX(-50%);

垂直居中

  • 行内元素

    • 指定行内元素的行高line-height为父元素高度。

    • 给父元素添加flex布局

      1
      2
      display: flex; 
      align-items: center;
    • 给自身添加绝对定位

      1
      2
      3
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
  • 行内块元素/块级元素

    • 给父元素添加flex布局

      1
      2
      display: flex; 
      align-items: center;
    • 给自身添加绝对定位

      1
      2
      3
      position: absolute;
      top: 50%;
      transform: translateY(-50%);

总结

绝对定位flex布局是万能的。margin:0 autotext-align:centerline-height:父元素高度,是一些特殊情况。

说说对CSS预处理语言的理解?有哪些区别?

背景

  • CSS代码看起来是没有逻辑性的,不方便维护及扩展,不利于复用css预处理语言在css的基础上,添加了变量混入嵌套等功能,让css代码看起来更有逻辑性,更容易维护和复用。

  • css预处理语言包括一套自定义的语法和一个解析器,解析器会把用自定义语法编写的代码转化成css代码

常见的css预编译语言

  • sass

    2007 年诞生,最早也是最成熟的 Css预处理器,拥有 Ruby 社区的支持和 Compass 这一最强大的 Css框架,目前受 LESS 影响,已经进化到了全面兼容 CssScss

    sass和scss的区别与联系

    • 是同一种css预处理语言(sass)的两种不同语法形式,它们都扩展了标准的 CSS,虽然有不同的自定义语法,但是使用同一个解析器(编译器)来处理
    • sass使用缩进来表示代码块,不使用大括号 {} 和分号 ;更接近 Python 的风格,强调简洁性和可读性。文件使用 .sass 扩展名
    • scss与 CSS 的语法几乎完全相同,所以更容易被现有开发者接受和使用。文件使用 .scss 扩展名
  • less

    2009年出现,受SASS的影响较大,但又使用 Css 的语法,让大部分开发者和设计师更容易上手,在 Ruby社区之外支持者远超过 SASS

    其缺点是比起 SASS来,可编程功能不够,不过优点是简单和兼容 Css,反过来也影响了 SASS演变到了Scss 的时代

常用特性

  • 嵌套

    二者的嵌套语法都是一致的,甚至连引用父级选择器的标记 & 也相同

    区别只是 Sass可以用没有大括号的方式书写(用缩进)

    1
    2
    3
    4
    5
    .a {
    &.b {
    color: red;
    }
    }
  • 变量

    变量的出现有效的提高了css代码复用性,减少了不必要的硬编码

    less声明的变量必须以@开头,后面紧跟变量名和变量值,而且变量名和变量值需要使用冒号:分隔开

    1
    2
    3
    4
    @red: #c00;
    strong {
    color: @red;
    }

    sass声明的变量跟less十分的相似,只是变量名前面使用$开头

    1
    2
    3
    4
    5
    $red: #c00;

    strong {
    color: $red;
    }
  • 作用域

    在css预处理语言中,变量是具有作用域的。

    sass中不存在全局变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $color: black;
    .scoped {
    $bg: blue;
    $color: white;
    color: $color;
    background-color:$bg;
    }
    .unscoped {
    color:$color;
    }

    编译后

    1
    2
    3
    4
    5
    6
    7
    .scoped {
    color:white;/*是白色*/
    background-color:blue;
    }
    .unscoped {
    color:white;/*白色(无全局变量概念)*/
    }

    所以,在sass中最好不要定义相同的变量名,后面定义的会覆盖前面定义的。

    less的变量作用域跟javascript十分的相似,有局部变量全局变量之分。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @color: black;
    .scoped {
    @bg: blue;
    @color: white;
    color: @color;
    background-color:@bg;
    }
    .unscoped {
    color:@color;
    }

    编译后:

    1
    2
    3
    4
    5
    6
    7
    .scoped {
    color:white;/*白色(调用了局部变量)*/
    background-color:blue;
    }
    .unscoped {
    color:black;/*黑色(调用了全局变量)*/
    }
  • 混入

    Mixins可以将一部分样式抽出,作为单独定义的模块,被很多选择器重复使用,可以说是css预处理语言的精髓所在。

    less中的用法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    .alert {
    font-weight: 700;
    }
    //这不是在定义函数吗
    .highlight(@color: red) {
    font-size: 1.2em;
    color: @color;
    }

    .heads-up {
    .alert;
    //函数调用?
    .highlight(red);
    }

    编译后

    1
    2
    3
    4
    5
    6
    7
    8
    .alert {
    font-weight: 700;
    }
    .heads-up {
    font-weight: 700;
    font-size: 1.2em;
    color: red;
    }

    Sass声明mixins时需要使用@mixin,后面紧跟mixin的名,也可以设置参数,参数名为变量$声明的形式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @mixin large-text {
    font: {
    family: Arial;
    size: 20px;
    weight: bold;
    }
    color: #ff0000;
    }

    .page-title {
    @include large-text;
    padding: 4px;
    margin-top: 10px;
    }
  • 代码模块化

    模块化就是将复杂的Css代码按某种规则划分为一个个文件,每个文件就是一个模块,模块可以通过@import引入

    scssless二者的使用方法都如下所示,这样的导入语法不能直接在标准的 CSS 文件中使用。

    1
    2
    3
    4
    @import './common';
    @import './github-markdown';
    @import './mixin';
    @import './variables';

如何实现单行/多行文本溢出的省略样式?

对于文本的溢出,我们可以分成两种形式:

  • 单行文本溢出
  • 多行文本溢出

单行文本溢出显示

涉及的css属性有:

  • text-overflow
  • white-space
  • overflow

overflow设为hidden,普通情况用在块级元素的外层隐藏内部溢出元素,或者配合下面两个属性实现文本溢出省略

white-space:nowrap,作用是设置文本不换行,是overflow:hiddentext-overflow:ellipsis生效的基础

text-overflow属性值有如下:

  • clip:当对象内文本溢出部分裁切
  • ellipsis:当对象内文本溢出时显示省略标记(…)

text-overflow只有在设置了overflow:hiddenwhite-space:nowrap才能够生效。

多行文本溢出显示

多行文本溢出的时候,我们可以分为两种情况:

  • 基于高度截断

    就是把省略号作为伪元素,添加到容器,并使用绝对定位定位到行尾并遮住文字,再通过 overflow: hidden 隐藏多余文字。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    .demo {
    position: relative;
    line-height: 20px;
    height: 40px;
    overflow: hidden;
    }
    .demo::after {
    content: "...";
    position: absolute;
    bottom: 0;
    right: 0;
    padding: 0 20px 0 10px;
    }

CSS提高性能的方法有哪些?

浏览器渲染的流程

浏览器有一个固定的渲染流程——只有在布局(layout)完成后才能绘制(paint)页面,而布局的前提是要生成渲染树(render tree),而渲染树的生成则需要 DOM树CSSOM 树的配合。

如果先让用户看到一个没有样式的页面,等 CSS 样式解析完后再重绘(repaint),这样的体验会很差。所以,浏览器会等到确定需要 CSS 时才开始渲染。只有在下载完 CSS 并生成 CSS 对象模型 (CSSOM)之后,浏览器会绘制页面。

如何提高性能

  • 内联首屏关键CSS
  • 异步加载CSS
  • 资源压缩
  • 合理使用选择器
  • 不要使用@import

内联首屏关键css

  • 如何找出关键css代码

    有许多工具可以帮助自动生成和内联关键 CSS,例如:

    • Penthouse:一个 Node.js 库,用于生成关键路径 CSS。
    • Webpack 插件:如 critical-css-webpack-plugin,可以在构建过程中自动处理关键 CSS。
  • 如何内联

    将提取出的关键 CSS 直接嵌入到 HTML 文件的 <head> 部分,使用 <style> 标签包裹

  • 为什么内联关键css能提高首屏渲染速度

    内联css代码将成为html文件的一部分,会随着html文件的下载而被下载,能够使浏览器在下载完html后就能立刻渲染

    说简单点就是节省了下载的时间

    而如果外部引用css代码,在解析html结构过程中遇到外部css文件,才会开始下载css代码,再解析,再渲染。

    所以,CSS内联使用使渲染时间提前

  • 那为什么不把全部css代码内联到html文件

    虽然内联关键 CSS 可以加速首屏渲染,但过大的内联样式可能会增加 HTML 文件的大小,反而影响加载速度。因此,应该尽量保持内联 CSS 的精简。

异步加载(就是下载)css

对于首屏关键css我们内联到html文件,其他非关键css代码放到外部文件中,再通过link标签引入,但浏览器在解析 HTML 时遇到 <link rel="stylesheet"> 标签时,默认等待这些样式表下载并解析完成之后才继续渲染页面,也就是说会阻塞页面的渲染

然而,非首屏关键css不应该阻塞页面的加载,因为这些样式没有必要立即加载出来,可以采用异步加载的方式来加载这些外部css文件。

  • 利用媒体查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!-- 加载和解析 styles.css 会阻塞渲染,因为这个样式表在浏览器渲染过程中是要用到的,浏览器会等待它解析完再渲染 -->
    <link rel="stylesheet" href="styles.css" />

    <!-- 加载和解析 print.css 不会阻塞渲染,因为这个样式表在浏览器渲染过程中不会用到 -->
    <link rel="stylesheet" href="print.css" media="print" />

    <!-- 在大屏幕上,加载和解析 mobile.css 不会阻塞渲染,因为这个样式表在浏览器渲染过程中不会用到 -->
    <link
    rel="stylesheet"
    href="mobile.css"
    media="screen and (max-width: 480px)" />

    可以看出,css文件的下载和解析本身不会阻塞浏览器渲染,阻塞的原因是在等待需要用到的css文件解析完毕罢了

    设置一个当前浏览器不支持的值:

    1
    <link rel="stylesheet" href="./index.css" media="print" onload="this.media='all'">

    在加载完毕之后,使用onload属性将link的媒体类型设置为all,然后便开始渲染。

    这样这个css文件的加载不会阻塞浏览器渲染,加载结束之后变为浏览器渲染需要用到的css文件,于是开始解析(此时会阻塞渲染),解析完毕浏览器开始渲染。

  • 利用js插入link标签

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function loadCSS(url) {
    var link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = url;
    document.head.appendChild(link);
    }
    //等待dom树构建完毕再加载非关键css代码
    //因为是在dom树构建完毕后再添加的link标签,能再一定程度上延迟css文件的加载和解析
    document.addEventListener('DOMContentLoaded', function() {
    loadCSS("styles.css");
    });
  • 使用预加载preload

    预加载最适合通常由浏览器较晚发现的资源,通过预加载某个资源,是在告知浏览器希望在浏览器发现该资源之前提前提取该资源,因为确定该资源对当前网页很重要

    现代浏览器在确定资源优先级方面已经非常出色,因此请务必谨慎使用 preload,并仅预加载最重要的资源。

    可以使用 rel="preload"link元素转换为预加载器,用于关键资源。

    1
    <link rel="preload" href="style.css" as="style" />

    preload,浏览器会尽快获取引用的资源,并将其存储在浏览器缓存中(它不会执行脚本或应用样式表,单纯只是下载),以便在后续代码中引用时可以更快地使用它们。为了让用户体验尽可能流畅,我们应提前加载页面加载初期用户会遇到的高优先级资源。

资源压缩

利用webpackgulp/gruntrollup等模块化工具,将css代码进行压缩,使文件变小,大大降低了浏览器的加载时间

合理使用css选择器

css匹配的规则是从右往左开始匹配,例如#markdown .content h3匹配规则如下:

  • 先找到h3标签元素
  • 然后去除祖先不是.content的元素
  • 最后去除祖先不是#markdown的元素

如果嵌套的层级更多,页面中的元素更多,那么匹配所要花费的时间代价自然更高。

所以最好不要嵌套使用过多复杂选择器。

不使用@import

css样式文件有两种引入方式,一种是link元素,另一种是@import

使用 @import 规则引入的 CSS 文件是串行加载的

1
2
3
4
5
6
/* styles.css */
@import url('reset.css');
@import url('layout.css');
@import url('theme.css');

body { /* ... */ }

在这个例子中,reset.csslayout.csstheme.css 是按顺序加载的,只有当前一个文件加载完成后才会开始加载下一个。

1
2
3
4
5
<head>
<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="theme.css">
</head>

尽管 <link> 标签默认会阻塞页面渲染,但它允许浏览器并行加载多个 CSS 文件。这意味着虽然整个页面的渲染仍然会被延迟,但单个文件的加载不会影响其他文件的下载直到所有这些文件都解析完毕

怎么理解回流跟重绘?什么场景下会触发?

是什么

HTML中,每个元素都会被表示成一个盒子,在浏览器解析过程中,会涉及到回流重绘

  • 回流:布局引擎会根据各种样式计算每个盒子在页面上的大小位置简单的来说,回流的作用就是布局元素,即确定元素的位置和大小
  • 重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制简单的来说,就是更为细致的渲染盒子

具体的浏览器解析渲染机制如下所示:

  • 解析HTML,生成DOM树,解析CSS,生成CSSOM树
  • 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
  • Layout(回流): 根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
  • Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  • Display:将像素发送给GPU,展示在页面上

在页面初始渲染阶段,回流不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局发生改变

当我们对 DOM 的修改引发了 DOM几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来

当我们对 DOM的修改导致了样式的变化(colorbackground-color),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了重绘。

如何触发回流

那么当页面布局或者几何信息发生变化的时候,就需要回流,如下面情况:

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的大小发生变化(包括margin、padding、border、width,height等,即盒模型的四部分)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代
  • 页面一开始渲染的时候(这避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的,这个依据变了,就难免要重新计算)
  • 获取元素特定的属性,比如client,scroll,offset开头的布局属性,这些属性有一个共性,就是需要通过即时计算得到。因此浏览器为了获取这些值,也会进行回流。

如何触发重绘

触发回流一定会触发重绘

除此之外还有一些其他引起重绘行为:

  • 颜色的修改
  • 文本方向的修改
  • 阴影的修改

浏览器的优化机制

由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列

当你获取布局信息的操作的时候,会强制队列刷新,包括前面讲到的offsetTop等方法都会返回最新的数据

因此浏览器不得不立即清空队列,触发回流重绘来返回正确的值。

css选择器有哪些?优先级?哪些属性可以继承?

CSS3新增了那些特性

是什么

css,即层叠样式表(Cascading Style Sheets)的简称,是一种标记语言,由浏览器解释执行用来使页面变得更美观

css3css这种标记语言的最新标准,是向后兼容的,CSS1/2的特性在CSS3 里都是可以使用的

CSS3 也增加了很多新特性,为开发带来了更佳的开发体验,让页面变得更为美观

新增选择器

新增样式

  • 边框

    • border-radius:创建圆角边框

      值可以是绝对单位px也可以是百分比单位。

      如果是绝对单位,比如为10px,则从盒模型每个角的顶点开始,往两条边的方向分别延伸10px,然后在这两个点的位置做一条垂直的边,确定交点,然后再以这个焦点为圆心做一个圆,然后在舍去这个圆截取的部分 ,就得到了圆角模型。

      如果单位是百分比,则先转化为绝对单位,相对的是每个角对应的两条边。

      1
      2
      3
      4
      5
      6
      .box {
      width: 300px;
      height: 50px;
      border-radius: 10px;
      background-color: pink;
      }

      图中圆的半径为10px

      1
      2
      3
      4
      5
      6
      .box {
      width: 300px;
      height: 50px;
      border-radius: 50%;
      background-color: pink;
      }

      图中的红线只是辅助线,可以看到当令 border-radius: 50%时就能得到一个椭圆,如果盒子的宽高相等就是一个圆形。

      50%border-radius能取得的最大值,再增加也不会有效果。

    • box-shadow:为元素添加阴影

      设置属性如下:

      • 水平阴影(x轴)
      • 垂直阴影(y轴)
      • 模糊距离(z轴)
      • 阴影尺寸(影子大小)
      • 阴影颜色
      • 内/外阴影
      1
      box-shadow: 3px 3px 4px;

      如果第一个属性值(x)为正数,则阴影就会出现在盒模型的右边 ,值越大右边阴影越,反之则出现在盒模型的左边

      如果第二个属性值(y)为正数,则阴影就会出现在盒模型的下边 ,值越大下边阴影越,反之则出现在盒模型的上边

      如果第三个属性值(z)表示盒模型距离视口的距离,一般为正值,这个值增大可以看到盒模型仿佛在贴近屏幕,阴影就变越来越模糊了。

      第四个值为阴影的尺寸,其实修改第一,二个属性的值就能控制阴影的大小了,这个值存在的意义就是在原有的大小上再添加。

    • border-image:使用图片来绘制边框

      1
      2
      3
      4
      5
      6
      7
      //添加border-image
      .box {
      width: 300px;
      height: 50px;
      border: 40px solid black;
      background-color: pink;
      }

      1
      2
      3
      4
      5
      6
      7
      8
      //添加border-image
      .box2 {
      width: 300px;
      height: 50px;
      border: 40px solid black;
      border-image: url("C:/Users/35194/Desktop/hexo/myblog/source/images/cover/江南.png");
      background-color: pink;
      }

      看起来好像很鸡肋啊。

  • 背景

  • 颜色

  • transition过渡

    transition属性可以为指定的一个或多个CSS属性添加过渡效果,让样式改变看起来更自然,多个属性之间用逗号进行分隔,必须规定两项内容:css属性持续时间

    1
    transition: CSS属性,花费时间,效果曲线(默认ease),延迟时间(默认0)
  • transform转换

    transform属性允许你平移缩放旋转指定元素

  • animation 动画

  • 渐变

    • linear-gradient:线性渐变

      线性渐变创建了一条沿直线前进的颜色带,这条直线的方向角度是可以自定义的。

      基础线性渐变

      1
      2
      3
      .simple-linear {
      background: linear-gradient(blue, pink);//注意是background属性,不能是background-color
      }

      改变渐变方向

      默认情况下,线性渐变的方向是从上到下,你可以指定一个值来改变渐变的方向。

      1
      2
      3
      .horizontal-gradient {
      background: linear-gradient(to right, blue, pink);
      }

      1
      2
      3
      .diagonal-gradient {
      background: linear-gradient(to bottom right, blue, pink);
      }

      设置渐变角度

      在使用角度的时候,0deg 代表渐变方向为从下到上,90deg 代表渐变方向为从左到右,诸如此类正角度都属于顺时针方向。而负角度意味着逆时针方向。

      1
      2
      3
      .angled-gradient {
      background: linear-gradient(45deg, blue, pink);
      }

      控制渐变的进程

      默认情况下,渐变在两个相邻的色标之间都是均匀推进的,两个色标之间的中点是颜色值的中点,也可以控制在哪个位置才开始均匀推进。

      1
      2
      3
      4
      5
      .box{
      width: 100px;
      height: 100px;
      background: linear-gradient(blue 50px, pink);//可以是px,也可以是相对单位。
      }

      如图所示,前50px的部分的颜色都是blue

      堆叠渐变

      你可以将渐变与其他的渐变堆叠起来。只要顶部的渐变不是完全不透明的,那么下面的渐变就会依然可见。

      顶部的渐变就是最先声明的渐变,就是书写位置最靠前的渐变。

      1
      2
      3
      4
      5
      6
      7
      .a {
      width: 100px;
      height: 100px;
      //渐变的声明顺序不能乱。
      background: linear-gradient(to bottom, transparent 10px, #fff),
      linear-gradient(to right, blue, pink);
      }

    • radial-gradient:径向渐变

      径向渐变类似于线性渐变,除了是从一个中心点向外辐射的。你可以指定中心点的位置。你还可以使其为圆形或者是椭圆形

      更多内容参考:使用 CSS 渐变 - CSS:层叠样式表 | MDN

  • flex布局与grid布局

    有文章介绍,不赘述。

CSS3新增动画

css实现动画的方式,有如下几种,都是css3新增的

  • transition 实现渐变动画
  • transform 转变动画
  • animation 实现自定义动画

transition

transition的属性如下:

  • property:填写需要变化的css属性
  • duration:完成过渡效果需要的时间单位(s或者ms)
  • timing-function:完成效果的速度曲线
  • delay: 动画效果的延迟触发时间

time-function的所有值如下

注意:并不是所有的属性都能使用过渡的,如display:none<->display:block,而visibility:hidden<->visibility:visible却可以,所以不是显示与隐藏的问题,就是display:none<->display:block不能。

transform

一般配合transition过渡使用

不影响其他盒子的位置。元素会在视觉上发生位置变化,但实际位置没变,这一点和相对定位相同。

注意的是,transform不支持inline元素(因为无法指定宽高(也无法指定垂直外边距)),使用前把它变成block

包含四个常用的功能:

  • translate:位移

    1
    transform:translate(15px,15px) //向右向下平移15px

    传入的单位也可以是百分比,相对的是元素自身的宽/高

  • rotate:旋转

    1
    transform:rotate(45deg

    正值表示为顺时针旋转45度

    1
    transform-origin: x y

    设置平面转换中心点,默认是元素中心,x y可以是像素(px),也可以是百分比,相对的也是元素自身的宽高,比如transform-origin: 50% 50%;指定的转换中心点就是盒子中点。也可以是方位名词,所有组合方式只能表示9个点。

  • scale:缩放

    1
    transform:scale(x,y) 

    里面的数字不跟单位,就是倍数的意思,分别表示宽高缩放为原来的多少倍。

  • skew:倾斜

animation

先通过@keyframes定义动画,再通过animation属性使用动画

定义动画

CSS 动画只需要定义一些关键的帧,而其余的帧,浏览器会根据计时函数插值计算出来

通过 @keyframes 来定义关键帧

比如,如果我们想要让元素旋转一圈,只需要定义开始结束两帧即可:

1
2
3
4
5
6
7
8
9
10
11
@keyframes rotate{
//开始样式
from{
//在大括号内具体描述每个时刻对应的样式
transform: rotate(0deg);
}
//结束样式
to{
transform: rotate(360deg);
}
}

from 表示最开始的那一帧,to 表示结束时的那一帧。rotate表示自定义的动画名。

也可以使用百分比刻画生命周期:

1
2
3
4
5
6
7
8
9
10
11
12
@keyframes rotate{
0%{
//在大括号内具体描述每个时刻对应的样式
transform: rotate(0deg);
}
50%{
transform: rotate(180deg);
}
100%{
transform: rotate(360deg);
}
}

使用动画

定义好动画,我们就可以直接使用了,使用动画涉及到了许多属性,记忆起来较为困难。

animation是其他8个属性的简写,当使用 animation 简写属性时,按顺序来书写值是很重要的。如果你省略了某些中间的属性,必须确保后续属性仍然按照正确的顺序给出。参考下面书写的顺序。

属性描述属性值
animation-name指定 @keyframes 动画的名称
animation-duration指定动画完成一个周期所需要时间,单位秒(s)或毫秒(ms),默认是 0
animation-timing-function指定动画计时函数,即动画的速度曲线,默认是 “ease”linear、ease、ease-in、ease-out、ease-in-out
animation-delay指定动画延迟时间,即动画何时开始,默认是 0
animation-iteration-count指定动画播放的次数,默认是 1具体次数,infinite(无限多次)
animation-direction 指定动画播放的方向默认是 normalnormal、reverse、alternate、alternate-reverse
animation-fill-mode定义动画在播放之前或之后如何影响元素的样式。默认是 noneforwards、backwards、both
animation-play-state指定动画播放状态,正在运行或暂停。默认是 runningrunning、pauser

什么是响应式设计?响应式设计的基本原理是什么?

是什么

响应式设计的目标是确保一个网站在不同类型的设备上都能良好地工作,能够根据视口尺寸和大小来布局元素

如何实现

响应式设计的基本原理是通过媒体查询检测不同的设备屏幕尺寸从而做对应处理,为了处理移动端,页面头部必须有meta声明viewport

1
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no”>
  • width=device-width: 布局视口的宽度等于设备宽度。
  • maximum-scale:是缩放比例的最大值
  • inital-scale:是缩放的初始化
  • user-scalable:值为布尔值,决定用户是否可以进行缩放操作。

实现响应式布局的方式有如下:

  • 像素单位使用相对单位:

    • 百分比

      heightwidth属性的百分比依托于父元素宽高,但是其他属性则不完全依赖父元素

      子元素的top/leftbottom/right如果设置百分比,则相对于最近的定位元素(position非static)的宽高

      子元素的内外边距(padding和margin)如果设置百分比,不论是垂直方向或者是水平方向,都相对于直接父元素width,而与父元素的height无关。

    • vw/vh

      有文章介绍,不赘述

    • rem

      有文章介绍,不赘述

  • 媒体查询

    媒体查询是css3中引入的功能,作用类似js中的if-else语句,通过媒体查询,可以通过给不同分辨率的设备编写不同的样式来实现响应式的布局。即便我们使用了相对单位,也只能解决同一类型设备的响应式问题,比如在PC端设计的网页,即便使用了相对单位,在移动端展示的效果也是宽大于高的矩形,展示效果不佳,此时就需要借助媒体查询来解决这个问题。

    基本语法

    1
    2
    3
    @media [not|only] media-type and (media-feature) {
    /* CSS样式规则 */
    }
    • @media:用于定义一个媒体查询。
    • [not|only]:可选关键字,not排除特定条件,only用来指定仅在满足条件时应用样式(主要用于避免旧浏览器解析媒体查询)。
    • media-type:指定媒体类型,例如screen(屏幕)、print(打印机)等。如果省略,则默认为all,即适用于所有类型的媒体。
    • and:逻辑运算符,用来连接多个媒体特性条件。
    • (media-feature):媒体特性条件表达式,例如(max-width: 600px)

    示例

    根据屏幕大小确定字体大小

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /* 当屏幕宽度小于等于600px时 */
    @media screen and (max-width: 600px) {
    body {
    font-size: 18px;
    }
    //后续还可以写更多选择器
    }
    /* 当屏幕宽度大于等于768px且小于1200px时 */
    @media screen and (min-width: 768px) and (max-width: 1199px) {
    body {
    font-size: 20px;
    }
    }
    /* 当屏幕宽度大于等于1200px时 */
    @media screen and (min-width: 1200px) {
    body {
    font-size: 24px;
    }
    }

    根据屏幕方向调整布局

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /* 适用于竖屏设备 */
    @media screen and (orientation: portrait) {
    .container {
    flex-direction: column;
    }
    }

    /* 适用于横屏设备 */
    @media screen and (orientation: landscape) {
    .container {
    flex-direction: row;
    }
    }