Hexo


  • Startseite

  • Archiv

浏览器缓存

Veröffentlicht am 2019-01-22

web缓存的种类有很多种,比如数据库缓存、CDN缓存、代理服务器缓存、浏览器缓存等,这里我们主要了解一下浏览器的缓存机制。

浏览器端的缓存规则

对于浏览器端的缓存来讲,这些规则是在HTTP协议头和HTML页面的Meta标签中定义的。他们分别从新鲜度和校验值两个维度来规定浏览器是否可以直接使用缓存中的副本,还是需要去源服务器获取更新的版本。

  • 新鲜度(过期机制):也就是缓存副本有效期。一个缓存副本必须满足以下条件,浏览器会认为它是有效的,足够新的:含有完整的过期时间控制头信息(HTTP协议报头),并且仍在有效期内;
    浏览器已经使用过这个缓存副本,并且在一个会话中已经检查过新鲜度;
    满足以上两个情况的一种,浏览器会直接从缓存中获取副本并渲染。

  • 校验值(验证机制):服务器返回资源的时候有时在控制头信息带上这个资源的实体标签Etag(Entity Tag),它可以用来作为浏览器再次请求过程的校验标识。如过发现校验标识不匹配,说明资源已经被修改或过期,浏览器需求重新获取资源内容。

图示:

两个概念

  • 强缓存:(Chrome下的现象是 200 OK (from disk cache) 或者 200 OK (from memory cache))
    用户发送的请求,直接从客户端缓存中获取,不发送请求到服务器,不与服务器发生交互行为。

  • 协商缓存:(也就是我们常见的304状态码)
    用户发送的请求,发送到服务器后,由服务器判定是否从缓存中获取资源。

  • 两者共同点:客户端获得的数据最后都是从客户端缓存中获得。

  • 两者的区别:从名字就可以看出,强缓存不与服务器交互,而协商缓存则需要与服务器交互。

对上诉步骤进行一些解说

步骤b:缓存是否过期

两个参数:expires 、Cache-Control

当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires

  • expires

Http1.0 中的标准,表明过期时间,注意此处的时间都是指的是服务器的时间。

存在的问题:服务器时间与客户端时间的不一致,就会导致缓存跟期待效果出现偏差。

  • Cache-Control

Http1.1 中的标准,可以看成是 expires 的补充。使用的是相对时间的概念。

Cache-Control的属性设置

参数 解析
max-age 设置缓存的最大的有效时间,单位为秒(s)。max-age会覆盖掉Expires。>0读取缓存,<=0向server 发送http 请求确认 ,该资源是否有修改,有的话返回200 ,无的话返回304.
s-maxage 只用于共享缓存,比如CDN缓存(s -> share)。与max-age的区别是:max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage,则会覆盖max-age 和 Expires.
public 响应会被缓存,并且在多用户间共享。默认是public。
private 响应只作为私有的缓存,不能在用户间共享。如果要求HTTP认证,响应会自动设置为private
no-cache 指定不缓存响应,表明资源不进行缓存。但是设置了no-cache之后并不代表浏览器不缓存,而是在缓存前要向服务器确认资源是否被更改。因此有的时候只设置no-cache防止缓存还是不够保险,还可以加上private指令,将过期时间设为过去的时间。
no-store 绝对禁止缓存
must-revalidate 如果页面过期,则去服务器进行获取

图示:

所以判断缓存是否过期步骤是:

1) 查看是否有cache-control 的max-age / s-maxage , 如果有,则用服务器时间date值 + max-age/s-maxage 的秒数计算出新的过期时间,将当前时间与过期时间进行比较,判断是否过期

2)查看是否有cache-control 的max-age / s-maxage,则用expires 作为过期时间比较

图示:

步骤c:跟服务器协商是否使用缓存

到这一步的时候,浏览器会向服务器发送请求,同时如果上一次的缓存中有Last-modified 和 Etag 字段,
浏览器将在request header 中加入If-Modified-Since(对应于Last-modified), 和If-None-Match(对应于Etag)。

  • Last-modified: 表明请求的资源上次的修改时间。
  • If-Modified-Since:客户端保留的资源上次的修改时间。
  • Etag:资源的内容标识。(不唯一,通常为文件的md5或者一段hash值,只要保证写入和验证时的方法一致即可)
  • If-None-Match: 客户端保留的资源内容标识。

步骤d:协商缓存

在这个阶段,服务器一般会将Cache-control、expires 、last-modified、date、etag 等字段在response header 中返回,便于下次缓存。当然具体的场景,也是看服务器的约定规则设定。

缓存的不同来源

from disk cache

从磁盘中获取缓存资源,等待下次访问时不需要重新下载资源,而直接从磁盘中获取。它的直接操作对象为CurlCacheManager。

一般非脚本会存在内存当中,如css等

from memory cache

从内存中获取资源,等待下次访问时不需要重新下载资源,而直接从内存中获取。Webkit早已支持memoryCache。
目前Webkit资源分成两类,一类是主资源,比如HTML页面,或者下载项,一类是派生资源,比如HTML页面中内嵌的图片或者脚本链接,分别对应代码中两个类:MainResourceLoader和SubresourceLoader。虽然Webkit支持memoryCache,但是也只是针对派生资源,它对应的类为CachedResource,用于保存原始数据(比如CSS,JS等),以及解码过的图片数据。

一般脚本、字体、图片会存在内存当中。

区别

当退出进程时,内存中的数据会被清空,而磁盘的数据不会,所以,当下次再进入该进程时,该进程仍可以从diskCache中获得数据,而memoryCache则不行。

相似

diskCache与memoryCache相似之处就是也只能存储一些派生类资源文件。它的存储形式为一个index.dat文件,记录存储数据的url,然后再分别存储该url的response信息和content内容。Response信息最大作用就是用于判断服务器上该url的content内容是否被修改。

图:

用户行为的缓存变化

f5刷新(mac 即command + r)

没啥变化

强制刷新(command+shift+r)

在request中多了个属性:
都有Cache-Control: no-cache的标识。
这表明每次都需要服务器评估是否有效,不要理解为直接不使用缓存。
此外可以注意到request中没有可以匹配response中ETag的If-None-Match属性,
所以会重新加载。

304->200

看图很清晰:

扩展阅读

link1

link2

TortoiseGit使用小记

Veröffentlicht am 2018-10-12

这是一篇多图博客,便捷插入图片分享

TortoiseGit简介

tortoiseGit是一个开放的git版本控制系统的源客户端,该软件功能和git一样。
不同的是:git是命令行操作模式,tortoiseGit界面化操作模式。
下载

安装步骤

