BFC布局

解决问题

  1. 浮动定位
  2. 消除外边距折叠
  3. 清楚浮动
  4. 自适应多拦布局

BFC创建

  1. 根元素或包含根元素的元素
  2. 浮动元素(float不为none)
  3. 绝对定位元素(position为absolute或者fixed)
  4. display为inline-block,table-cell,table-caption
  5. overflow不为visible
  6. 弹性布局(flex布局)
  7. 网格布局(grid布局)

BFC约束条件

  1. 内部BOX会在垂直方向一个接一个的放置
  2. 垂直方向上的距离由margin决定。属于同一BFC的两个相邻Box的margin会发生重叠
  3. 每个元素的左外边距与包含块的左外边界相接触(从左向右)
  4. BFC的区域不会与float元素区域重叠
  5. 计算BFC区域的高度,浮动子元素也参与计算
  6. BFC为独立容器,容器里面的子元素不会影响到外面元素

Examples

margin重叠

  1. 重叠
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<p>top</p>
<p>bottom</p>
</body>

<style>
p {
width: 100px;
height: 100px;
background: yellow;
line-height: 100px;
margin: 10px;
text-align: center;
}
</style>

20190813125350.png

  1. 不重叠
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<p>top</p>
<div>
<p>bottom</p>
</div>
</body>
<style>
p {
width: 100px;
height: 100px;
background: yellow;
line-height: 100px;
margin: 10px;
text-align: center;
}

div {
overflow: auto;
}

</style>

20190813125339.png

让浮动元素等高

  1. 不等高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="box">
<div class="float">浮动元素</div>
<p>未浮动元素</p>
</div>
<style>
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
}

.float {
float: left;
width: 200px;
height: 150px;
background-color: white;
border:1px solid black;
padding: 10px;
}
</style>

20190813125311.png

  1. 等高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class="box">
<div class="float">浮动元素</div>
<p>未浮动元素</p>
</div>
<style>
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
overflow: auto;
}

.float {
float: left;
width: 200px;
height: 150px;
background-color: white;
border:1px solid black;
padding: 10px;
}
</style>

20190813125635.png

前端统计如何实现数据传递

百度,google,友盟等的统计都用的是 <img src="..."> 的方式来统计信息,

前端用GIF打点

why,从原理说起

前端监控原理

由Web页面将用户信息(UA/鼠标点击位置/页面报错/停留时长/etc)等 上报给服务器

上报数据url_encode(百度统计/CNZZ)或JSON编码(神策/诸葛io)为字符串,通过url参数传递给后台服务器

关键在于两点

  1. 收集用户信息
  2. 上报数据,只要能上报,其实选择有多种,如下图

20190810002049.png

因此需要一种方式,尽可能的满足一下条件

  1. 不关注返回结构
  2. 传递信息尽可能小
  3. 不能阻塞web的正常业务,且影响最小

用排除法来看(大家都用git是有原因的)

不用GET/POST/HEAD请求上报?

请求都会涉及跨域。而跨域请求很容易出现由于配置不当,被浏览器拦截并报错。所以排除,且对于后端而言开放所有的跨域拦截是有安全风险的,并且由于要做跨域校验,是有性能消耗的。

为什么不用其他文件(js/css/ttf)

js等的加载只能插入到html文档内部,才能被加载,这个会阻塞浏览器的页面渲染,且改变dom结构,即使之删除了,操作dom也是相当消耗浏览器性能的,影响用户体验。所以排除。

所以选择图片

图片不仅不用插入DOM元素内,且在JS中直接new一个Image就能发起请求。而且还没有阻塞问题。就是没有js的环境下也能通过直接加载img标签来正常打点。这是其他资源所做不到的。

为什么选择GIF

原因: GIF是传输最小的合法图片格式(详情参考:图片类型解析)。

总结:用GIF的原因

  1. 没有跨域问题
  2. 不会阻塞页面加载,影响用户体验
  3. 在所有的图片中提及最小,比较BMP/PNG,可以节省41%/35%的网络资源

