全文主要参考如下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:none
与visibility: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-direction
,flex-wrap
,flex-flow
,justify-content
,align-items
,align-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,将占满整个容器的高度。
- flex-start:交叉轴的
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-grow
和flex-shrink
生效前的尺寸。- auto: 默认值,这意味着项目将根据其内容确定其
初始大小
,如设置了width
则元素尺寸由width/height
决定 - 固定值:你可以指定一个
具体的长度单位
,如像素 (px
)、百分比 (%
) 等。这会设置项目的初始大小
,不考虑内容的自然尺寸。 - 0:完全依赖于
flex-grow
来分配多余的空间。这种方式常用于创建等分布局,如果令一个元素的flex-basis:0
,由于flex-grow
默认为0
,此时无论是否指定宽度,在主轴上的宽度都为0
。
- auto: 默认值,这意味着项目将根据其内容确定其
flex
flex
属性是flex-grow
,flex-shrink
和flex-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 | <div class="container"> |
上述代码实例中,.container
元素就是网格布局容器,.item
元素就是网格的项目,由于网格元素只能是容器的顶层子元素
,所以p
元素并不是网格元素
同样,Grid
布局属性可以分为两大类:
- 容器属性
- 项目属性
容器属性
display
- display:grid 则该容器是一个
块级元素
- display: inline-grid 则容器元素为
行内元素
- display: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/3 和 2/3
grid-template-columns: 200px 1fr 2frminmax:产生一个长度范围,表示长度就在这个范围之中都可以应用到网格项目中。第一个参数就是最小值,第二个参数就是最大值
minmax(100px, 1fr)
表示列宽不小于(>=)
100px,不大于(<=)
1fr1
grid-template-columns:repeat(auto-fill,minmax(260px,1fr))
auto:由浏览器自己决定长度
1
2//表示第一第三列为 100px,中间由浏览器决定长度
grid-template-columns: 100px auto 100px
- 第一个参数是重复的
gap
定义宫格之间的
间隔
容器成员属性
CSS如何画一个三角形
可以通过指定边框(border)
样式实现。
1 | .border { |
这样一个盒子的样式如下:
当盒子的宽高
不断减小,那每个边框是不是就会变成一个三角形呢?
这样会得到四个三角形,但是我们只要一个,我们可以把其他边框的颜色变透明
,但是这样隐藏的部分仍然占据部分高度
,
比如我们只要下面的三角形,那我们就可以让上边框的的宽度变为0
,效果就是这样。
然后再让左右边框变为透明的就好了。
1 | .border { |
如何实现元素水平居中,垂直居中
水平居中
行内元素/行内块元素
给父元素添加
text-align:center
给父元素添加
flex
布局1
2display: flex;
justify-content: center;给自身添加
绝对定位
1
2
3position: absolute;
left: 50%;
transform: translateX(-50%);
块级元素
添加
margin: 0 auto
,要求块级元素的宽度确定
给父元素添加
flex
布局1
2display: flex;
justify-content: center;给自身添加绝对定位
1
2
3position: absolute;
left: 50%;
transform: translateX(-50%);
垂直居中
行内元素
指定行内元素的行高
line-height
为父元素高度。给父元素添加
flex
布局1
2display: flex;
align-items: center;给自身添加绝对定位
1
2
3position: absolute;
top: 50%;
transform: translateY(-50%);
行内块元素/块级元素
给父元素添加
flex
布局1
2display: flex;
align-items: center;给自身添加绝对定位
1
2
3position: absolute;
top: 50%;
transform: translateY(-50%);
总结
绝对定位
和flex布局
是万能的。margin:0 auto
,text-align:center
,line-height:父元素高度
,是一些特殊情况。
说说对CSS预处理语言的理解?有哪些区别?
背景
CSS代码看起来是
没有逻辑性
的,不方便维护及扩展
,不利于复用
。css预处理语言
在css的基础上,添加了变量
,混入
,嵌套
等功能,让css代码看起来更有逻辑性
,更容易维护和复用。css预处理语言包括一套
自定义的语法
和一个解析器
,解析器会把用自定义语法编写的代码转化成css代码
。
常见的css预编译语言
sass
2007 年诞生,最早也是最成熟的
Css
预处理器,拥有 Ruby 社区的支持和Compass
这一最强大的Css
框架,目前受LESS
影响,已经进化到了全面兼容Css
的Scss
sass和scss的区别与联系
- 是同一种css预处理语言(sass)的两种
不同语法形式
,它们都扩展了标准的 CSS,虽然有不同的自定义语法
,但是使用同一个解析器
(编译器)来处理 - sass使用
缩进
来表示代码块,不使用大括号{}
和分号;
更接近 Python 的风格,强调简洁性和可读性。文件使用.sass
扩展名 - scss与 CSS 的语法几乎完全相同,所以更容易被现有开发者接受和使用。文件使用
.scss
扩展名
- 是同一种css预处理语言(sass)的两种
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
引入scss
、less
二者的使用方法都如下所示,这样的导入语法不能直接在标准的 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:hidden
和text-overflow:ellipsis
生效的基础
text-overflow
属性值有如下:
clip
:当对象内文本溢出部分裁切
掉ellipsis
:当对象内文本溢出时显示省略
标记(…)
text-overflow
只有在设置了overflow:hidden
和white-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
11function 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
,浏览器会尽快获取引用的资源,并将其存储在浏览器缓存中(它不会执行脚本或应用样式表,单纯只是下载),以便在后续代码中引用时可以更快地使用它们。为了让用户体验尽可能流畅,我们应提前加载页面加载初期用户会遇到的高优先级资源。
资源压缩
利用webpack
、gulp/grunt
、rollup
等模块化工具,将css
代码进行压缩,使文件变小,大大降低了浏览器的加载时间
合理使用css选择器
css
匹配的规则是从右往左
开始匹配,例如#markdown .content h3
匹配规则如下:
- 先找到h3标签元素
- 然后去除祖先不是.content的元素
- 最后去除祖先不是#markdown的元素
如果嵌套的层级更多,页面中的元素更多,那么匹配
所要花费的时间代价自然更高。
所以最好不要嵌套使用过多复杂选择器。
不使用@import
css样式文件有两种引入方式,一种是link
元素,另一种是@import
使用 @import
规则引入的 CSS 文件是串行
加载的
1 | /* styles.css */ |
在这个例子中,reset.css
、layout.css
和 theme.css
是按顺序加载的,只有当前一个文件加载完成后才会开始加载下一个。
1 | <head> |
尽管 <link>
标签默认会阻塞页面渲染
,但它允许浏览器并行加载
多个 CSS 文件。这意味着虽然整个页面的渲染仍然会被延迟,但单个文件的加载不会影响其他文件的下载直到所有这些文件都解析完毕。
怎么理解回流跟重绘?什么场景下会触发?
是什么
在HTML
中,每个元素都会被表示成一个盒子,在浏览器解析过程中,会涉及到回流
与重绘
:
- 回流:布局引擎会根据各种样式计算每个盒子在页面上的
大小
与位置
,简单的来说,回流的作用就是布局元素,即确定元素的位置和大小 - 重绘:当计算好盒模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行
绘制
,简单的来说,就是更为细致的渲染盒子。
具体的浏览器解析渲染机制如下所示:
- 解析
HTML
,生成DOM树
,解析CSS
,生成CSSOM树
- 将DOM树和CSSOM树
结合
,生成渲染树(Render Tree) - Layout(回流): 根据生成的
渲染树
,进行回流(Layout)
,得到节点的几何信息(位置,大小) - Painting(重绘):根据
渲染树
以及回流得到的几何信息
,得到节点的绝对像素
- Display:将像素发送给GPU,展示在页面上
在页面初始渲染阶段,回流
不可避免的触发,可以理解成页面一开始是空白的元素,后面添加了新的元素使页面布局
发生改变
当我们对 DOM
的修改引发了 DOM几何尺寸
的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性,然后再将计算的结果绘制出来
当我们对 DOM
的修改导致了样式的变化(color
或background-color
),却并未影响其几何属性时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式,这里就仅仅触发了重绘。
如何触发回流
那么当页面布局
或者几何信息
发生变化的时候,就需要回流
,如下面情况:
- 添加或删除
可见的
DOM元素 - 元素的
位置
发生变化 - 元素的
大小
发生变化(包括margin、padding、border、width,height等,即盒模型
的四部分) 内容
发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代- 页面一开始渲染的时候(这避免不了)
- 浏览器的
窗口尺寸
变化(因为回流是根据视口的大小来计算元素的位置和大小的,这个依据变了,就难免要重新计算) - 获取元素
特定的属性
,比如client,scroll,offset
开头的布局属性
,这些属性有一个共性,就是需要通过即时计算
得到。因此浏览器为了获取这些值,也会进行回流。
如何触发重绘
触发回流一定会触发重绘
除此之外还有一些其他引起重绘行为:
- 颜色的修改
- 文本方向的修改
- 阴影的修改
浏览器的优化机制
由于每次重排都会造成额外的计算消耗,因此大多数浏览器都会通过队列化
修改并批量执行
来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列
当你获取布局信息的操作的时候,会强制队列刷新
,包括前面讲到的offsetTop
等方法都会返回最新的数据
因此浏览器不得不立即清空队列,触发回流重绘
来返回正确的值。
css选择器有哪些?优先级?哪些属性可以继承?
CSS3新增了那些特性
是什么
css
,即层叠样式表(Cascading Style Sheets)的简称,是一种标记语言,由浏览器解释执行用来使页面变得更美观
css3
是css
这种标记语言的最新标准,是向后兼容的,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 | @keyframes rotate{ |
from
表示最开始的那一帧,to
表示结束时的那一帧。rotate
表示自定义的动画名。
也可以使用百分比
刻画生命周期:
1 | @keyframes rotate{ |
使用动画
定义好动画,我们就可以直接使用了,使用动画涉及到了许多属性,记忆起来较为困难。
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 指定动画播放的方向 | 默认是 normal | normal、reverse、alternate、alternate-reverse |
animation-fill-mode | 定义动画在播放之前或之后如何影响元素的样式。默认是 none | forwards、backwards、both |
animation-play-state | 指定动画播放状态,正在运行或暂停。默认是 running | running、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:值为布尔值,决定用户是否可以进行缩放操作。
实现响应式布局
的方式有如下:
像素单位使用相对单位:
百分比
height
、width
属性的百分比依托于父元素
的宽高
,但是其他属性则不完全依赖父元素
子元素的
top/left
和bottom/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;
}
}