1、下载所需版本,需要汉化的顺便下载第二个箭头指向的chinese,xxx,英语66的请忽略~~
点击安装,一直下一步即可,途中可能有那些配置什么什么的,先忽略,后面再配置

语言包直接点击打开安装即可,然后就可以选择中文版了~

可能会遇到git版本过低,重新安装覆盖即可

到这里安装好了~接下来配置

2、配置秘钥

下载 PuttyGen:是一套可以产生密钥的工具;可以生成RSA或DSA密钥;用于Putty、Plink、PSFP、PSCP和Pageant.

  • 点击Generate生成密钥:
    注意:生成时鼠标要不停划过进度条,不然进度条会一直不动!

  • 先点击Save private key把私有的密钥存起来,记住存储的位置,后面会用到

  • 把生成出来的public Key复制粘贴到Gitlab上面,配置SSH key

  • 打开:开始–>TortoiseGit–>Pageant,打开以后右下角会有图标,双击点开蓝屏幕电脑那个图标
    说明:使用TortoiseGit进行和远端输出项目时,Pageant必须启动且添加了对应的私钥。否则会报错

到这里已经可以使用了~

  • Pageant在git中主要负责和服务器端进行身份验证,但是每次在启动Pageant后都需要手动的加载秘钥文件,为了避免每次使用都要重复添加私钥,可以设置一下自动加载。

(1)Pageant 的开机自启
首先找到TortoiseGit 的安装目录的bin目录,然后找到pageant.exe 然后单击右键 创建快捷方式,此时bin目录中有 两个pageant.exe 文件

win10添加自启项:win+r => 输入:shell:startup => 将自启项的快捷方式丢进去即可

win10查看自启项:win+r => 输入:msconfig => 启动-打开任务管理器-启动。
即可查看是否添加成功

(2)关联加载秘钥ppk
在上面的开机自启文件夹下 选中pageant 的快捷方式 查看属性,在目标的一栏,前面是 pageant 程序的位置 ,在后面空格 然后粘贴ppk秘钥的路径(前面保存过的),然后点击确定就搞定了

使用

大致功能如图:(每台电脑貌似会存在差异,)

克隆

空白文件夹右键 - Git克隆:
填入仓库地址

分支操作

右键-切换切出-创建分支
右键-创建分支

提交

。。。。还有很多功能呀,自己慢慢摸索~~~

快应用笔记之组件通信

Veröffentlicht am 2018-09-10

一、父传子

props

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 父组件
<import name="comp-part1" src="../Common/part"></import>
<template>
<div class="tutorial-page">
<text class="tutorial-title">页面父组件:</text>
<text>{{ data1 }}</text>
<text>{{ data2.name }}</text>

<!-- 子组件参数传递 -->
<comp-part1 prop1="{{data1}}" prop2-object="{{data2}}"></comp-part1>

</div>