以为一切完了吗?没有好戏才真正开始,上代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <img src="http://0.0.0.0:8000/logo.png?from=0" id="image0" />
    <img src="http://0.0.0.0:8000/logo.png?from=1" id="image1" />
  </body>
  <script>
    var unique = (function() {
      var time = new Date().getTime() + '-',
        i = 0;
      return function() {
        return time + i++;
      };
    })();

    window.addEventListener('beforeunload', function(e) {
      var data = window['imgLogData'] || (window['imgLogData'] = {});

      var uid = unique();
      image0.src = 'http://0.0.0.0:8000/logo.png' + '?from=2_uid=' + uid;
      data[uid] = image0;
      image0.onload = image0.onerror = function() {
        image0.onload = image0.onerror = null;
        image0 = null;
        delete data[uid];
      };

      var uid = unique();
      image1.src = 'http://0.0.0.0:8000/logo.png' + '?from=3_uid=' + uid;
      data[uid] = image1;
      image1.onload = image1.onerror = function() {
        image1.onload = image1.onerror = null;
        image1 = null;
        delete data[uid];
      };

      xhr2 = new XMLHttpRequest();
      xhr2.open(
        'GET',
        'http://0.0.0.0:8000/logo.png' + '?from=4&_uid=' + unique(),
        true
      );
      xhr2.send(null);
      xhr3 = new XMLHttpRequest();
      xhr3.open(
        'GET',
        'http://0.0.0.0:8000/logo.png' + '?from=5&_uid=' + unique(),
        true
      );
      xhr3.send(null);
      xhr4 = new XMLHttpRequest();
      xhr4.open(
        'GET',
        'http://0.0.0.0:8000/logo.png' + '?from=6&_uid=' + unique(),
        true
      );
      xhr4.send(null);
      return ((e | window.event).returnValue = '123');
    });
    window.addEventListener('unload', function(e) {
      console.dir(123);
    });
  </script>
</html>v

这个代码,就是检测统计时在刷新时的图片加载顺序情况。如图(注:logo是随便的一个普通图片,可以替换成其他的,加uid是为了每次有用新的图片)

chrome 测一下

20190810004819.png

form: 4->5->6->2->3->0->1
发现什么了吗?xhr竟然比js内src要先发出(实效性很小,目前没搞清楚为什么,可能和浏览器事件执行有关系,xhr的队列比js内src请求队列,要有先执行,dom内src请求队列最后执行),而dom内src最后才发出。如果不考虑安全,跨域因素,真的用ajax要更好,而且ajax后端能返回204状态码,没有返回内容,这更好。

safari 测一下

20190810013434.png

form: 2->3->4->5->6->0->1
调整xhr的调用顺序

20190810014238.png
form: 4->2->3->5->6->0->1
从图中看出safari对xhr和js内部src没有做区分,但是报错了,浏览器任务不安全,应为request的请求头中没有指名服务器信息,和请求格式等。由安全性问题,所以选择js内src最好,

beforeupload和upload

beforeunload 和 unload 的触发时间

// beforeunload在新资源开始跳转或老资源刷新之前之前,可以取消加载
window.addEventListener('beforeunload', function (e) {
  //返回值之后,会弹出是否关闭当前页面的提示框
  return (e|window).returnValue = '123'
})

//unload是在老资源卸载后,(?新资源请求发出后触发)不能取消加载
window.addEventListener('unload', function(e) {
});

猜测点: 浏览器在资源卸载后最后一步,有可能存在调优,如取消事件系统,比如micotask和macotask等.unload的可能性要比beforeunlaod高。

20190809234916.png

测试代码

  <!DOCTYPE html>
  <html>
      <head>
          <meta charset="utf-8"/>
          <title> beforeunload vs unload </title>
      </head>
      <body>
          请手工关闭当前浏览器窗口。<br/>
          请手工在地址栏输入其他页面地址或从收藏夹、历史记录中将页面导航其他站点。<br/>
          请手工单击后退,前进,刷新,或主页按钮。<br/>
          <a href="http://www.baidu.com" id="A">点击一个链接到新页面</a><br />
          <button onclick="document.getElementById('A').click()">调用 anchor.click 方法</button><br />
          <button onclick="document.write('A')">调用 document.write 方法</button><br />
          <button onclick="document.open('http://baidu.com')">调用 document.open 方法</button><br />
          <button onclick="document.close()">调用 document.close 方法</button><br />
          <button onclick="window.open('http://www.baidu.com','_self')">调用 window.open方法,窗口名称设置值为 _self</button><br />
          <button onclick="try{window.navigate('http://www.baidu.com')}catch(e){alert('不支持此方法')}">调用 window.navigate 方法</button><br />
          <button onclick="try{window.external.NavigateAndFind('http://www.baidu.com','','')}catch(e){alert('不支持此方法')}">调用 NavigateAndFind 方法</button><br />
          <button onclick="location.replace('http://www.baidu.com')">调用 location.replace 方法</button><br />
          <button onclick="location.reload()">调用 location.reload 方法</button><br />
          <button onclick="location.href='http://www.baidu.com'">指定一个 location.href 属性的新值</button><br />
          <form action="http://www.baidu.com" id="B">
          <input type="submit" value="提交具有 action 属性的一个表单">
          </form>
          <button onclick="document.getElementById('B').submit()">调用 form.submit 方法</button><br />
          <a href="javascript:void(0)">调用 javascipt: 伪协议</a><br />
          <a href="mailto:">调用 mailto: 伪协议</a><br />
          <a href="custom:">调用自定义伪协议</a>

          <script>
          function wait(ms) {
              var now = new Date().getTime();
              while(new Date() - ms <= now) {}
          }
          window.onunload = function () {
              //wait(2000);
              var img = new Image();
              img.src = 'http://10.209.12.214:1234/log?rand=' + (new Date()).getTime() + '&a=' + navigator.userAgent;
          };
          // window.onbeforeunload = function () {
          //     //wait(2000);
          //     var img = new Image();
          //     img.src = 'http://10.209.12.214:1234/log?rand=' + (new Date()).getTime() + '&a=' + navigator.userAgent;
          // };
          </script>
      </body>
  </html>

通过向server发送请求查看server日志来观察是否正常触发

var PORT = 1234;

var http = require('http');
var url = require('url');
var path = require('path');

var server = http.createServer(function (request, response) {
    var pathname = url.parse(request.url).pathname;
    var params = url.parse(request.url, true).query;
    switch (pathname) {
        case '/':
            var pdpss = params.adunitid.split(',');
            
            if ('string' === typeof pdpss) {
                pdpss = [pdpss];
            }
            var adContent = {};
            pdpss.forEach(function (pdps) {
                adContent[pdps] = {
                    id: pdps,
                    type: 'an',
                    content: 'i am ' + pdps + ' content'
                };
            });
            var contentType = "application/json";
            response.writeHead(200, {
                'Content-Type': contentType
            });
            response.write(params.callback + '(' + JSON.stringify(adContent) + ')');
            response.end();
            break;
        default:
            console.log(pathname, params);
            response.writeHead(404, {
                'Content-Type': 'text/plain'
            });

            response.write("This request URL " + pathname + " was not found on this server.");
            response.end();
            break;
    }
});
server.listen(PORT);
console.log("Server runing at port: " + PORT + ".");

测试结果

beforeunload

20190809235806.png

unload

20190809235836.png

结论

beforeunload支持力度更广一些,unload会释放一些资源。 unload在safari上缓存有影响

隐式类型转换

字符串连接符与算数运算符

/*
*
* 1. 字符串连接符(+):会把其他数据类型调用String() 方法转换成字符串然后拼接
* 2. 算数运算符:会把其他数据类型调用Number() 方法转成数字然后做加法计算
*/
// 字符串连接符号
console.log(1 + 'true') // 1true
console.log(1 + true) // 2
// Number(undefined) = NaN
console.log(1 + undefined) //NaN
//Number(null) = 0
console.log(1 + null) // 1