</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding: 20px 10px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 父组件
export default {
private: {
data1: 'string',
data2: {
name: 'object'
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 子组件
<template>
<div class="tutorial-page">
<text class="tutorial-title">子组件:</text>
<text>{{ prop1 }}</text>
<text>{{ prop2Object.name }}</text>
</div>
</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding-top: 20px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 子组件
export default {
props: [
'prop1',
'prop2Object'
],
data: {},
onInit () {
console.info(`父组件传递下来的值:`, this.prop1, this.prop2Object)
}
}
</script>

二、双向的事件传递

父子组件之间的事件触发:$broadcast()、$dispatch()、子组件$emit()节点绑定的事件

1.向下传递:父组件触发,子组件响应;调用parentVm.$broadcast()完成向下传递,子组件 $on 响应,如:evtType1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 父组件
<import name="comp-part1" src="../Common/part">
</import>
<template>
<div class="tutorial-page">
<text class="tutorial-title">页面父组件:</text>
<text onclick="evtType1Emit">触发$broadcast(),向下传递</text>
<!-- 子组件 -->
<comp-part1></comp-part1>
</div>
</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding: 20px 10px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 父组件
export default {
data: {},
//触发$broadcast()
evtType1Emit () {
this.$broadcast('evtType1', { params: '额外参数', channel: 'test'})
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 子组件
<template>
<div class="tutorial-page">
<text class="tutorial-title">子组件:</text>
<text>1、父组件触发子组件响应,子组件接收的参数 e.detail:</text>
<text>{{ parentDate }}</text>
<text>2、e.type:</text>
<text>{{ Etype }}</text>
</div>
</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding-top: 20px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 子组件
export default {
data: {
parentDate: '',
Etype: ''
},
onInit() {
// 绑定VM的自定义事件
//evtType1 要跟父组件 $broadcast 的对应一致
//responseEvent 子组件响应以后的操作
this.$on('evtType1', this.responseEvent )
},
responseEvent (e) {
console.info('子组件evt', e)
console.info(`子组件:事件响应: `, e.type, e.detail)
console.info(`evt.detail.channel: `, e.detail.channel)
this.parentDate = JSON.stringify(e.detail);
this.Etype = JSON.stringify(e.type);
// 结束事件传递
// evt.stop()
}
}
</script>

2.向上传递:子组件触发,父组件响应;调用childVm.$dispath()完成向上传递,如:evtType2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 子组件
<template>
<div class="tutorial-page">
<text class="tutorial-title">子组件:</text>
<text onclick="evtType2Dispatch">$dispatch触发向上传递</text>
<text onclick="evtType2Emit">$emit触发向上传递</text>
</div>
</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding-top: 20px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 子组件
export default {
data: {},
onInit() {

},
evtType2Dispatch () {
this.$dispatch('evtType2', { params: '$dispatch', respose: '向上传递' })
},
evtType2Emit () {
this.$emit('evtType3', { params: '$emit', respose: '向上传递' })
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 父组件
<import name="comp-part1" src="../Common/part">
</import>
<template>
<div class="tutorial-page">
<text class="tutorial-title">页面父组件:</text>
<text>$dispatch: {{ childData }}</text>
<text>$emit: {{ childData2 }}</text>
<!-- 子组件 evt-type2 => evtType2 -->
<comp-part1 onevt-type2="parentResponse" onevt-type3="parentResponse3"></comp-part1>
</div>

</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding: 20px 10px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 父组件
export default {
data: {
childData: '',
childData2: ''
},
parentResponse(e) {
console.log(`父组件事件响应1: `, e);
console.info(`父组件:事件响应: `, e.type, e.detail)
this.childData = JSON.stringify(e.detail)
// 结束事件传递
// evt.stop()
},
parentResponse3(e) {
this.childData2 = JSON.stringify(e.detail)
}
}
</script>

三、兄弟组件通信

传统的兄弟等非父子组件之间通信,是通过Publish/Subscribe模型来完成。
开发者如果想要使用这样的能力,当然可以自己写一个Pub/Sub模型实现通信解耦;当然在业务逻辑相对简单的情况下,也可以使用ViewModel本身的事件绑定来处理:$on(),$emit()。

子组件定义了Sub端的逻辑处理,有processMessage()、customEventInVm2(),后者同使用$on效果一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 组件2 part2
<template>
<div class="tutorial-page">
<text class="tutorial-title">自定义组件2:</text>
<text>处理消息:{{msg}}</text>
<text>事件内容:{{eventDetail}}</text>
</div>
</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding: 20px 10px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 子组件: part2
export default {
props: [],
data() {
return {
msg: null,
eventDetail: null
}
},
processMessage(msg) {
const now = (new Date).toISOString()
this.msg = `${now}: ${msg}`
},
/**
* 通过events对象:绑定事件
*/
events: {
customEventInVm2(evt) {
const now = (new Date).toISOString()
this.eventDetail = `${now}: ${evt.detail}`
}
}
}
</script>

另外一个兄弟组件可以通过父组件中建立相互引用达到相互持有ViewModel的目的,通过在生命周期onReady()中执行establishRef()实现,如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 父组件
<import name="comp-part2" src="../Common/part2"></import>
<import name="comp-part3" src="../Common/part3"></import>

<template>
<div class="tutorial-page">
<!-- 兄弟VM通信 -->
<comp-part2 id="sibling2"></comp-part2>
<comp-part3 id="sibling3"></comp-part3>

<text @click="pppp">父组件调用子组件方法</text>
</div>
</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding: 20px 10px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 父组件
export default {
onReady() {
this.establishRef()
},
/**
* 建立相互VM的引用
*/
establishRef() {
const siblingVm2 = this.$vm('sibling2')
const siblingVm3 = this.$vm('sibling3')
console.info(siblingVm2)
console.info(siblingVm3)
siblingVm2.parentVm = this
siblingVm2.part2 = siblingVm3 //将组件3整个赋值给组件2的part2属性,打印可以看到part2属性内部就拥有组件3定义的一些方法
siblingVm3.parentVm = this
siblingVm3.part3 = siblingVm2 //将组件2整个赋值给组件3的part3属性
},
pppp() {
this.$vm('sibling2').processMessage();
}
}
</script>

this.$vm(id)可以获取到组件的属性跟方法

那么另外一个子组件的Pub端定义就很简单了,执行sendMesssage()即可完成触发,如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 子组件 part3
<template>
<div class="tutorial-page">
<text class="tutorial-title">自定义组件3:</text>
<text onclick="sendMesssage">点击发送消息</text>
</div>
</template>

<style lang="less">
.tutorial-page {
flex-direction: column;
padding: 20px 10px;
.tutorial-title {
font-weight: bold;
}
}
</style>

<script>
// 子组件: part3
export default {
sendMesssage() {
// 父组件中把组件2赋值在 part3下
if (this.part3) {
// Way1. 调用方法
this.part3.processMessage('兄弟之间通信的消息内容')
// Way2. 触发事件
this.part3.$emit('customEventInVm2', '兄弟之间通信的消息内容')
}
},

}
</script>

css变量

Veröffentlicht am 2018-08-19

基本使用

CSS中原生的变量定义语法是:– ,变量使用语法是:var(– ` ),其中*表示我们的变量名称。变量名称没有限制(不能包含$,[,^,(,%等字符)。

  • 声明一个局部变量:
1
2
3
4
5
6
7
element {
--main-bg-color: brown;
--1: #fff;
--红色: red;
--붉은: green;
--_color1: green;
}
  • 声明一个 全局 CSS 变量:
1
2
3
4
:root {
--global-color: #666;
--pane-padding: 5px 42px;
}

:root 这个 CSS 伪类匹配文档树的根元素。对于 HTML 来说,:root 表示 元素,除了优先级更高之外,与 html 选择器相同。

  • 使用
1
2
3
element {
background-color: var(--main-bg-color);
}

作用域

  • 同一个 CSS变量,可以在多个选择器内声明。读取的时候,优先级最高的声明生效。这与 CSS 的”层叠”规则是一致的。

  • CSS 变量是支持继承的

  • 通俗一点就是局部变量会在作用范围内覆盖全局变量。

  • CSS变量并不支持 !important 声明。

1
2
3
4
5
6
7
8
9
10
11
:root { --color: blue; }
div { --color: pink; }
#purple { --color: purple; }
* { color: var(--color); }

<p>蓝色继承于根元素</p>
<div>粉色来自直接设置</div>
<div id='purple'>
ID选择器权重更高,紫色
<p>继承了紫色</p>
</div>

效果:

蓝色继承于根元素 粉色来自直接设置 ID选择器权重更高,紫色 继承了紫色

总结:

  • 变量的作用域就是它所在的选择器的有效范围。
  • 覆盖规则由CSS选择器的权重决定

CSS变量完整语法

var( [, ]? ),用中文表示就是:var( <自定义属性名> [, <默认值 ]? ),

1、变量未定义

1
2
3
4
5
p {
color: var(--color, red)
}

<p>我是默认值颜色:红色</p>

效果:变量未定义,我是默认值颜色:红色

2、CSS变量不合法的缺省特性

1
2
3
4
5
6
7
8
9
body{
color: blue;
}
p {
--color: 20px;
color: var(--color, red)
}

<p>变量不合法,颜色为蓝色</p>

效果:变量不合法,颜色为蓝色

变量类型

1、CSS变量值是数值,不能与数值单位直接连用。

1
2
3
4
p {
--size: 20;
font-size: var(--size)px;
}

此处font-size:var(–size)px等同于font-size:20 px,注意,20后面有个空格。

或者用css3的calc:

1
font-size: calc(var(--size) * 1px);

2、css变量为字符串的组合使用

1
2
3
4
5
6
7
8
9
:root{
--word:"this";
--word-second:"is";
--word-third:"CSS Variable";
}

div::before{
content:var(--word)' 'var(--word-second)' 'var(--word-third);
}

戳

响应式布局

可以在响应式布局的media命令里面声明变量,使得不同的屏幕宽度有不同的变量值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
css:

:root{
--columns: 4;
--margins: calc(24px / var(--columns));
--space: calc(4px * var(--columns));
--fontSize: calc(20px - 4 / var(--columns));
}
.box{
background-color: aquamarine;
width: 50%;
min-width: 320px;
margin: auto;
overflow: hidden;
}
.cell{
width: calc((100% - var(--margins) * var(--columns) * 2) / var(--columns));
margin: var(--margins);
background-color: #f0f3f9;
float: left;
}
main {
height: 150px;
padding: var(--space);
font-size: var(--fontSize);
}
@media screen and (max-width: 1200px) {
.box {
--columns: 3;
}
}

@media screen and (max-width: 900px) {
.box {
--columns: 2;
}
}

@media screen and (max-width: 600px) {
.box {
--columns: 1;
}
}

html:

<div class="box">
<div class="cell">
<main>内容</main>
</div>
<div class="cell">
<main>内容</main>
</div>
<div class="cell">
<main>内容</main>
</div>
<div class="cell">
<main>内容</main>
</div>
<div class="cell">
<main>内容</main>
</div>
<div class="cell">
<main>内容</main>
</div>
</div>

戳

JavaScript 操作

JavaScript 也可以检测浏览器是否支持 CSS 变量。

1
2
3
4
5
6
7
8
9
10
const isSupported =
window.CSS &&
window.CSS.supports &&
window.CSS.supports('--a', 0);

if (isSupported) {
/* supported */
} else {
/* not supported */
}
1
2
3
4
5
6
7
8
9
10
// 设置变量
document.body.style.setProperty('--primary', '#7F583F');

// 读取变量
const rootStyles = getComputedStyle(document.documentElement);
const varValue = String(rootStyles.getPropertyValue('--primary')).trim();
// '#7F583F'

// 删除变量
document.body.style.removeProperty('--primary');

JavaScript 可以将任意值存入样式表

1
2
3
4
5
6
const docStyle = document.documentElement.style;

document.addEventListener('mousemove', (e) => {
docStyle.setProperty('--mouse-x', e.clientX);
docStyle.setProperty('--mouse-y', e.clientY);
});

getComputedStyle方法获取的是最终应用在元素上的所有CSS属性对象(即使没有CSS代码,也会把默认的祖宗八代都显示出来),只读

兼容性

image

兼容性处理

对于不支持 CSS 变量的浏览器,可以采用下面的写法。

1
2
3
4
a {
color: #7F583F;
color: var(--primary);
}

也可以使用@support命令进行检测。

1
2
3
4
5
6
7
@supports ( (--a: 0)) {
/* supported */
}

@supports ( not (--a: 0)) {
/* not supported */
}

与传统 LESS 、SASS 等预处理器变量比较

1、CSS 变量的动态性,能在页面运行时更改,而传统预处理器变量编译后无法更改

2、CSS 变量能够继承,能够组合使用,具有作用域

3、配合 Javascript 使用,可以方便的从 JS 中读/写

有趣的用法

。。。

参考链接

浏览器对象模型 (BOM)

Veröffentlicht am 2018-07-21

浏览器对象模型(Browser Object Model,BOM):浏览器为js提供的对象集合。

window

window对象表示浏览器中打开的窗口。windows对象是个全局对象,他不需要使用名称访问自己的属性和方法。

1
2
3
4
//下面的代码本质是相同的
alert("hello!");

window.alert("hello!");

如果文档包含框架(< frame > 或 < iframe > 标签),浏览器会为 HTML 文档创建一个 window 对象,并为每个框架创建一个额外的 window 对象。

window 尺寸

  • window.innerHeight - 浏览器窗口的内部高度
  • window.innerWidth - 浏览器窗口的内部宽度

内部宽高度:除去菜单栏、工具栏、边框等占位元素后,用于显示网页的净宽高。

对于 Internet Explorer 8、7、6、5:

  • document.documentElement.clientHeight / document.body.clientHeight
  • document.documentElement.clientWidth / document.body.clientWidth
1
2
3
var w=window.innerWidth || document.documentElement.clientWidth ||document.body.clientWidth;

var h=window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;

其他 Window 方法

  • window.open() - 打开新窗口
  • window.close() - 关闭当前窗口
  • window.moveTo() - 移动当前窗口
  • window.resizeTo() - 调整当前窗口的尺寸

对窗口操作

窗口引用

假设在一个网页中使用iframe或者frame标签, 那么在这个网页中就会有多个窗口

  • window对象提供了window.top, window.parent, window.self三个属性表示顶层窗口, 父窗口和当前窗口. top, parent, self是对象, 也可以单独使用
  • 我们的a标签, form标签等有个target属性, 表示在哪一个窗口打开, 这个属性有三个属性值, 分别于top, parent, self对象对应, 分别是_top, _parent, _self属性值.
1
<a href="/xxx' target="_parent">在父窗口中打开</a>

iframe标签

这个标签可以在网页中嵌入一个窗口

  • iframe标签遵守同源策略, 若父窗口和ifrmae来自不同域名则不能使用脚本直接通信, 要使用window.postMessage方法通信
  • iframe嵌入窗口的window对象,有一个frameElement属性,返回它在父窗口中的DOM节点。对于那么非嵌入的窗口,该属性等于null

navigator

navigator对象表示浏览器的信息,最常用的属性包括:

  • navigator.appName:浏览器名称;
  • navigator.appVersion:浏览器版本;
  • navigator.language:浏览器设置的语言;
  • navigator.platform:操作系统类型;
  • navigator.userAgent:浏览器设定的User-Agent字符串。

screen

screen对象表示屏幕的信息,常用的属性有:

  • screen.width:屏幕宽度,以像素为单位;
  • screen.height:屏幕高度,以像素为单位;
  • screen.colorDepth:返回颜色位数,如8、16、24。

location

location对象表示当前页面的URL信息。location 对象是 Window 对象的一个部分,可通过 window.location 属性来访问。

可以用location.href获取URL各个部分的值:

1
2
3
4
5
6
7
8
//URL: https://zxcs.linghit.com/mllyuncheng/index.html?channel=zxcs&lap=0#email

location.protocol; // 'https'
location.host; // 'zxcs.linghit.com'
location.port; // 设置或返回当前 URL 的端口号。
location.pathname; // '/mllyuncheng/index.html'
location.search; // '?channel=zxcs&lap=0'
location.hash; // '#email'

location 对象方法

  • assign() 加载新的文档。
  • reload() 重新加载当前文档。
  • replace() 用新的文档替换当前文档。

document

document对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。

document的title属性是从HTML文档中的< title >xxx</ title>读取的,但是可以动态改变:

1
document.title = '好好学习天天向上';

用document对象提供的getElementById()和getElementsByTagName()可以按ID获得一个DOM节点和按Tag名称获得一组DOM节点。

document对象还有一个cookie属性,可以获取当前页面的Cookie。

1
document.cookie;

由于JavaScript能读取到页面的Cookie,存在较大安全隐患,为了解决这个问题,服务器在设置Cookie时可以使用httpOnly,设定了httpOnly的Cookie将不能被JavaScript读取。这个行为由浏览器实现,主流浏览器均支持httpOnly选项,IE从IE6 SP1开始支持。

history

history对象保存了浏览器的历史记录,JavaScript可以调用history对象的back()或forward (),相当于用户点击了浏览器的“后退”或“前进”按钮。

  • back() 加载 history 列表中的前一个 URL。
  • forward() 加载 history 列表中的下一个 URL。
  • go() 加载 history 列表中的某个具体页面。
1
2
3
4
5
6
7
history.go(-1);     //后退1个页面
history.back();

history.go(1); //前进1个页面
history.forward();

history.go(-2); //后退2个页面

这个对象属于历史遗留对象,对于现代Web页面来说,由于大量使用AJAX和页面交互,简单粗暴地调用history.back()可能会让用户感到非常愤怒。任何情况,你都不应该使用history这个对象了。

localStorage、sessionStorage、Cookie

Veröffentlicht am 2018-06-18

localStorage

localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。存放数据大小为一般为5MB,而且它仅在客户端(即浏览器)中保存,不参与和服务器的通信。

sessionStorage

sessionStorage仅在当前会话下有效,关闭页面或浏览器后被清除。存放数据大小为一般为5MB,而且它仅在客户端(即浏览器)中保存,不参与和服务器的通信。源生接口可以接受,亦可再次封装来对Object和Array有更好的支持。

cookie

cookie非常小,它的大小限制为4KB左右。有个数限制(各浏览器不同),一般不能超过20个。与服务器端通信:每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题。Cookie使用需要程序员自己封装。

localStorage和sessionStorage使用时使用相同的API:

1
2
3
4
5
6
7
localStorage.setItem("key","value");//以“key”为名称存储一个值“value”

localStorage.getItem("key");//获取名称为“key”的值

localStorage.removeItem("key");//删除名称为“key”的信息。

localStorage.clear(); //清空localStorage中所有信息

localStorage其他注意事项

一般我们会将JSON存入localStorage中,但是在localStorage会自动将localStorage转换成为字符串形式.
可以使用JSON.stringify()这个方法,来将JSON转换成为JSON字符串;
读取之后要将JSON字符串转换成为JSON对象,使用JSON.parse()方法

三者的异同

特性 Cookie localStorage sessionStorage
数据的生命期 可设置失效时间,默认是关闭浏览器后失效 除非被清除,否则永久保存 仅在当前会话下有效,关闭页面或浏览器后被清除
存放数据大小 4K左右 一般为5MB 一般为5MB
与服务器端通信 每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题 仅在客户端(即浏览器)中保存,不参与和服务器的通信 仅在客户端(即浏览器)中保存,不参与和服务器的通信
易用性 需要程序员自己封装,源生的Cookie接口不友好 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持 源生接口可以接受,亦可再次封装来对Object和Array有更好的支持

安全性

需要注意的是,不是什么数据都适合放在 Cookie、localStorage 和 sessionStorage 中的。使用它们的时候,需要时刻注意是否有代码存在 XSS 注入的风险。因为只要打开控制台,你就随意修改它们的值,也就是说如果你的网站中有 XSS 的风险,它们就能对你的 localStorage 肆意妄为。所以千万不要用它们存储你系统中的敏感数据。

XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。

HTTP中Get与Post的区别

Veröffentlicht am 2018-05-26

什么是 HTTP?

超文本传输协议(HTTP)的设计目的是保证客户机与服务器之间的通信。
HTTP 的工作方式是客户机与服务器之间的请求-应答协议。
web 浏览器可能是客户端,而计算机上的网络应用程序也可能作为服务器端。
举例:客户端(浏览器)向服务器提交 HTTP 请求;服务器向客户端返回响应。响应包含关于请求的状态信息以及可能被请求的内容。

客户端对服务器的请求类型主要有四种:

  • GET(从服务器获取)
  • POST(向服务器发送请求数据)
  • PUT(更新)
  • DELETE(删除)

一般来讲,分别对应着查、增、更、删四种操作,不过实际上四种操作一般情况下通过GET和POST就可以完成了,所以日常使用最多的也都是这两个。

最常用的两种 HTTP 请求方法:GET 和 POST

  • GET - 从指定的资源请求数据。
  • POST - 向指定的资源提交要被处理的数据

GET方法

1
2
//查询字符串(名称/值对)是在 GET 请求的 URL 中发送的:
//test/demo_form.asp?name1=value1&name2=value2

GET 请求可被缓存
GET 请求保留在浏览器历史记录中
GET 请求可被收藏为书签
GET 请求不应在处理敏感数据时使用
GET 请求有长度限制(URL 的最大长度是 2048 个字符)
GET 请求只应当用于取回数据

POST 方法

1
2
3
4
//查询字符串(名称/值对)是在 POST 请求的 HTTP 消息主体中发送的:
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2

POST 请求不会被缓存
POST 请求不会保留在浏览器历史记录中
POST 不能被收藏为书签
POST 请求对数据长度没有要求

对比

GET POST
后退按钮/刷新 无害 数据会被重新提交(浏览器应该告知用户数据会被重新提交)。
书签 可收藏为书签 不可收藏为书签
缓存 能被缓存 不能缓存
编码类型 application/x-www-form-urlencoded application/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。
历史 参数保留在浏览器历史中 参数不会保存在浏览器历史中。
对数据长度的限制 是的。当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。 无限制。
对数据类型的限制 只允许 ASCII 字符。 没有限制。也允许二进制数据。
安全性 与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时绝不要使用 GET ! POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。
可见性 数据在 URL 中对所有人都是可见的。 数据不会显示在 URL 中。

本质区别

1、GET和POST本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。
2、GET产生一个TCP数据包;POST产生两个TCP数据包。

  • 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
  • 因为POST需要两步,时间上消耗的要多一点,看起来GET比POST更有效。因此Yahoo团队有推荐用GET替换POST来优化网站性能。但这是一个坑!跳入需谨慎。为什么?
  1. GET与POST都有自己的语义,不能随便混用。

  2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。

  3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。

String对象中常用方法

Veröffentlicht am 2018-04-21

1、charAt() 和 charCodeAt()

charAt():返回指定索引位置处的字符。如果超出有效范围的索引值返回空字符串。

1
2
3
4
var str = "Hello world";

console.log(str.charAt(0)); //H
console.log(str.charAt(99)); //""

charCodeAt():与charAt类似,只不过返回表示给定索引的字符的Unicode的值。如果指定的 index 小于 0 或不小于字符串的长度,则 charCodeAt 返回 NaN。

1
2
3
4
5
6
7
"ABC".charCodeAt(0) // returns 65:"A"

"ABC".charCodeAt(1) // returns 66:"B"

"ABC".charCodeAt(2) // returns 67:"C"

"ABC".charCodeAt(3) // returns NaN

2、indexOf() 和 lastIndexOf()

indexOf():从字符串对象中返回首个被发现的给定值的索引值,如果没有找到则返回-1。

lastIndexOf():从字符串对象中返回最后一个被发现的给定值的索引值,如果没有找到则返回-1。

接收两个参数:第一个是需要搜索的子字符串,第二个是从字符串中的哪个位置开始搜索(可选)。

1
2
3
4
5
6
7
var str = "hello world";

console.log(str.indexOf("l")); //2
console.log(str.lastIndexOf("l")); //9

console.log(str.indexOf("l",4)); //9
console.log(str.lastIndexOf("l",4)); //3

3、concat()

连接两个字符串文本,并返回一个新的字符串。

1
2
var hello = "Hello,";
console.log(hello.concat("good luck for you.")); /* Hello,good luck for you. */

在实际应用中,最常用到的还是加号操作符“+”,或者es6模板字符串``

1
2
3
4
var a = "Hello,";
var b = "good luck for you.";
console.log(a+b); /* Hello,good luck for you. */
console.log(`${a}${b}`); /* Hello,good luck for you. */

4、slice()、substr()、substring()

stringObject.slice(start,end)

摘取一个字符串区域,返回一个新的字符串。接收两个参数:第一个参数指定子字符串的开始位置,第二个参数指定的是子字符串最后一个字符后面的位置(如果没有指定第二个参数,则一直到字符串结尾)。该方法对原字符串没有影响。

1
2
3
4
5
6
var str = "hello world";
console.log(str.slice(2)); //llo world
console.log(str.slice(2,8)); //llo wo

console.log(str.slice(-9)); //llo world
console.log(str.slice(2,-3)); //llo wo

当传入的参数为负数时,会将该负数加上字符串的长度转换成正数,再进行截取。如果传入的负数的绝对值大于字符串的长度,则用0替代。

stringObject.substr(start,length)

在字符串中抽取从 start 下标开始的指定数目的字符。

  • length如果省略了,那么返回从 stringObject 的开始位置到结尾的字串。
1
2
3
4
var str="Hello world!"

console.log(str.substr(3)) //lo world!
console.log(str.substr(3,4)) //lo w

stringObject.substring(start,stop)

返回在字符串中指定两个下标之间的字符。只能接收非负的整数参数

  • substring() 方法返回的子串包括 start 处的字符,但不包括 stop 处的字符。
  • 如果参数 start 与 stop 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)。如果 start 比 stop 大,那么该方法在提取子串之前会先交换这两个参数。
1
2
3
4
var str="Hello world!"

console.log(str.substring(3)) //lo world!
console.log(str.substring(3,4)) //l

注意:
与 slice() 和 substr() 方法不同的是,substring() 不接受负的参数。

5、trim()

ES5新增方法,用来删除字符串前置及后缀的所有空格。

1
2
3
4
var str = "   hello world   ";
var strCopy = str.trim();
console.log(str); //" hello world "
console.log(strCopy); //"hello world"

6、toLowerCase() 和 toUpperCase()

  • toLowerCase() :将整个字符串转成小写字母。
  • toUpperCase():将整个字符串转成大写字母。
  • 还有两个方法:toLocaleLowerCase()和 toLocaleUpperCase()方法则是针对特定地区的实现。
  • 少数语言(如土耳其语)会为 Unicode 大小写转换应用特殊的规则,这时候就必须使用针对地区的方法来保证实现正确的转换。
1
2
3
4
5
6
7
var str = "HellO WoRlD"; 
console.log(str.toLowerCase()); //hello world
console.log(str); //HellO WoRlD(原字符串不变)
console.log(str.toLocaleLowerCase()); //hello world

console.log(str.toUpperCase()); //HELLO WORLD
console.log(str.toLocaleUpperCase()); // HELLO WORLD

7、match()

match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。

1
2
3
4
5
6
7
8
9
10
11
var myString = "welcome to my blog blog blog...";
var myPattern = /.og/;
var myResult = myString.match(myPattern);
console.log(myResult);// ["log", index: 15, input: "welcome to my blog blog blog..."]
console.log(myResult[0]); // "log"
console.log(myResult.index);// 15
console.log(myResult.input);// "welcome to my blog blog blog..."

var myPattern2 = /.oog/;
var myResult2 = myString.match(myPattern2);
console.log(myResult2); //null

8、search()

返回与正则表达式查找内容匹配的第一个字符串的位置。

与match()方法不同的是:search()方法只返回一个数值而不是一个数组。

1
2
3
4
var myString = "welcome to my blog blog blog...";
var myPattern = /.og/;
var myResult = myString.search(myPattern);
console.log(myResult);// 15

9、replace()

用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

1
2
var str="Mr Blue has a blue house and a blue car";
var n=str.replace(/blue/g,"red"); //Mr Blue has a red house and a red car

忽略大小写

1
2
var str="Mr Blue has a blue house and a blue car";
var n=str.replace(/blue/gi, "red");

10、split()

把一个字符串分割成字符串数组。

1
2
3
4
5
var str = "cat, bat, sat, fat";
var strArr = str.split(",");
console.log(strArr); // ["cat", " bat", " sat", " fat"]
var strArr2 = str.split(",",2);
console.log(strArr2); // ["cat", " bat"]

11、localeCompare()

  • 用本地特定的顺序来比较两个字符串
  • 比较两个字符串,并根据比较结果返回一个值。该方法接收一个参数,即要与之比较的字符串参数。
  • 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是-1)
  • 如果字符串等于字符串参数,则返回 0;
  • 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是 1)
1
2
3
4
var str = "sean";
console.log(str.localeCompare("lily")); // 1
console.log(str.localeCompare("sean")); // 0
console.log(str.localeCompare("Tom")); // -1

12、fromCharCode()

接收一或多个字符编码,然后将它们转换成一个字符串。该方法是String 构造函数本身的静态方法。该方法与前面介绍的charCodeAt()方法作用正好相反。

1
console.log(String.fromCharCode(104, 101, 108, 108, 111)); //"hello"

chorme的Network面板简介

Veröffentlicht am 2018-03-19

1.概述

Network面板可以记录页面上的网络请求的详情信息,从发起网页页面请求Request后分析HTTP请求后得到的各个请求资源信息(包括状态、资源类型、大小、所用时间、Request和Response等),可以根据这个进行网络性能优化。

2.Network 面板由五个窗格组成

1、Controls。使用这些选项可以控制 Network 面板的外观和功能。

2、Filters。 使用这些选项可以控制在 Requests Table 中显示哪些资源。提示:按住 Cmd (Mac) 或 Ctrl (Windows/Linux) 并点击过滤器可以同时选择多个过滤器。

3、Overview。 此图表显示了资源检索时间的时间线。如果您看到多条竖线堆叠在一起,则说明这些资源被同时检索。

4、Requests Table。 此表格列出了检索的每一个资源。 默认情况下,此表格按时间顺序排序,最早的资源在顶部。点击资源的名称可以显示更多信息。 提示:右键点击 Timeline 以外的任何一个表格标题可以添加或移除信息列。

5、Summary。 此窗格可以一目了然地展示请求总数、传输的数据量和加载时间。

示意图:

image

默认情况下,Requests Table 会显示以下列。可以添加和移除列。

  • Name。资源的名称。
  • Status。HTTP 状态码。
  • Type。已请求资源的 MIME 类型。
  • Initiator。发起请求的对象或进程。值为以下选项之一:
    • Parser。Chrome 的 HTML 解析器发起请求。
    • Redirect。HTTP 重定向发起请求。
    • Script。脚本发起请求。
    • Other。某些其他进程或操作发起请求,例如用户通过链接或者在地址栏中输入网址导航到页面。
  • Size。响应标头(通常为数百字节)加响应正文(由服务器提供)的组合大小。
  • Time。从请求开始至在响应中接收到最终字节的总持续时间。
  • Timeline。Timeline 列可以显示所有网络请求的可视瀑布。 点击此列的标题可以显示一个包含更多排序字段的菜单。

HTTP 状态码

分类 分类描述
1** 信息,服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

多用途Internet邮件扩展(MIME)类型 是一种标准化的方式来表示文档的性质和格式。通用结构:type/subtype,例如:text/html、image/jpeg、audio/ogg、video/mp4

3.记录网络活动

在 Network 面板打开时,DevTools 在默认情况下会记录所有网络活动。 要记录活动,只需在面板打开时重新加载页面,或者等待当前加载页面上的网络活动。

image
您可以通过 record 按钮指示 DevTools 是否记录。 显示红色 (记录按钮打开) 表明 DevTools 正在记录。 显示灰色 (记录按钮关闭) 表明 DevTools 未在记录。 点击此按钮可以开始或停止记录,也可以按键盘快捷键 Cmd/Ctrl+e。

4.在记录期间捕捉屏幕截图

Network 面板可以在页面加载期间捕捉屏幕截图。此功能称为幻灯片。

点击摄影机图标可以启用幻灯片。图标为灰色时,幻灯片处于停用状态 (已停用幻灯片)。如果图标为蓝色,则说明已启用 (已启用幻灯片)。

重新加载页面可以捕捉屏幕截图。屏幕截图显示在概览上方。双击屏幕截图可查看放大版本。
image

5.查看 DOMContentLoaded 和 load 事件信息

DOMContentLoaded事件会在页面上DOM完全加载并解析完毕之后触发,不会等待CSS、图片、子框架加载完成。 load事件会在页面上所有DOM、CSS、JS、图片完全加载完毕之后触发。

DOMContentLoaded事件在Overview上用一条蓝色竖线标记,并且在Summary以蓝色文字显示确切的时间。

load事件同样会在Overview和Requests Table上用一条红色竖线标记,在Summary也会以红色文字显示确切的时间。

image

6.查看单个资源的详细信息

点击资源名称(位于 Requests Table 的 Name 列下)可以查看与该资源有关的更多信息。下面四个标签最常见:

  • Headers。与资源关联的 HTTP 标头。
  • Preview。JSON、图像和文本资源的预览。
  • Response。HTTP 响应数据(如果存在)。
  • Cookies 显示资源HTTP的Request和Response过程中的Cookies信息。
  • Timing。资源请求生命周期的精细分解。

①查看 HTTP 标头

Headers 标签可以显示资源的请求网址、HTTP 方法以及响应状态代码。 此外,该标签还会列出 HTTP 响应和请求标头、它们的值以及任何查询字符串参数。

点击每一部分旁边的 view source 或 view parsed 链接,您能够以源格式或者解析格式查看响应标头、请求标头或者查询字符串参数。

image

②预览资源

点击 Preview 标签可以查看该资源的预览。Preview 标签可能显示一些有用的信息,也可能不显示,具体取决于所选择资源的类型。

③查看资源HTTP的Response信息

在Response标签里面可根据选择的资源类型(JSON、图片、文本、JS、CSS)显示相应资源的Response响应内容。

④查看 Cookie

如果选择的资源在Request和Response过程中存在Cookies信息,则Cookies标签会自动显示出来,在里面可以查看所有的Cookies信息。

下面是 Cookie 表中每一列的说明:

  • Name。Cookie 的名称。
  • Value。Cookie 的值。
  • Domain。Cookie 所属的域。
  • Path。Cookie 来源的网址路径。
  • Expires / Max-Age。Cookie 的 expires 或 max-age 属性的值。
  • Size。Cookie 的大小(以字节为单位)。
  • HTTP。指示 Cookie 应仅由浏览器在 HTTP 请求中设置,而无法通过 - JavaScript 访问。
  • Secure。如果存在此属性,则指示 Cookie 应仅通过安全连接传输。

⑤查看网络耗时

点击 Timing 标签可以查看单个资源请求生命周期的精细分解。
生命周期按照以下类别显示花费的时间:

  • Queuing
  • Stalled
  • 如果适用:DNS lookup、initial connection、SSL handshake
  • Request sent
  • Waiting (TTFB)
  • Content Download

官网参考
中文参考文档

react的生命周期

Veröffentlicht am 2018-01-21

在ES6中,一个React组件是用一个class来表示的

1
2
3
4
// 定义一个TodoList的React组件,通过继承React.Component来实现
class TodoList extends React.Component {
...
}

组件生命周期

  • 一个React组件被渲染的过程有三个阶段。这个过程就叫做组件的生命周期。每个React组件都会经历这个过程。为了使这个过程可操控,React提供了一些方法,在组件生命周期过程中,通过这些方法我们可以得到某个阶段发生的通知。这些方法就叫作组件的生命周期方法,它们按特定顺序被调用。

  • 方法中带有前缀 will 的在特定环节之前被调用,而带有前缀 did 的方法则会在特定环节之后被调用。

组件的生命周期分成三个状态:

  • Mounting:已插入真实 DOM (实例化)
  • Updating:正在被重新渲染 (存在期)
  • Unmounting:已移出真实 DOM (销毁时)
  1. Mounting

    当组件在客户端被实例化,第一次被创建时,以下方法依次被调用:
  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()
  1. Updating

    属性或状态的改变会触发一次更新。当一个组件在被重渲时,这些方法将会被调用:

componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()

  1. Unmounting

每当React使用完一个组件,这个组件必须从 DOM 中卸载后被销毁,此时 componentWillUnmout 会被执行,完成所有的清理和销毁工作,在 componentDidMount 中添加的任务都需要在该方法中撤销,如创建的定时器或事件监听器。

  • componentWillUnmount()

参考

render()

最基本的,React组件需要一个render()方法,因此它至少会返回null或者false。render()负责告诉React怎样渲染这个React组件。它可以返回null,表示不渲染任何内容。它也可以返回一个组件。

render方法需要满足下面几点:

  • 只能通过 this.props 和 this.state 访问数据(不能修改)

    • this.props存储的是从父级传过来的只读数据。它属于父级,并且不能被它的子元素改变。这个数据应当认为是不可改变的。
    • this.state存储的数据是组件私有的。它能被组件修改。当state更新后组件会自动重新渲染(一般情况下)。
  • 可以返回 null,false 或者任何React组件

  • 只能出现一个顶级组件,不能返回一组元素(React15.4.0)

  • 不能改变组件的状态(props,state),render方法必须是一个纯函数。也就是每次调用,这个方法都要返回同样的结果。

  • 不能修改DOM的输出

render方法返回的结果并不是真正的DOM元素,而是一个虚拟的表现,类似于一个DOM tree的结构的对象。react之所以效率高,就是这个原因。

constructor()

  • React组件的构造函数将会在挂载之前被调用。当为一个React.Component子类定义构造函数时,你应该在任何其他的表达式之前调用super(props)。否则,this.props在构造函数中将是未定义,并可能引发异常。

  • 构造函数是初始化状态的合适位置。若你不初始化状态且不绑定方法,那你也不需要为你的React组件定义一个构造函数。

1
2
3
4
5
6
7
constructor(props) {
super(props);
this.state = {
color: props.color,
text: '初始化'
};
}

componentWillMount()

  • componentWillMount()在挂载发生前被立刻调用。其在render()之前被调用,因此在这方法里同步地设置状态将不会触发重渲。

  • 在 render 方法调用之前修改 state 的最后一次机会

  • 在React的生命周期中,这个函数只会被执行一次

componentDidMount()

  • 它在React将组件插入到DOM之后立即被调用。更新后的DOM现在可以被访问,这意味着这个方法是初始化其他需要访问这些DOM的JavaScript库的最佳地方。React的子组件也有componentDidMount函数,并且会在父组件的componentDidMount函数之前被调用。

  • 该方法被调用时,已经渲染出真实的 DOM。由于组件并不是真实的 DOM 节点,而是存在于内存之中的一种数据结构,叫做虚拟 DOM。只有当它插入文档以后,才会变成真实的 DOM 。有时需要从组件获取真实 DOM 的节点,这时就要用到 ref 属性

  • 这是一个适合实现网络请求的地方。在该方法里设置状态将会触发重渲。

componentWillReceiveProps()

当 props 发生改变,componentWilReceiveProps 会被调用。可以在这个方法里更新 state,以触发 render 方法重新渲染组件。

1
2
3
4
5
6
7
componentWillReceiveProps ( nextProps ) {
if ( nextProps.bool ) {
this.setState({
bool: true
});
}
}

shouldComponentUpdate()

  • 组件挂载之后,每次调用setState后都会调用shouldComponentUpdate判断是否需要重新渲染组件。默认返回true,需要重新render。

  • 返回false表示跳过后续的生命周期方法,通常不需要使用以避免出现bug。

  • 在首次渲染期间或者调用了forceUpdate方法后,该方法不会被调用。

1
2
3
shouldComponentUpdate(nextProps, nextState) {
return true;
}
  • 在比较复杂的应用里,有一些数据的改变并不影响界面展示,可以在这里做判断,优化渲染效率。它是在重新渲染过程开始前触发的。
1
2
3
4
5
6
7
8
9
10
//例如想让组件只在props.color或者state.count的值变化时重新渲染
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}

componentWillUpdate()

1
componentWillUpdate(nextProps, nextState)
  • componentWillUpdate()方法在React即将更新DOM之前被调用。该方法并不会在初始化渲染时调用。它得到以下两个参数:
    • nextProps:下一个属性对象
    • nextState:下一个状态对象

注意:

你不能在这调用this.setState(),若你需要更新状态响应属性的调整,使用componentWillReceiveProps()代替。

componentDidUpdate()

  • componentDidUpdate()方法在React更新DOM之后会被立即调用。该方法并不会在初始化渲染时调用。它得到如下两个参数:
    • prevProps:上一个属性对象
    • prevState:上一个状态对象
  • 当组件被更新时,使用该方法是操作DOM的一次机会。这也是一个适合发送请求的地方,要是你对比了当前属性和之前属性(例如,如果属性没有改变那么请求也就没必要了)。

componentWillMount、componentDidMount和componentWillUpdate、componentDidUpdate可以对应起来。区别在于,前者只有在挂载的时候会被调用;而后者在以后的每次更新渲染之后都会被调用。

componentWillUnmount()

componentWillUnmount()在组件被卸载和销毁之前立刻调用。可以在该方法里处理任何必要的清理工作,例如解绑定时器,取消网络请求,清理任何在componentDidMount环节创建的DOM元素。

其他API

forceUpdate()

  • 默认情况,当你的组件或状态发生改变,你的组件将会重渲。若你的render()方法依赖其他数据,你可以通过调用forceUpdate()来告诉React组件需要重渲。

  • 调用forceUpdate()将会导致组件的 render()方法被调用,并忽略shouldComponentUpdate()。这将会触发每一个子组件的生命周期方法,涵盖,每个子组件的shouldComponentUpdate() 方法。若当标签改变,React仅会更新DOM。

  • 通常你应该尝试避免所有forceUpdate() 的用法并仅在render()函数里从this.props和this.state读取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, {Component} from "react";

class Demo1 extends Component{
constructor(props){
super(props);
this.state ={
text:'hello',
show: true
}
}

componentDidMount() {
this.setState({
text: 'hello,world',
show: false
})
this.forceUpdate();
}

shouldComponentUpdate() {
return false;
}

render() {
return (
<div>
{this.state.text}
</div>
);
}
}

export default Demo1;

更新render的方法

在react中,触发render的有4条路径。

以下假设shouldComponentUpdate都是按照默认返回true的方式。

  • 首次渲染Initial Render
  • 调用this.setState (并不是一次setState会触发一次render,React可能会合并操作,再一次性进行render)
  • 父组件发生更新(一般就是props发生改变,但是就算props没有改变或者父子组件之间没有数据交换也会触发render)
  • 调用this.forceUpdate

image

12

John Doe

13 Artikel
© 2019 John Doe
Erstellt mit Hexo
|
Theme — NexT.Muse v5.1.4