关系运算符

会把其他数据类型转换成number之后再比较关系

//关系运算符有一边是字符串时,会将其他数据类型使用Number转换,然后比较
console.log('2' > 10) // false
//两边都为字符串时,用Number转换,但是要用Uncode码来比较'2'.charCodeAt() = 50
console.log('2' > '10') // true
//多字符串时从左往右依次比较
console.log("abc">"b") // false
//特殊情况
console.log(undefined == undefined) // true
console.log(undefined == null) //true
console.log(null == null) // true
//NaN与任何数比较都是false
console.log(NaN == NaN) // false  

复杂类型在隐式转换时,先转换成String,然后在转换成Number运算

20190805223243.png

var a = ???
if (a == 1 && a == 2 && a == 3) {
  console.log(1)
}
// 如何完善a,才能打印1

/*
* 复杂数据类型转number顺序如下
* 1. 先使用valueOf()方法获取其原始值,如果原始值不是number类型,则使用toString()方法转String
* 2. 再将toString转成number运算
*/
// 先将左边数组转成string,然后右边也是string,则转成unicode编码运算
console.log([1,2] == '1,2') // true
console.log([1,2].valueOf()) //[1,2]
console.log([1,2].toString())// 1,2

var a = {};
console.log(a == "[object Object]") // true
console.log(a.valueOf().toStirng()) // [object Object]

// 重写对象的valueOf方法使其每次加一返回
var a = {
  i:1,
  valueOf: function(){
    return ++a.i
  }
}

if(a == 1 && a == 2 && a == 3) {
  console.log("1")
}

逻辑非隐式转换与关系运算符隐式转换搞混淆

/*
* 1. 关系运算符: 将其他数据类型转换成数字
* 2. 逻辑非: 将其他数据类型使用Boolean转换成Boolean
* 一下8种情况都会转换成false,其他都是true
* 0, -0, NaN, undefined, null, "", false, document.all()
*/
// [].valueOf.toString() 
// Number('') = 0
console.log([] == 0) // true
//![]看成布尔表达式 ![] == false;
console.log(![] == 0) // true
// {} 与 !{} 比较
// {}.valueOf().toString() 为 [object Object]
// !{} = false
// Number("[object Object]") == Number(false) 
// 最后NaN == 0
console.log({} == !{}) // false;
// 引用数据类型存储在栈中,比较的是堆栈地址
console.log({} == {}) //false

// [].valueOf().toString() 为 “”
// ![] 为 false
// Number("") == Number("false") 都为0
console.log([] == ![]) // true
// 引用数据类型存储在栈中,比较的是堆栈地址
console.log([] == []) // false

NaN和isNaN

今天判断一个值是不是NaN,遇到一个很诡异的问题

20190805172639.png

从图中可以看出isNaN在判断数字类型string和bool的时候都将其转换成了数字,所以严格类型判断需要先判断data是不是一个number类型(typeof data === 'number)然后在判断他是不是一个是不是一个数字

但是看起来很完美,这只是表现,我看了一下NaN的定义

NaN(NotaNumber,非数)是计算机科学中数值数据类型的一类值,表示未定义或不可表示的值。常在浮点数运算中使用。首次引入NaN的是1985年的IEEE 754浮点数标准。

首先NaN是一个数,且是一个不可能的数。

返回NaN的运算有如下三种:

1. 至少有一个参数是NaN的运算 
2. 不定式
    下列除法运算:0/0、∞/∞、∞/−∞、−∞/∞、−∞/−∞
    下列乘法运算:0×∞、0×−∞
    下列加法运算:∞ + (−∞)、(−∞) + ∞
    下列减法运算:∞ - ∞、(−∞) - (−∞)
3. 产生复数结果的实数运算。例如:
    对负数进行开偶次方的运算
    对负数进行对数运算
    对正弦或余弦到达域以外的数进行反正弦或反余弦运算 [1] 

在Chrome里有一种情况数与一个undefined或者一个非纯数字符串相互运算 也会产生NaN

20190805170014.png

原因isNaN判断的时候会将data进行Number类型转换

Number(undefined) = NaN
Number("12343") = 12343
Number("12343A") = NaN
Number("true") = 1
Number({}) = NaN
Number([]) = 0
Number([1]) = 1
Number([1,2,3]) = NaN

然后再去判断,总之很坑很坑

敏捷模型看板

20190802141953.png

  • BACKLOG BA拆解任务卡,可用于开发的Story
  • INDEV 开发开发的卡,且每个开发只能在一张卡上
  • INTEGRATING 应内部原因中断的卡,比如需要调试等
  • DEV DONE 开发完成自测后且Desc Check完成后,卡挪到DEV DONE
  • IN QA 测试正在测试的卡
  • QA DONE 测试完成之后的卡
  • BLOCK 应外部原因中断的卡
  • DONE 完成之后归档的卡

图片类型解析

1、GIF

  GIF图是比较古老的web图片格式之一,可以追溯到1987,几乎所有的浏览器都支持这一种格式,老有老的好处嘛。GIF是一种索引色模式图片,所以GIF每帧图所表现的颜色最多为256种。GIF能够支持动画,也能支持背景透明,这点连古老的IE6都支持,所以在以前想要在项目中使用背景透明图片,有一些方案就是生成GIF图片。GIF与JPEG、PNG相比,在通常情况下确实体积比较小。不过里面如果放入了足够多帧的图片,那么可能就不是那种情况了。现在网络上的GIF可以说是爆炸式的再增长,显然更多的在与他的两个特点:支持动画与兼容性好。缺点就是:色彩表现度不够丰富。

2、JPEG、JPG

  平常我们大部分见到的静态图基本都是这种图片格式。这种格式的图片能比较好的表现各种色彩,主要在压缩的时候会有所失真(主要是压缩时,会在细节上把相邻的一些色彩给同化掉),也正因为如此,造就了这种图片格式体积的轻量。格式被各中老弱病残的浏览器兼容,不过不支持背景透明与动画。平时web上的广告图、相片、特大背景图、轮播图等等一些大图场景中,都适用这个。

3、PNG

  PNG格式是有三种版本的,分别为PNG-8,PNG-24,PNG-32,所有这些版本都不支持动画的。PNG-8跟GIF类似的属性是相似的,都是索引色模式,而且都支持背景透明。相对比GIF格式好的特点在与背景透明时,图像边缘没有什么噪点,颜色表现更优秀。PNG-24其实就是无损压缩的JPEG。而PNG-32就是在PNG-24的基础上,增加了透明度的支持。PNG格式在老浏览器IE6以及以下,PNG-8透明度的支持度不是很好,PNG-32的透明度基本不支持。正因为如此,以前有一个js插件,专门应对IE6这种BUG,主要是用IE6里的滤镜来重新渲染图片达到透明.随着时代的发展,PNG也想进步,也想支持动画。所以,有人推出了APNG(Animated PNG)格式图片。从字面上理解,就是会动的PNG图片,不过这个技术实现上与PNG开发小组理念不合,没有得到有效推广。到现在,也就有Blink内核的浏览器(代表浏览器:火狐)有比较好的支持,其它的就无从谈起了。

4、webP

  这个格式的图片的格式是财大气粗的Google在2010发布出来的,它拥有现有位图格式的所有优点,包括体积小、色彩表现足够、支持动画(一开始是不支持的)。当然,新东西的缺点就是兼容性不是很好,还有就是呈现这种图片格式计算量比平常的图片要大很多。由于出生好,东西本身也不错,越来越多的开发者与设计者开始关注它。国内某家公司也在使用这种格式图片制作表情。

5、SVG

  SVG是一种矢量图,在现在来说,得到的支持是很可观的。矢量图比位图一个天生的有点,就是它不管放多大都不会模糊。这种格式的图片,对一些简单的线条、 形状表现是很不错的,如果表达更复杂的图像(如照片),那这个就会变的太复杂。SVG能够支持动画(SVG的动画特性不能被IE浏览器很好的支持),以前的flash那样,还支持css样式的一些修改。我们现在网页上的很多icon图标都是使用这个的,svg也能够把多个SVG组合起来。总体来说,SVG还是一个比较看好的技术。

  浏览器中,对于图片的技术更新一直突破,其较于文字来说唯一缺点就是体积太大,但图片的表现力是文字无法比拟的,也相信图片会越来越好吧。关于web上位图的技术还有一个是base64,这个是可以把的图片转化成为16位的代码直接插入web中。

20190805120023.png

给我一个最小图

,1x1像素是最小的合法图片。而且,因为是通过图片打点,所以图片最好是透明的,这样一来不会影响页面本身展示效果,二者表示图片透明只要使用一个二进制位标记图片是透明色即可,不用存储色彩空间数据,可以节约体积。因为需要透明色,所以可以直接排除JEPG(BMP32格式可以支持透明色)。

1*1 透明图像比较

BMP:

20190810010912.png

PNG:

20190810011026.png

GIF:

20190810011056.png

可以看到,最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF,只需要43个字节。同样的响应,GIF可以比BMP节约41%的流量,比PNG节约35%的流量

网络性能优化

设计网络方向的主要有三个:

  1. DNS 解析
  2. TCP 连接
  3. HTTP 请求/响应

HTTP 请求/响应

  1. 减少网络请求
  2. 减少单次网络请求所花费的时间

浏览器缓存

MemoryCache

图片全部会放到内存中缓存, 大文件的js或css会放到硬盘内,小的也放到内存中

ServiceWorkCache

独立于浏览器主线程,不能操作DOM,可以实现离线缓存、消息推送、网络代理

HTTPCache

20190802094419.png

  1. 强缓存

利用http请求头中的expiresCache-Control两个字段来控制。同时出现时Cache-Control优先级更高

Cache-Control关键字理解

no-cache: 直接绕过浏览器缓存,直接请求服务器,响应字段如果有浏览器不缓存
no-store: 浏览器和服务器都不缓存
no-transform: 不要随意转换我发过来的东西
only-if-cached: 缓存中有就返回,没有就不返回
max-age: 用于请求头,超过年龄限制就返回新的
max-stale: 用于请求头,允许返回已经过期的资源,但有最大时间限制
min-fresh: 用于请求头,对即将过期的资源不返回
public: 用于响应头,允许客户端缓存数据,也能给别人使用,比如代理服务器可以缓存
private: 用于响应头,允许客户端缓存数据,不能给别人使用

  1. 协商缓存

依赖Last-Modified,If-Modified-SinceETag,If-None-Match来实现

Last-Modified: response返回表示资源的最后修改时间
If-Modified-Since: 资源在请求期间是否有修改,如果没有修改,则命中协商缓存,从缓存中拿数据,如果有修改,则返回新的Last-Modified,和服务器资源

问题:

  • 周期性的重写资源,但是资源内容没有改动
  • 修改的内容不重要
  • Last-Modified时间太短无法精确

ETag: 资源的唯一标示,随服务器返回
If-None-Match: 服务器比较If-No-Match和当前资源的ETag比较,如果一样则返回缓存中的,如果不一样,就返回新的ETag和服务器资源

  1. HTTP 缓存决策

20190802111709.png

  • 资源内容是否复用,拒绝一切形式缓存
  • 是否需要向服务器发起请求
  • 是否可以被代理服务器缓存
  • 是否有缓存时间
  1. PushCache

指服务器HTTP2在server push阶段的缓存

  • Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情况下才会去询问 Push Cache。
  • 只存在session使用期间
  • 共享同一个HTTP2连接,可以共享同一个PushCache